WASM Secure Sandbox - Implementation Guide
WASM Secure Sandbox - Implementation Guide
Target Audience: Coder Agents (Day 4-5 Implementation) Priority: HIGH - Critical Security Fixes Time Estimate: 16 hours (2 days)
Implementation Overview
What Already Exists
The security infrastructure is already partially in place:
/heliosdb-wasm/src/resource_limits.rs - WasmResourceLimits struct - ResourceLimiter implementation - Secure engine creation - Comprehensive tests
/heliosdb-wasm/src/pointer_validation.rs - Pointer validation functions - EscapeDetector - Safe slice creation - Validation tests
/heliosdb-wasm/src/allocation_tracker.rs - AllocationTracker struct - Double-free prevention - Memory leak detection - Tracking tests
/heliosdb-procedures/src/wasm_sandbox.rs - SecurityProfile system - Capability model - SandboxContext - Resource tracking
/heliosdb-procedures/src/wasm_runtime.rs - WasmRuntime implementation - Module compilation - Instance management - Execution with limitsWhat Needs to Be Done 🚧
Critical Fixes (from security audit):
-
Fix 15 Critical Pointer Operations (8 hours)
- heliosdb-wasm-sdk/src/host.rs (15 operations)
- heliosdb-wasm-sdk/src/loader.rs (7 operations)
- heliosdb-wasm-sdk/src/edge.rs (8 operations)
- heliosdb-wasm/src/module.rs (1 operation)
-
Implement Safe Memory Bridge (4 hours)
- Create SafeWasmMemory trait
- Implement safe host function wrappers
- Replace unsafe operations with safe API
-
Integration & Testing (4 hours)
- Integration with existing runtime
- Security test suite
- Performance benchmarks
Phase 1: Critical Pointer Operation Fixes
File 1: heliosdb-wasm-sdk/src/host.rs
Current Status: 15 unsafe pointer operations
Location: /home/claude/HeliosDB/heliosdb-wasm-sdk/src/host.rs
Fix 1: Database Query Function (CRITICAL-01)
Current Code (UNSAFE):
#[no_mangle]pub extern "C" fn heliosdb_db_query( query_ptr: *const u8, query_len: usize, out_ptr: *mut *mut u8, out_len: *mut usize,) -> i32 { unsafe { // Line 111: CRITICAL - No bounds validation let query = std::slice::from_raw_parts(query_ptr, query_len); let query_str = std::str::from_utf8_unchecked(query);
// Execute query... let result = execute_query(query_str);
// Line 159: CRITICAL - Direct pointer write *out_len = result.len(); }}Fixed Code (SAFE):
use crate::validation::{validate_and_create_slice, validate_and_read};use crate::allocation::{ALLOCATION_TRACKER};
#[no_mangle]pub extern "C" fn heliosdb_db_query( query_ptr: *const u8, query_len: usize, out_ptr: *mut *mut u8, out_len: *mut usize,) -> i32 { // Get WASM memory bounds from context let (memory_base, memory_size) = match get_wasm_memory_bounds() { Ok(bounds) => bounds, Err(_) => return -1, // Error: Cannot get memory bounds };
// SAFE: Full validation before creating slice let query = unsafe { match validate_and_create_slice( query_ptr, query_len, memory_base, memory_size, ) { Ok(slice) => slice, Err(e) => { tracing::error!("Query pointer validation failed: {}", e); return -2; // Error: Invalid pointer } } };
// SAFE: UTF-8 validation let query_str = match std::str::from_utf8(query) { Ok(s) => s, Err(_) => { tracing::error!("Query is not valid UTF-8"); return -3; // Error: Invalid UTF-8 } };
// Validate query length if query_str.len() > MAX_QUERY_LENGTH { tracing::error!("Query too long: {} > {}", query_str.len(), MAX_QUERY_LENGTH); return -4; // Error: Query too long }
// Execute query with timeout let result = match execute_query(query_str) { Ok(data) => data, Err(e) => { tracing::error!("Query execution failed: {}", e); return -5; // Error: Query failed } };
// SAFE: Validate output pointers before writing unsafe { // Validate out_ptr is not null if out_ptr.is_null() { tracing::error!("Output pointer is null"); return -6; }
// Validate out_len is not null if out_len.is_null() { tracing::error!("Output length pointer is null"); return -7; }
// Allocate result buffer (tracked) let result_size = result.len(); let result_ptr = match heliosdb_alloc(result_size) { ptr if !ptr.is_null() => ptr, _ => return -8, // Error: Allocation failed };
// Copy result data std::ptr::copy_nonoverlapping( result.as_ptr(), result_ptr, result_size, );
// Write output pointers (validated above) *out_ptr = result_ptr; *out_len = result_size; }
0 // Success}Fix 2: Allocation Function (CRITICAL-03)
Current Code (UNSAFE - Memory Leak):
#[no_mangle]pub extern "C" fn heliosdb_alloc(size: usize) -> *mut u8 { let mut buf = Vec::with_capacity(size); let ptr = buf.as_mut_ptr(); std::mem::forget(buf); // LEAK! ptr}Fixed Code (SAFE - Tracked):
use crate::allocation::ALLOCATION_TRACKER;
#[no_mangle]pub extern "C" fn heliosdb_alloc(size: usize) -> *mut u8 { // Enforce maximum allocation size if size > MAX_WASM_ALLOCATION { tracing::error!( "Allocation too large: {} > {}", size, MAX_WASM_ALLOCATION ); return std::ptr::null_mut(); }
// Allocate buffer let mut buf = Vec::with_capacity(size); buf.resize(size, 0); // Zero-initialize for security let ptr = buf.as_mut_ptr();
// Calculate WASM offset let (memory_base, _) = match get_wasm_memory_bounds() { Ok(bounds) => bounds, Err(_) => return std::ptr::null_mut(), }; let offset = (ptr as usize - memory_base) as u32;
// Track allocation (prevents leaks and double-frees) if let Err(e) = ALLOCATION_TRACKER.register(offset, size) { tracing::error!("Failed to track allocation: {}", e); // Don't leak - drop the buffer drop(buf); return std::ptr::null_mut(); }
// Intentionally forget (ownership transferred to WASM) std::mem::forget(buf);
tracing::debug!("Allocated {} bytes at offset 0x{:x}", size, offset); ptr}Fix 3: Deallocation Function (CRITICAL-04)
Current Code (UNSAFE - Double-Free Risk):
#[no_mangle]pub extern "C" fn heliosdb_dealloc(ptr: *mut u8, size: usize) { unsafe { let _ = Vec::from_raw_parts(ptr, size, size); }}Fixed Code (SAFE - Tracked):
use crate::allocation::ALLOCATION_TRACKER;
#[no_mangle]pub extern "C" fn heliosdb_dealloc(ptr: *mut u8, size: usize) { // Calculate WASM offset let (memory_base, _) = match get_wasm_memory_bounds() { Ok(bounds) => bounds, Err(_) => { tracing::error!("Cannot get memory bounds for deallocation"); return; } }; let offset = (ptr as usize - memory_base) as u32;
// Validate this is a known allocation (prevents double-free) match ALLOCATION_TRACKER.unregister(offset, size) { Ok(info) => { tracing::debug!( "Deallocated {} bytes at offset 0x{:x} (age: {:?})", size, offset, info.timestamp.elapsed() ); } Err(e) => { tracing::error!( "Deallocation failed for offset 0x{:x}: {}", offset, e ); // Don't proceed with deallocation - could be double-free return; } }
// Safe to deallocate (validated above) unsafe { let _ = Vec::from_raw_parts(ptr, size, size); }}File 2: heliosdb-wasm-sdk/src/loader.rs
Location: /home/claude/HeliosDB/heliosdb-wasm-sdk/src/loader.rs
Issues: 7 unsafe pointer operations
Fix 4: Module Load Function (CRITICAL-05)
Current Code:
pub fn load_module(name: &str, json_config: &str) -> Result<()> { // Line 228: CRITICAL - No validation on pointers unsafe { let result = heliosdb_module_load( name.as_ptr(), name.len(), json_config.as_ptr(), json_config.len(), ); }}Fixed Code:
pub fn load_module(name: &str, json_config: &str) -> Result<()> { // Validate input lengths if name.len() > MAX_MODULE_NAME_LENGTH { return Err(Error::NameTooLong(name.len())); } if json_config.len() > MAX_CONFIG_SIZE { return Err(Error::ConfigTooLarge(json_config.len())); }
// Ensure strings are valid for the lifetime of the call let name_bytes = name.as_bytes(); let config_bytes = json_config.as_bytes();
// Validate JSON before passing to C let _: serde_json::Value = serde_json::from_str(json_config) .map_err(|e| Error::InvalidJson(e.to_string()))?;
unsafe { // Pointers are valid because: // 1. name_bytes and config_bytes live for duration of call // 2. as_ptr() on &[u8] is always valid // 3. Lengths are validated above let result = heliosdb_module_load( name_bytes.as_ptr(), name_bytes.len(), config_bytes.as_ptr(), config_bytes.len(), );
if result != 0 { return Err(Error::ModuleLoadFailed(result)); } }
tracing::info!("Loaded module: {}", name); Ok(())}File 3: heliosdb-wasm-sdk/src/edge.rs
Location: /home/claude/HeliosDB/heliosdb-wasm-sdk/src/edge.rs
Issues: 8 unsafe pointer operations (lines 169, 236, 241-248, 293, 331-342, 368)
Fix 5: Edge Function Invocation (CRITICAL-08 through CRITICAL-15)
Current Code:
pub fn invoke_edge_function( function_name: &str, region: Option<&str>, payload: &[u8],) -> Result<Vec<u8>> { let region_ptr = region.map(|r| r.as_ptr()).unwrap_or(std::ptr::null()); let region_len = region.map(|r| r.len()).unwrap_or(0);
let mut out_buf = vec![0u8; 1024 * 1024]; let mut out_len: usize = out_buf.len();
unsafe { let result = heliosdb_edge_invoke( function_name.as_ptr(), function_name.len(), region_ptr, region_len, payload.as_ptr(), payload.len(), out_buf.as_mut_ptr(), &mut out_len as *mut usize, ); }}Fixed Code:
pub fn invoke_edge_function( function_name: &str, region: Option<&str>, payload: &[u8],) -> Result<Vec<u8>> { // === Input Validation ===
// Validate function name if function_name.is_empty() { return Err(Error::InvalidParameter("Empty function name")); } if function_name.len() > MAX_FUNCTION_NAME_LENGTH { return Err(Error::NameTooLong(function_name.len())); }
// Validate payload size if payload.len() > MAX_EDGE_PAYLOAD_SIZE { return Err(Error::PayloadTooLarge(payload.len())); }
// Validate region if provided if let Some(r) = region { if r.len() > MAX_REGION_NAME_LENGTH { return Err(Error::InvalidRegion); } }
// === Prepare Safe Pointers ===
let function_bytes = function_name.as_bytes(); let region_bytes = region.map(|r| r.as_bytes());
let region_ptr = region_bytes .as_ref() .map(|r| r.as_ptr()) .unwrap_or(std::ptr::null()); let region_len = region_bytes .as_ref() .map(|r| r.len()) .unwrap_or(0);
// Allocate output buffer (capped at reasonable size) let max_response_size = std::cmp::min( 1024 * 1024, // 1MB default MAX_EDGE_RESPONSE_SIZE ); let mut out_buf = vec![0u8; max_response_size]; let mut out_len: usize = out_buf.len();
// === Execute with Safety Checks ===
let result = unsafe { // Defensive: validate pointers are not null before call debug_assert!(!function_bytes.as_ptr().is_null()); debug_assert!(!payload.as_ptr().is_null()); debug_assert!(!out_buf.as_mut_ptr().is_null());
heliosdb_edge_invoke( function_bytes.as_ptr(), function_bytes.len(), region_ptr, region_len, payload.as_ptr(), payload.len(), out_buf.as_mut_ptr(), &mut out_len as *mut usize, ) };
// === Validate Response ===
if result != 0 { return Err(Error::EdgeInvokeFailed(result)); }
// Validate out_len was not corrupted if out_len > out_buf.len() { tracing::error!( "🚨 Response length corrupted: {} > {}", out_len, out_buf.len() ); return Err(Error::CorruptedResponse); }
// Truncate to actual response size out_buf.truncate(out_len);
tracing::debug!( "Edge function '{}' returned {} bytes", function_name, out_len );
Ok(out_buf)}File 4: heliosdb-wasm/src/module.rs
Location: /home/claude/HeliosDB/heliosdb-wasm/src/module.rs
Issue: Unsafe module deserialization (CRITICAL-06)
Fix 6: Module Deserialization
Current Code:
// Line 181: CRITICAL - Deserializes untrusted datapub fn deserialize(engine: &Engine, bytes: &[u8]) -> Result<WasmModule> { let module = unsafe { WasmtimeModule::deserialize(engine, bytes) .map_err(|e| WasmError::InternalError(format!("Failed: {}", e)))? };
Ok(WasmModule { module, /* ... */ })}Fixed Code:
use sha2::{Sha256, Digest};
pub fn deserialize( engine: &Engine, bytes: &[u8], expected_hash: Option<&str>,) -> Result<WasmModule> { // Validate module size if bytes.len() > MAX_MODULE_SIZE { return Err(WasmError::ModuleTooLarge { size: bytes.len(), max: MAX_MODULE_SIZE, }); }
// Verify module hash if provided (signature verification) if let Some(expected) = expected_hash { let mut hasher = Sha256::new(); hasher.update(bytes); let computed = format!("{:x}", hasher.finalize());
if computed != expected { tracing::error!( "🚨 Module hash mismatch! Expected: {}, Got: {}", expected, computed ); return Err(WasmError::UntrustedModule); } }
// Validate WASM magic number if bytes.len() < 4 || &bytes[0..4] != b"\0asm" { return Err(WasmError::InvalidModule("Missing WASM magic number")); }
// Deserialize with Wasmtime's built-in validation // Note: Still unsafe but Wasmtime performs extensive validation internally let module = unsafe { WasmtimeModule::deserialize(engine, bytes) .map_err(|e| { tracing::error!("Module deserialization failed: {}", e); WasmError::DeserializationFailed(e.to_string()) })? };
tracing::info!( "Deserialized module: {} bytes, hash: {}", bytes.len(), expected_hash.unwrap_or("none") );
Ok(WasmModule { module, hash: expected_hash.map(|s| s.to_string()), // ... other fields })}Phase 2: Safe Memory Bridge Implementation
Step 1: Create Safe Memory Trait
File: /home/claude/HeliosDB/heliosdb-wasm/src/safe_memory.rs
//! Safe Memory Access Bridge for WASM-Host Boundary
use crate::error::{WasmError, Result};use crate::pointer_validation::{ validate_and_create_slice, validate_and_create_slice_mut, validate_and_read,};use crate::allocation_tracker::ALLOCATION_TRACKER;
/// Safe memory access interface (no raw pointers exposed)pub trait SafeWasmMemory { /// Get WASM linear memory bounds fn memory_bounds(&self) -> Result<(usize, usize)>;
/// Read bytes from WASM memory with full validation fn read_bytes(&self, offset: u32, len: u32) -> Result<Vec<u8>> { let (memory_base, memory_size) = self.memory_bounds()?;
unsafe { let ptr = (memory_base + offset as usize) as *const u8; let slice = validate_and_create_slice( ptr, len as usize, memory_base, memory_size, )?; Ok(slice.to_vec()) } }
/// Write bytes to WASM memory with full validation fn write_bytes(&mut self, offset: u32, data: &[u8]) -> Result<()> { let (memory_base, memory_size) = self.memory_bounds()?;
unsafe { let ptr = (memory_base + offset as usize) as *mut u8; let slice = validate_and_create_slice_mut( ptr, data.len(), memory_base, memory_size, )?; slice.copy_from_slice(data); }
Ok(()) }
/// Read UTF-8 string with validation fn read_string(&self, offset: u32, len: u32) -> Result<String> { // Maximum string length: 64KB const MAX_STRING_LENGTH: u32 = 64 * 1024;
if len > MAX_STRING_LENGTH { return Err(WasmError::StringTooLarge { len: len as usize, max: MAX_STRING_LENGTH as usize, }); }
let bytes = self.read_bytes(offset, len)?;
// UTF-8 validation String::from_utf8(bytes) .map_err(|e| WasmError::InvalidUtf8(e.to_string())) }
/// Write UTF-8 string fn write_string(&mut self, offset: u32, s: &str) -> Result<()> { self.write_bytes(offset, s.as_bytes()) }
/// Read typed value with alignment validation fn read_typed<T: Copy>(&self, offset: u32) -> Result<T> { let (memory_base, memory_size) = self.memory_bounds()?;
unsafe { let ptr = (memory_base + offset as usize) as *const T; validate_and_read(ptr, memory_base, memory_size) } }
/// Write typed value fn write_typed<T: Copy>(&mut self, offset: u32, value: T) -> Result<()> { let (memory_base, memory_size) = self.memory_bounds()?; let size = std::mem::size_of::<T>();
unsafe { let ptr = (memory_base + offset as usize) as *mut T;
// Validate alignment and bounds crate::pointer_validation::validate_alignment(ptr)?; crate::pointer_validation::validate_wasm_memory_bounds( ptr as *const u8, size, memory_base, memory_size, )?;
std::ptr::write(ptr, value); }
Ok(()) }
/// Allocate memory in WASM linear memory fn allocate(&mut self, size: usize) -> Result<u32>;
/// Deallocate memory fn deallocate(&mut self, offset: u32, size: usize) -> Result<()>;}Step 2: Implement for WasmInstance
impl SafeWasmMemory for WasmInstance { fn memory_bounds(&self) -> Result<(usize, usize)> { let memory = self.get_memory() .ok_or(WasmError::MemoryNotFound)?;
let base = memory.data_ptr(&self.store) as usize; let size = memory.data_size(&self.store);
Ok((base, size)) }
fn allocate(&mut self, size: usize) -> Result<u32> { // Call WASM allocator function let alloc_fn = self.get_function("__wasm_allocate") .ok_or(WasmError::AllocatorNotFound)?;
let result = alloc_fn.call( &mut self.store, &[Val::I32(size as i32)], )?;
match result.get(0) { Some(Val::I32(ptr)) if *ptr != 0 => Ok(*ptr as u32), _ => Err(WasmError::AllocationFailed), } }
fn deallocate(&mut self, offset: u32, size: usize) -> Result<()> { // Call WASM deallocator function let dealloc_fn = self.get_function("__wasm_deallocate") .ok_or(WasmError::DeallocatorNotFound)?;
dealloc_fn.call( &mut self.store, &[Val::I32(offset as i32), Val::I32(size as i32)], )?;
Ok(()) }}Phase 3: Integration & Testing
Security Test Suite
File: /home/claude/HeliosDB/heliosdb-wasm/tests/security_tests.rs
#[cfg(test)]mod security_tests { use super::*;
#[test] fn test_buffer_overflow_prevention() { let runtime = create_test_runtime();
// Attempt to read beyond memory bounds let result = runtime.memory_bridge.read_bytes( 0xFFFFFF00, // Near end of 32-bit address space 0x1000, // 4KB - will overflow );
assert!(matches!(result, Err(WasmError::OutOfBounds { .. }))); }
#[test] fn test_null_pointer_rejection() { let validator = PointerValidator::new();
let result = unsafe { validate_and_create_slice( std::ptr::null(), 100, 0x1000, 0x10000, ) };
assert!(matches!(result, Err(WasmError::NullPointer))); }
#[test] fn test_allocation_limit_enforcement() { let tracker = AllocationTracker::new( 1024 * 1024, // 1MB total 512 * 1024, // 512KB single );
// Attempt to allocate beyond single limit let result = tracker.register(0x1000, 1024 * 1024); assert!(result.is_err());
// Allocate within limits tracker.register(0x1000, 256 * 1024).unwrap(); tracker.register(0x2000, 256 * 1024).unwrap();
// Attempt to exceed total limit let result = tracker.register(0x3000, 600 * 1024); assert!(result.is_err()); }
#[test] fn test_double_free_prevention() { let tracker = AllocationTracker::new(1024 * 1024, 1024);
tracker.register(0x1000, 256).unwrap(); tracker.unregister(0x1000, 256).unwrap();
// Second free should fail let result = tracker.unregister(0x1000, 256); assert!(matches!(result, Err(WasmError::DoubleFree(0x1000)))); }
#[test] fn test_execution_timeout() { let runtime = WasmRuntime::new_with_limits( WasmResourceLimits { max_execution_time_ms: 100, // 100ms timeout ..Default::default() } );
// Load infinite loop module let module = compile_wat(r#" (module (func (export "infinite_loop") (loop $l (br $l)) ) ) "#);
let result = runtime.execute(&module, "infinite_loop", vec![]); assert!(matches!(result, Err(WasmError::Timeout(100)))); }
#[test] fn test_memory_limit_enforcement() { let runtime = WasmRuntime::new_with_limits( WasmResourceLimits { max_memory_pages: 16, // 1MB (16 pages * 64KB) ..Default::default() } );
// Attempt to allocate 10MB (exceeds limit) let module = compile_wat(r#" (module (memory (export "memory") 160) ;; 160 pages = 10MB ) "#);
let result = runtime.load_module(module); assert!(matches!(result, Err(WasmError::MemoryLimitExceeded { .. }))); }
#[test] fn test_capability_enforcement() { let runtime = WasmRuntime::new(); let profile = SecurityProfile::minimal(); // No capabilities
// Attempt file read without capability let result = runtime.execute_with_profile( &file_reader_module, "read_file", vec!["/etc/passwd".into()], profile, );
assert!(matches!( result, Err(WasmError::CapabilityDenied { .. }) )); }}Integration Tests
File: /home/claude/HeliosDB/heliosdb-wasm/tests/integration_tests.rs
#[tokio::test]async fn test_query_executor_integration() { let executor = WasmQueryExecutorIntegration::new();
// Load test module let module_code = include_bytes!("fixtures/test_procedure.wasm"); executor.load_module("test_procedure", module_code).await.unwrap();
// Execute with database-only profile let result = executor.execute_stored_procedure( "test_procedure", "process_order", vec![json!({"order_id": 123, "amount": 99.99})], SecurityProfile::database_only(), ).await.unwrap();
assert_eq!(result.len(), 1);}
#[tokio::test]async fn test_event_trigger_integration() { let trigger_system = WasmEventTriggerIntegration::new();
// Register trigger let trigger_id = trigger_system.register_trigger( "validate_insert", "validator_module", "validate", EventType::BeforeInsert, ).await.unwrap();
// Fire event let event = Event::Insert { table: "orders".to_string(), data: json!({"total": -10.0}), // Invalid: negative amount };
let result = trigger_system.fire_event(event).await; assert!(result.is_err()); // Should reject negative amount}Implementation Checklist
Day 4 (8 hours)
Hour 1-2: Setup & Infrastructure
- Review existing code in
/heliosdb-wasm/src/ - Verify
pointer_validation.rs,allocation_tracker.rs,resource_limits.rs - Add any missing utility functions
- Set up test infrastructure
Hour 3-4: Fix Critical Host Functions
- Fix
heliosdb_db_query(host.rs:111) - Fix
heliosdb_db_execute(host.rs:159) - Fix
heliosdb_alloc(host.rs:346) - Fix
heliosdb_dealloc(host.rs:355) - Add tests for each fix
Hour 5-6: Fix Module Loading
- Fix
load_modulepointer operations (loader.rs:228) - Add module signature verification (module.rs:181)
- Implement hash validation
- Add malicious module detection tests
Hour 7-8: Documentation
- Document all safety invariants
- Add inline safety comments
- Create migration guide for unsafe code
Day 5 (8 hours)
Hour 1-3: Fix Edge Functions
- Fix
register_edge_function(edge.rs:169) - Fix
invoke_edge_function(edge.rs:236-248) - Fix async invocation safety (edge.rs:331-342)
- Fix region list operations (edge.rs:368)
- Add comprehensive edge function tests
Hour 4-5: Implement Safe Memory Bridge
- Create
SafeWasmMemorytrait - Implement for
WasmInstance - Add comprehensive type conversion
- Test all memory operations
Hour 6-7: Security Test Suite
- Implement all security tests from checklist
- Run fuzzing tests (if time permits)
- Performance benchmarks
- Verify <5% overhead target
Hour 8: Final Integration
- Integration testing with query executor
- Integration testing with event system
- Integration testing with CDC pipeline
- Final documentation review
- Create handoff summary
Success Criteria
Security Criteria
- All 15 critical pointer operations have validation
- Zero unsafe operations without safety comments
- Allocation tracker prevents double-free
- Resource limits prevent exhaustion
- Execution timeout prevents infinite loops
- Security test suite 100% pass rate
- No new security warnings from audit tools
Performance Criteria
- Overhead < 5% for typical workload
- Pointer validation < 100μs per call
- Allocation tracking < 50μs per allocation
- Memory usage acceptable (< 10% increase)
Integration Criteria
- Query executor integration works
- Event trigger integration works
- CDC pipeline integration works
- Existing tests still pass
- New security tests pass
Troubleshooting
Common Issues
Issue: “Cannot get memory bounds”
- Cause: WASM instance not fully initialized
- Fix: Ensure memory is exported from module
- Prevention: Add validation in module loader
Issue: “Validation overhead too high”
- Cause: Validating on every access
- Fix: Cache validation results for sequential accesses
- Prevention: Use bulk operations where possible
Issue: “Allocation tracker out of sync”
- Cause: Missed deallocation or double-free
- Fix: Review allocation/deallocation call pairs
- Prevention: Use RAII patterns, ensure cleanup
Support & Resources
Code References
- Security audit:
/docs/WASM_SECURITY_AUDIT.md - Architecture:
/docs/architecture/WASM_SECURE_SANDBOX_DESIGN.md - Existing validation:
/heliosdb-wasm/src/pointer_validation.rs - Resource limits:
/heliosdb-wasm/src/resource_limits.rs
Testing
- Run security tests:
cargo test -p heliosdb-wasm security - Run integration tests:
cargo test -p heliosdb-wasm integration - Run all tests:
cargo test
Getting Help
- Review architecture document for design decisions
- Check existing implementations for patterns
- Reference Wasmtime docs: https://docs.wasmtime.dev/
Ready to implement! Follow this guide step-by-step for a secure, production-ready WASM sandbox.
Last Updated: 2025-11-10 Version: 1.0