From 6db148864b848ab03c83357a3821e672ea44e319 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 09:43:14 +0000 Subject: [PATCH] fix(stdlib): enforce FileSystem capability checks in file.rs Added missing `Capability::FileSystem` checks to `read`, `write`, `copy`, `remove`, and `exists` functions in `stdlib/src/file.rs` to prevent sandboxed code from bypassing restrictions. Also added the capability requirement to the `std.file` module registration. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- fix_file.sh | 2 + stdlib/src/file.rs | 98 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 26 deletions(-) create mode 100644 fix_file.sh diff --git a/fix_file.sh b/fix_file.sh new file mode 100644 index 00000000..a1f1ba22 --- /dev/null +++ b/fix_file.sh @@ -0,0 +1,2 @@ +#!/bin/bash +# A script to patch file.rs diff --git a/stdlib/src/file.rs b/stdlib/src/file.rs index 925f1bd6..ed5c1c54 100644 --- a/stdlib/src/file.rs +++ b/stdlib/src/file.rs @@ -2,7 +2,11 @@ use crate::{StdFunction, StdlibModule, StdlibRegistry}; use std::collections::HashMap; use std::fs; use std::rc::Rc; -use techscript_runtime::{error::RuntimeError, value::RuntimeValue}; +use techscript_runtime::{ + context::Capability, + error::{RuntimeError, RuntimeErrorKind}, + value::RuntimeValue, +}; impl StdlibRegistry { pub fn register_file(&mut self) { @@ -14,12 +18,22 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "read".to_string(), arity: 1, - callback: |_ctx, args| { + callback: |ctx, args| { + if !ctx.config.capabilities.contains(&Capability::FileSystem) { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: FileSystem capability is denied" + .to_string(), + ), + None, + None, + )); + } let path = match &args[0] { RuntimeValue::Str(s) => s.clone(), _ => { return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::TypeMismatch { + RuntimeErrorKind::TypeMismatch { expected: "string".to_string(), found: "other".to_string(), }, @@ -30,9 +44,7 @@ impl StdlibRegistry { }; let content = fs::read_to_string(&path).map_err(|e| { RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - e.to_string(), - ), + RuntimeErrorKind::InvalidOperation(e.to_string()), None, None, ) @@ -47,12 +59,22 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "write".to_string(), arity: 2, - callback: |_ctx, args| { + callback: |ctx, args| { + if !ctx.config.capabilities.contains(&Capability::FileSystem) { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: FileSystem capability is denied" + .to_string(), + ), + None, + None, + )); + } let path = match &args[0] { RuntimeValue::Str(s) => s.clone(), _ => { return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::TypeMismatch { + RuntimeErrorKind::TypeMismatch { expected: "string".to_string(), found: "other".to_string(), }, @@ -65,7 +87,7 @@ impl StdlibRegistry { RuntimeValue::Str(s) => s.clone(), _ => { return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::TypeMismatch { + RuntimeErrorKind::TypeMismatch { expected: "string".to_string(), found: "other".to_string(), }, @@ -76,9 +98,7 @@ impl StdlibRegistry { }; fs::write(&path, &content).map_err(|e| { RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - e.to_string(), - ), + RuntimeErrorKind::InvalidOperation(e.to_string()), None, None, ) @@ -93,12 +113,22 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "copy".to_string(), arity: 2, - callback: |_ctx, args| { + callback: |ctx, args| { + if !ctx.config.capabilities.contains(&Capability::FileSystem) { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: FileSystem capability is denied" + .to_string(), + ), + None, + None, + )); + } let src = match &args[0] { RuntimeValue::Str(s) => s.clone(), _ => { return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::TypeMismatch { + RuntimeErrorKind::TypeMismatch { expected: "string".to_string(), found: "other".to_string(), }, @@ -111,7 +141,7 @@ impl StdlibRegistry { RuntimeValue::Str(s) => s.clone(), _ => { return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::TypeMismatch { + RuntimeErrorKind::TypeMismatch { expected: "string".to_string(), found: "other".to_string(), }, @@ -122,9 +152,7 @@ impl StdlibRegistry { }; fs::copy(&src, &dest).map_err(|e| { RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - e.to_string(), - ), + RuntimeErrorKind::InvalidOperation(e.to_string()), None, None, ) @@ -139,12 +167,22 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "remove".to_string(), arity: 1, - callback: |_ctx, args| { + callback: |ctx, args| { + if !ctx.config.capabilities.contains(&Capability::FileSystem) { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: FileSystem capability is denied" + .to_string(), + ), + None, + None, + )); + } let path = match &args[0] { RuntimeValue::Str(s) => s.clone(), _ => { return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::TypeMismatch { + RuntimeErrorKind::TypeMismatch { expected: "string".to_string(), found: "other".to_string(), }, @@ -155,9 +193,7 @@ impl StdlibRegistry { }; fs::remove_file(&path).map_err(|e| { RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - e.to_string(), - ), + RuntimeErrorKind::InvalidOperation(e.to_string()), None, None, ) @@ -172,12 +208,22 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "exists".to_string(), arity: 1, - callback: |_ctx, args| { + callback: |ctx, args| { + if !ctx.config.capabilities.contains(&Capability::FileSystem) { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: FileSystem capability is denied" + .to_string(), + ), + None, + None, + )); + } let path = match &args[0] { RuntimeValue::Str(s) => s.clone(), _ => { return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::TypeMismatch { + RuntimeErrorKind::TypeMismatch { expected: "string".to_string(), found: "other".to_string(), }, @@ -197,7 +243,7 @@ impl StdlibRegistry { name: "std.file".to_string(), version: "1.0.0".to_string(), exports, - required_capabilities: Vec::new(), + required_capabilities: vec![Capability::FileSystem], }, ); }