From 1ab11fa44c5895e281a49a0062bc341f65331172 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 08:25:24 +0000 Subject: [PATCH 1/2] Enforce Network capability checks for std.socket operations Added `Capability::Network` checks in `stdlib/src/socket.rs` before `TcpStream::connect` and `TcpListener::bind` to ensure network access is restricted when the capability is denied. Registered the network capability in the `std.socket` module exports. Added `test_socket_module_sandboxing` to explicitly test privileged vs unprivileged contexts. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- stdlib/src/socket.rs | 32 +++++++++++++++++++--- stdlib/tests/stdlib_tests.rs | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/stdlib/src/socket.rs b/stdlib/src/socket.rs index 3c6e483b..6d048760 100644 --- a/stdlib/src/socket.rs +++ b/stdlib/src/socket.rs @@ -2,7 +2,9 @@ use crate::{StdFunction, StdlibModule, StdlibRegistry}; use std::collections::HashMap; use std::net::TcpStream; use std::rc::Rc; -use techscript_runtime::{error::RuntimeError, value::RuntimeValue}; +use techscript_runtime::{ + context::Capability, error::RuntimeError, value::RuntimeValue, +}; impl StdlibRegistry { pub fn register_socket(&mut self) { @@ -14,7 +16,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "connect".to_string(), arity: 2, - callback: |_ctx, args| { + callback: |ctx, args| { let host = match &args[0] { RuntimeValue::Str(s) => s.clone(), _ => { @@ -42,6 +44,17 @@ impl StdlibRegistry { } }; let addr = format!("{}:{}", host, port); + + if !ctx.config.capabilities.contains(&Capability::Network) { + return Err(RuntimeError::new( + techscript_runtime::error::RuntimeErrorKind::InvalidOperation( + "Security policy violation: Network capability is denied".to_string(), + ), + None, + None, + )); + } + TcpStream::connect(&addr).map_err(|e| { RuntimeError::new( techscript_runtime::error::RuntimeErrorKind::InvalidOperation( @@ -61,7 +74,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "listen".to_string(), arity: 1, - callback: |_ctx, args| { + callback: |ctx, args| { let port = match &args[0] { RuntimeValue::Int(n) => *n as u16, _ => { @@ -76,6 +89,17 @@ impl StdlibRegistry { } }; let addr = format!("0.0.0.0:{}", port); + + if !ctx.config.capabilities.contains(&Capability::Network) { + return Err(RuntimeError::new( + techscript_runtime::error::RuntimeErrorKind::InvalidOperation( + "Security policy violation: Network capability is denied".to_string(), + ), + None, + None, + )); + } + let _listener = std::net::TcpListener::bind(&addr).map_err(|e| { RuntimeError::new( techscript_runtime::error::RuntimeErrorKind::InvalidOperation( @@ -96,7 +120,7 @@ impl StdlibRegistry { name: "std.socket".to_string(), version: "1.0.0".to_string(), exports, - required_capabilities: Vec::new(), + required_capabilities: vec![Capability::Network], }, ); } diff --git a/stdlib/tests/stdlib_tests.rs b/stdlib/tests/stdlib_tests.rs index f7df969f..baa5f3fb 100644 --- a/stdlib/tests/stdlib_tests.rs +++ b/stdlib/tests/stdlib_tests.rs @@ -1151,3 +1151,54 @@ fn test_ai_generate_text() { let val = res.unwrap(); assert!(val.as_string().unwrap().contains("Prompt: What is 2+2?")); } + +#[test] +fn test_socket_module_sandboxing() { + let registry = StdlibRegistry::new(); + let socket = registry.get_module("std.socket").unwrap(); + + let mut config_unprivileged = RuntimeConfig::default(); + config_unprivileged.capabilities.remove(&Capability::Network); + let mut ctx_unprivileged = RuntimeContext::new(config_unprivileged); + + let mut config_privileged = RuntimeConfig::default(); + config_privileged.capabilities.insert(Capability::Network); + let mut ctx_privileged = RuntimeContext::new(config_privileged); + + let connect_fn = socket.exports.get("connect").unwrap(); + let listen_fn = socket.exports.get("listen").unwrap(); + + // 1. Unprivileged context should fail due to security policy + let res_connect = connect_fn.call( + &mut ctx_unprivileged, + vec![ + RuntimeValue::Str("example.com".to_string()), + RuntimeValue::Int(80), + ], + ); + assert!(res_connect.is_err()); + let err = res_connect.unwrap_err(); + assert!(err.message.contains("Security policy violation")); + + let res_listen = listen_fn.call(&mut ctx_unprivileged, vec![RuntimeValue::Int(8080)]); + assert!(res_listen.is_err()); + let err = res_listen.unwrap_err(); + assert!(err.message.contains("Security policy violation")); + + // 2. Privileged context shouldn't fail due to security policy (though they may fail due to valid network errors like bad host or port already in use) + let res_connect = connect_fn.call( + &mut ctx_privileged, + vec![ + RuntimeValue::Str("localhost".to_string()), + RuntimeValue::Int(9999), // likely nobody listening + ], + ); + if let Err(e) = res_connect { + assert!(!e.message.contains("Security policy violation")); + } + + let res_listen = listen_fn.call(&mut ctx_privileged, vec![RuntimeValue::Int(0)]); // port 0 allows OS to pick an available port + if let Err(e) = res_listen { + assert!(!e.message.contains("Security policy violation")); + } +} From 9762a94d92e8337b0f69181069109771d1259392 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 08:48:12 +0000 Subject: [PATCH 2/2] Enforce Network capability checks for std.socket operations Added `Capability::Network` checks in `stdlib/src/socket.rs` before `TcpStream::connect` and `TcpListener::bind` to ensure network access is restricted when the capability is denied. Registered the network capability in the `std.socket` module exports. Added `test_socket_module_sandboxing` to explicitly test privileged vs unprivileged contexts. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- stdlib/src/socket.rs | 10 +++++----- stdlib/tests/stdlib_tests.rs | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/stdlib/src/socket.rs b/stdlib/src/socket.rs index 6d048760..2d3e7f30 100644 --- a/stdlib/src/socket.rs +++ b/stdlib/src/socket.rs @@ -2,9 +2,7 @@ use crate::{StdFunction, StdlibModule, StdlibRegistry}; use std::collections::HashMap; use std::net::TcpStream; use std::rc::Rc; -use techscript_runtime::{ - context::Capability, error::RuntimeError, value::RuntimeValue, -}; +use techscript_runtime::{context::Capability, error::RuntimeError, value::RuntimeValue}; impl StdlibRegistry { pub fn register_socket(&mut self) { @@ -48,7 +46,8 @@ impl StdlibRegistry { if !ctx.config.capabilities.contains(&Capability::Network) { return Err(RuntimeError::new( techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - "Security policy violation: Network capability is denied".to_string(), + "Security policy violation: Network capability is denied" + .to_string(), ), None, None, @@ -93,7 +92,8 @@ impl StdlibRegistry { if !ctx.config.capabilities.contains(&Capability::Network) { return Err(RuntimeError::new( techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - "Security policy violation: Network capability is denied".to_string(), + "Security policy violation: Network capability is denied" + .to_string(), ), None, None, diff --git a/stdlib/tests/stdlib_tests.rs b/stdlib/tests/stdlib_tests.rs index baa5f3fb..bb9c7232 100644 --- a/stdlib/tests/stdlib_tests.rs +++ b/stdlib/tests/stdlib_tests.rs @@ -1158,7 +1158,9 @@ fn test_socket_module_sandboxing() { let socket = registry.get_module("std.socket").unwrap(); let mut config_unprivileged = RuntimeConfig::default(); - config_unprivileged.capabilities.remove(&Capability::Network); + config_unprivileged + .capabilities + .remove(&Capability::Network); let mut ctx_unprivileged = RuntimeContext::new(config_unprivileged); let mut config_privileged = RuntimeConfig::default();