use crate::runtime::values::Value;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct FFIResourceLimits {
pub max_execution_time: Duration,
pub max_memory_bytes: usize,
pub max_stack_depth: usize,
pub max_recursion_depth: usize,
pub max_input_size: usize,
}
impl Default for FFIResourceLimits {
fn default() -> Self {
Self {
max_execution_time: Duration::from_secs(30),
max_memory_bytes: 100_000_000, max_stack_depth: 1000,
max_recursion_depth: 100,
max_input_size: 10_000_000, }
}
}
pub struct FFIInputValidator;
impl FFIInputValidator {
pub fn validate_source(source: &str, limits: &FFIResourceLimits) -> Result<(), String> {
if source.len() > limits.max_input_size {
return Err(format!(
"Input too large: {} bytes (max: {} bytes)",
source.len(),
limits.max_input_size
));
}
if source.contains('\0') {
return Err("Input contains null bytes".to_string());
}
for line in source.lines() {
if line.len() > 1_000_000 {
return Err("Line too long (potential DoS)".to_string());
}
}
Ok(())
}
pub fn validate_value(value: &Value, limits: &FFIResourceLimits) -> Result<(), String> {
match value {
Value::String(s) => {
if s.len() > limits.max_input_size {
return Err("String value too large".to_string());
}
if s.contains('\0') {
return Err("String contains null bytes".to_string());
}
}
Value::List(list) => {
if list.len() > 100_000 {
return Err("List too large".to_string());
}
for item in list {
Self::validate_value(item, limits)?;
}
}
Value::Array(arr) => {
if arr.len() > 100_000 {
return Err("Array too large".to_string());
}
for item in arr {
Self::validate_value(item, limits)?;
}
}
Value::Map(map) => {
if map.len() > 100_000 {
return Err("Map too large".to_string());
}
for (k, v) in map {
if k.len() > 10_000 {
return Err("Map key too long".to_string());
}
Self::validate_value(v, limits)?;
}
}
_ => {} }
Ok(())
}
pub fn sanitize_string(input: &str) -> String {
input.chars().filter(|c| *c != '\0').collect()
}
}
pub struct FFIExecutionMonitor {
start_time: Instant,
limits: FFIResourceLimits,
}
impl FFIExecutionMonitor {
pub fn new(limits: FFIResourceLimits) -> Self {
Self {
start_time: Instant::now(),
limits,
}
}
pub fn check_timeout(&self) -> Result<(), String> {
if self.start_time.elapsed() > self.limits.max_execution_time {
return Err("Execution timeout exceeded".to_string());
}
Ok(())
}
pub fn remaining_time(&self) -> Duration {
self.limits.max_execution_time.saturating_sub(self.start_time.elapsed())
}
}
#[derive(Debug, Clone)]
pub struct FFISandbox {
pub allow_file_access: bool,
pub allow_network_access: bool,
pub allow_system_calls: bool,
pub allowed_paths: Vec<String>,
}
impl Default for FFISandbox {
fn default() -> Self {
Self {
allow_file_access: false,
allow_network_access: false,
allow_system_calls: false,
allowed_paths: vec![],
}
}
}
impl FFISandbox {
pub fn is_path_allowed(&self, path: &str) -> bool {
if !self.allow_file_access {
return false;
}
if self.allowed_paths.is_empty() {
return true; }
for allowed in &self.allowed_paths {
if path.starts_with(allowed) {
return true;
}
}
false
}
pub fn is_network_allowed(&self) -> bool {
self.allow_network_access
}
pub fn is_system_calls_allowed(&self) -> bool {
self.allow_system_calls
}
}
#[derive(Debug, Clone)]
pub struct FFISecurityContext {
pub limits: FFIResourceLimits,
pub sandbox: FFISandbox,
pub enable_validation: bool,
}
impl Default for FFISecurityContext {
fn default() -> Self {
Self {
limits: FFIResourceLimits::default(),
sandbox: FFISandbox::default(),
enable_validation: true,
}
}
}
impl FFISecurityContext {
pub fn strict() -> Self {
Self {
limits: FFIResourceLimits {
max_execution_time: Duration::from_secs(10),
max_memory_bytes: 10_000_000, max_stack_depth: 100,
max_recursion_depth: 10,
max_input_size: 1_000_000, },
sandbox: FFISandbox::default(), enable_validation: true,
}
}
pub fn permissive() -> Self {
Self {
limits: FFIResourceLimits {
max_execution_time: Duration::from_secs(300), max_memory_bytes: 1_000_000_000, max_stack_depth: 10000,
max_recursion_depth: 1000,
max_input_size: 100_000_000, },
sandbox: FFISandbox {
allow_file_access: true,
allow_network_access: true,
allow_system_calls: true,
allowed_paths: vec![],
},
enable_validation: false,
}
}
}