From 53ab9ad7d78dfbfa612dc09a160657d5bfee6147 Mon Sep 17 00:00:00 2001 From: filipriec Date: Sat, 8 Mar 2025 10:20:08 +0100 Subject: [PATCH] steel validation in progress --- Cargo.lock | 1 + server/Cargo.toml | 1 + server/src/steel/handlers.rs | 5 --- server/src/steel/handlers/engine.rs | 34 ------------------- server/src/steel/handlers/evaluator.rs | 24 ------------- server/src/steel/validation.rs | 2 ++ server/src/steel/validation/script.rs | 21 ++++++++---- .../handlers/post_table_script.rs | 23 ++++++++++++- 8 files changed, 40 insertions(+), 71 deletions(-) delete mode 100644 server/src/steel/handlers/engine.rs delete mode 100644 server/src/steel/handlers/evaluator.rs create mode 100644 server/src/steel/validation.rs diff --git a/Cargo.lock b/Cargo.lock index ff8b320..39a2747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2363,6 +2363,7 @@ dependencies = [ "serde_json", "sqlx", "steel-core", + "steel-derive", "time", "tokio", "tonic", diff --git a/server/Cargo.toml b/server/Cargo.toml index 2eda441..edcac6a 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -19,6 +19,7 @@ tonic-reflection = "0.12.3" tracing = "0.1.41" time = { version = "0.3.39", features = ["local-offset"] } steel-core = { git = "https://github.com/mattwparas/steel.git", branch = "master", package = "steel-core" } +steel-derive = { git = "https://github.com/mattwparas/steel.git", branch = "master", package = "steel-derive" } [lib] name = "server" diff --git a/server/src/steel/handlers.rs b/server/src/steel/handlers.rs index 81a847a..7a1c667 100644 --- a/server/src/steel/handlers.rs +++ b/server/src/steel/handlers.rs @@ -1,6 +1 @@ // src/steel/handlers.rs -pub mod evaluator; -pub mod engine; - -pub use engine::validate_steel_script; -pub use evaluator::validate_target_column; diff --git a/server/src/steel/handlers/engine.rs b/server/src/steel/handlers/engine.rs deleted file mode 100644 index 3bf3ec4..0000000 --- a/server/src/steel/handlers/engine.rs +++ /dev/null @@ -1,34 +0,0 @@ -// src/steel/handlers/engine.rs -use steel_core::{steel_vm::engine::Engine, rvals::SteelVal}; -use tonic::Status; - -pub fn validate_steel_script(script: &str) -> Result<(), Status> { - let mut engine = Engine::new(); - - // Basic syntax check - let parsed = engine.compile(script).map_err(|e| { - Status::invalid_argument(format!("Syntax error: {}", e)) - })?; - - // Validate required function signature - let has_transform = parsed.iter().any(|expr| match expr { - SteelVal::Func(f) => f.name() == Some("transform"), - _ => false, - }); - - if !has_transform { - return Err(Status::invalid_argument( - "Script must contain a 'transform' function" - )); - } - - // Simple sandboxed execution test - let test_input = SteelVal::StringV("test_data".into()); - - engine.call_function("transform", vec![test_input]) - .map_err(|e| { - Status::invalid_argument(format!("Runtime validation failed: {}", e)) - })?; - - Ok(()) -} diff --git a/server/src/steel/handlers/evaluator.rs b/server/src/steel/handlers/evaluator.rs deleted file mode 100644 index c0b74dd..0000000 --- a/server/src/steel/handlers/evaluator.rs +++ /dev/null @@ -1,24 +0,0 @@ -// src/steel/handlers/evaluator.rs -use crate::validation::script::validate_script; -use serde_json::Value; - -const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "firma", "created_at"]; - -pub fn validate_target_column( - table_name: &str, - target: &str, - table_columns: &Value, -) -> Result<(), String> { - if SYSTEM_COLUMNS.contains(&target) { - return Err(format!("Cannot override system column: {}", target)); - } - - let columns: Vec = serde_json::from_value(table_columns.clone()) - .map_err(|e| format!("Invalid column data: {}", e))?; - - if !columns.iter().any(|c| c == target) { - return Err(format!("Target column {} not defined in table {}", target, table_name)); - } - - Ok(()) -} diff --git a/server/src/steel/validation.rs b/server/src/steel/validation.rs new file mode 100644 index 0000000..dd7047a --- /dev/null +++ b/server/src/steel/validation.rs @@ -0,0 +1,2 @@ +// src/steel/validation.rs +pub mod script; diff --git a/server/src/steel/validation/script.rs b/server/src/steel/validation/script.rs index 427fafc..53f040e 100644 --- a/server/src/steel/validation/script.rs +++ b/server/src/steel/validation/script.rs @@ -1,30 +1,37 @@ // src/steel/validation/script.rs +use steel_core::steel_vm::engine::Engine; use std::fmt; -use super::handlers::engine::validate_steel_script; -use tonic::Status; #[derive(Debug)] pub enum ScriptValidationError { EmptyScript, - SteelValidation(String), + InvalidSyntax(String), + MissingTransformFunction, } impl fmt::Display for ScriptValidationError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ScriptValidationError::EmptyScript => write!(f, "Script cannot be empty"), - ScriptValidationError::SteelValidation(msg) => write!(f, "Steel validation error: {}", msg), + Self::EmptyScript => write!(f, "Script cannot be empty"), + Self::InvalidSyntax(msg) => write!(f, "Syntax error: {}", msg), + Self::MissingTransformFunction => write!(f, "Script must define a 'transform' function"), } } } pub fn validate_script(script: &str) -> Result<(), ScriptValidationError> { + // Check for empty script if script.trim().is_empty() { return Err(ScriptValidationError::EmptyScript); } - validate_steel_script(script) - .map_err(|e| ScriptValidationError::SteelValidation(e.message().to_string()))?; + // Create a new Steel engine + let mut engine = Engine::new(); + + // Check for the presence of a 'transform' function + if engine.extract_value("transform").is_err() { + return Err(ScriptValidationError::MissingTransformFunction); + } Ok(()) } diff --git a/server/src/table_script/handlers/post_table_script.rs b/server/src/table_script/handlers/post_table_script.rs index ca9695f..28b3319 100644 --- a/server/src/table_script/handlers/post_table_script.rs +++ b/server/src/table_script/handlers/post_table_script.rs @@ -2,8 +2,29 @@ use tonic::Status; use sqlx::{PgPool, Error as SqlxError}; use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScriptResponse}; -use crate::steel::handlers::evaluator::validate_target_column; use crate::steel::validation::script::validate_script; +use serde_json::Value; + +const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"]; + +fn validate_target_column( + table_name: &str, + target: &str, + table_columns: &Value, +) -> Result<(), String> { + if SYSTEM_COLUMNS.contains(&target) { + return Err(format!("Cannot override system column: {}", target)); + } + + let columns: Vec = serde_json::from_value(table_columns.clone()) + .map_err(|e| format!("Invalid column data: {}", e))?; + + if !columns.iter().any(|c| c == target) { + return Err(format!("Target column {} not defined in table {}", target, table_name)); + } + + Ok(()) +} pub async fn post_table_script( db_pool: &PgPool,