wasm-sandbox 0.4.1

A secure WebAssembly sandbox with dead-simple ease of use, progressive complexity APIs, and comprehensive safety controls
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
//! Security policies and resource limitations

use std::collections::HashMap;
use std::path::PathBuf;

pub mod audit;
pub mod capabilities;
pub mod resource_limits;
pub mod audit_impl;

/// Host specification for network access
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HostSpec {
    /// Hostname or IP address
    pub host: String,
    
    /// Port range (inclusive)
    pub ports: Option<PortRange>,
    
    /// Whether to allow secure connections (HTTPS)
    pub secure: bool,
}

/// Port range specification
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PortRange {
    /// Start port (inclusive)
    pub start: u16,
    
    /// End port (inclusive)
    pub end: u16,
}

impl PortRange {
    /// Create a new port range
    pub fn new(start: u16, end: u16) -> Self {
        assert!(start <= end, "Start port must be less than or equal to end port");
        Self { start, end }
    }
    
    /// Create a single port range
    pub fn single(port: u16) -> Self {
        Self {
            start: port,
            end: port,
        }
    }
    
    /// Check if a port is in the range
    pub fn contains(&self, port: u16) -> bool {
        port >= self.start && port <= self.end
    }
}

/// Network access capabilities
#[derive(Debug, Clone, PartialEq)]
pub enum NetworkCapability {
    /// No network access allowed
    None,
    
    /// Loopback only (localhost)
    Loopback,
    
    /// Specific hosts only
    AllowedHosts(Vec<HostSpec>),
    
    /// Specific ports only
    AllowedPorts(Vec<PortRange>),
    
    /// Full network access
    Full,
}

impl Default for NetworkCapability {
    fn default() -> Self {
        Self::None
    }
}

/// Filesystem access capabilities
#[derive(Debug, Clone, PartialEq)]
pub struct FilesystemCapability {
    /// Directories that can be read from
    pub readable_dirs: Vec<PathBuf>,
    
    /// Directories that can be written to
    pub writable_dirs: Vec<PathBuf>,
    
    /// Maximum file size for writing
    pub max_file_size: Option<u64>,
    
    /// Allow file creation
    pub allow_create: bool,
    
    /// Allow file deletion
    pub allow_delete: bool,
}

impl Default for FilesystemCapability {
    fn default() -> Self {
        Self {
            readable_dirs: Vec::new(),
            writable_dirs: Vec::new(),
            max_file_size: None,
            allow_create: false,
            allow_delete: false,
        }
    }
}

/// Environment variable access capabilities
#[derive(Debug, Clone, PartialEq)]
pub enum EnvironmentCapability {
    /// No environment variable access
    None,
    
    /// Allow only specific variables
    Allowlist(Vec<String>),
    
    /// Deny specific variables
    Denylist(Vec<String>),
    
    /// Full environment variable access
    Full,
}

impl Default for EnvironmentCapability {
    fn default() -> Self {
        Self::None
    }
}

/// Process creation capability
#[derive(Debug, Clone, PartialEq)]
pub enum ProcessCapability {
    /// No process creation allowed
    None,
    
    /// Allow only specific commands
    AllowedCommands(Vec<String>),
    
    /// Full process creation capability
    Full,
}

impl Default for ProcessCapability {
    fn default() -> Self {
        Self::None
    }
}

/// Time access capability
#[derive(Debug, Clone, PartialEq)]
pub enum TimeCapability {
    /// Read-only time access
    ReadOnly,
    
    /// Full time access (can set system time)
    Full,
}

impl Default for TimeCapability {
    fn default() -> Self {
        Self::ReadOnly
    }
}

/// Random number generation capability
#[derive(Debug, Clone, PartialEq)]
pub enum RandomCapability {
    /// No random number generation
    None,
    
    /// Pseudo-random number generation only
    PseudoOnly,
    
    /// Full random number generation (includes secure random)
    Full,
}

impl Default for RandomCapability {
    fn default() -> Self {
        Self::PseudoOnly
    }
}

/// Custom capability type
#[derive(Debug, Clone, PartialEq)]
pub enum CustomCapability {
    /// Boolean capability (enabled/disabled)
    Boolean(bool),
    
    /// Numeric capability with limits
    Numeric {
        /// Current value
        value: i64,
        /// Minimum allowed value
        min: i64,
        /// Maximum allowed value
        max: i64,
    },
    
    /// String capability
    String(String),
    
    /// String list capability
    StringList(Vec<String>),
}

/// Security capabilities for the sandbox
#[derive(Debug, Clone, PartialEq)]
pub struct Capabilities {
    /// Network access permissions
    pub network: NetworkCapability,
    
    /// Filesystem access permissions
    pub filesystem: FilesystemCapability,
    
    /// Environment variable access
    pub environment: EnvironmentCapability,
    
    /// Process creation capability
    pub process: ProcessCapability,
    
    /// Time access capability (for limiting time manipulation)
    pub time: TimeCapability,
    
    /// Random number generation capability
    pub random: RandomCapability,
    
    /// Custom capabilities map
    pub custom: HashMap<String, CustomCapability>,
}

impl Capabilities {
    /// Create capabilities with minimal permissions (most restrictive)
    pub fn minimal() -> Self {
        Self {
            network: NetworkCapability::None,
            filesystem: FilesystemCapability::default(),
            environment: EnvironmentCapability::None,
            process: ProcessCapability::None,
            time: TimeCapability::ReadOnly,
            random: RandomCapability::PseudoOnly,
            custom: HashMap::new(),
        }
    }
    
    /// Create capabilities for development (least restrictive)
    pub fn development() -> Self {
        Self {
            network: NetworkCapability::Loopback,
            filesystem: FilesystemCapability {
                readable_dirs: vec![std::env::current_dir().unwrap_or_default()],
                writable_dirs: vec![std::env::temp_dir()],
                max_file_size: Some(10 * 1024 * 1024), // 10MB
                allow_create: true,
                allow_delete: false,
            },
            environment: EnvironmentCapability::Allowlist(vec![
                "PATH".to_string(),
                "TEMP".to_string(),
                "TMP".to_string(),
            ]),
            process: ProcessCapability::None,
            time: TimeCapability::ReadOnly,
            random: RandomCapability::Full,
            custom: HashMap::new(),
        }
    }
    
    /// Add a custom capability
    pub fn add_custom(&mut self, name: &str, capability: CustomCapability) {
        self.custom.insert(name.to_string(), capability);
    }
    
    /// Get a custom capability
    pub fn get_custom(&self, name: &str) -> Option<&CustomCapability> {
        self.custom.get(name)
    }
}

impl Default for Capabilities {
    fn default() -> Self {
        Self::minimal()
    }
}

/// Memory resource limits
#[derive(Debug, Clone)]
pub struct MemoryLimits {
    /// Maximum memory pages (64KB each)
    pub max_memory_pages: u32,
    
    /// Reserved memory pages
    pub reserved_memory_pages: u32,
    
    /// Growth rate limiting
    pub max_growth_rate: Option<u32>,
    
    /// Maximum memory addresses
    pub max_tables: u32,
}

impl Default for MemoryLimits {
    fn default() -> Self {
        Self {
            max_memory_pages: 160, // 10MB (160 * 64KB)
            reserved_memory_pages: 16, // 1MB (16 * 64KB)
            max_growth_rate: Some(10),
            max_tables: 1,
        }
    }
}

/// CPU resource limits
#[derive(Debug, Clone)]
pub struct CpuLimits {
    /// Maximum execution time in milliseconds
    pub max_execution_time_ms: u64,
    
    /// CPU usage percentage (0-100)
    pub cpu_usage_percentage: Option<u8>,
    
    /// Thread limit
    pub max_threads: Option<u32>,
}

impl Default for CpuLimits {
    fn default() -> Self {
        Self {
            max_execution_time_ms: 5000, // 5 seconds
            cpu_usage_percentage: Some(50), // 50%
            max_threads: Some(1), // Single-threaded by default
        }
    }
}

/// I/O resource limits
#[derive(Debug, Clone)]
pub struct IoLimits {
    /// Maximum number of open files
    pub max_open_files: u32,
    
    /// Maximum read bytes per second
    pub max_read_bytes_per_second: Option<u64>,
    
    /// Maximum write bytes per second
    pub max_write_bytes_per_second: Option<u64>,
    
    /// Maximum total read bytes
    pub max_total_read_bytes: Option<u64>,
    
    /// Maximum total write bytes
    pub max_total_write_bytes: Option<u64>,
}

impl Default for IoLimits {
    fn default() -> Self {
        Self {
            max_open_files: 10,
            max_read_bytes_per_second: Some(1024 * 1024), // 1MB/s
            max_write_bytes_per_second: Some(1024 * 1024), // 1MB/s
            max_total_read_bytes: Some(10 * 1024 * 1024), // 10MB
            max_total_write_bytes: Some(5 * 1024 * 1024), // 5MB
        }
    }
}

/// Time limits
#[derive(Debug, Clone)]
pub struct TimeLimits {
    /// Maximum execution time in milliseconds
    pub max_total_time_ms: u64,
    
    /// Maximum idle time in milliseconds
    pub max_idle_time_ms: Option<u64>,
}

impl Default for TimeLimits {
    fn default() -> Self {
        Self {
            max_total_time_ms: 30000, // 30 seconds
            max_idle_time_ms: Some(5000), // 5 seconds
        }
    }
}

/// Resource limits for the sandbox
#[derive(Debug, Clone)]
pub struct ResourceLimits {
    /// Memory limits in bytes
    pub memory: MemoryLimits,
    
    /// CPU limits
    pub cpu: CpuLimits,
    
    /// I/O limits
    pub io: IoLimits,
    
    /// Time limits
    pub time: TimeLimits,
    
    /// Wasmtime fuel limits (instruction counting)
    pub fuel: Option<u64>,
}

impl Default for ResourceLimits {
    fn default() -> Self {
        Self {
            memory: MemoryLimits::default(),
            cpu: CpuLimits::default(),
            io: IoLimits::default(),
            time: TimeLimits::default(),
            fuel: Some(10_000_000), // 10M instructions by default
        }
    }
}