steel validation in progress
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2363,6 +2363,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"steel-core",
|
"steel-core",
|
||||||
|
"steel-derive",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tonic",
|
"tonic",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ tonic-reflection = "0.12.3"
|
|||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
time = { version = "0.3.39", features = ["local-offset"] }
|
time = { version = "0.3.39", features = ["local-offset"] }
|
||||||
steel-core = { git = "https://github.com/mattwparas/steel.git", branch = "master", package = "steel-core" }
|
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]
|
[lib]
|
||||||
name = "server"
|
name = "server"
|
||||||
|
|||||||
@@ -1,6 +1 @@
|
|||||||
// src/steel/handlers.rs
|
// 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
|
// src/steel/validation/script.rs
|
||||||
|
use steel_core::steel_vm::engine::Engine;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use super::handlers::engine::validate_steel_script;
|
|
||||||
use tonic::Status;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ScriptValidationError {
|
pub enum ScriptValidationError {
|
||||||
EmptyScript,
|
EmptyScript,
|
||||||
SteelValidation(String),
|
InvalidSyntax(String),
|
||||||
|
MissingTransformFunction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ScriptValidationError {
|
impl fmt::Display for ScriptValidationError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ScriptValidationError::EmptyScript => write!(f, "Script cannot be empty"),
|
Self::EmptyScript => write!(f, "Script cannot be empty"),
|
||||||
ScriptValidationError::SteelValidation(msg) => write!(f, "Steel validation error: {}", msg),
|
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> {
|
pub fn validate_script(script: &str) -> Result<(), ScriptValidationError> {
|
||||||
|
// Check for empty script
|
||||||
if script.trim().is_empty() {
|
if script.trim().is_empty() {
|
||||||
return Err(ScriptValidationError::EmptyScript);
|
return Err(ScriptValidationError::EmptyScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_steel_script(script)
|
// Create a new Steel engine
|
||||||
.map_err(|e| ScriptValidationError::SteelValidation(e.message().to_string()))?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,29 @@
|
|||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
use sqlx::{PgPool, Error as SqlxError};
|
use sqlx::{PgPool, Error as SqlxError};
|
||||||
use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScriptResponse};
|
use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScriptResponse};
|
||||||
use crate::steel::handlers::evaluator::validate_target_column;
|
|
||||||
use crate::steel::validation::script::validate_script;
|
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(
|
pub async fn post_table_script(
|
||||||
db_pool: &PgPool,
|
db_pool: &PgPool,
|
||||||
|
|||||||
Reference in New Issue
Block a user