steel validation in progress
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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<String> = 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(())
|
||||
}
|
||||
2
server/src/steel/validation.rs
Normal file
2
server/src/steel/validation.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
// src/steel/validation.rs
|
||||
pub mod script;
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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<String> = 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,
|
||||
|
||||
Reference in New Issue
Block a user