wasm_sandbox/
error.rs

1use std::path::PathBuf;
2use std::time::Duration;
3use thiserror::Error;
4
5/// Instance identifier for tracking across the sandbox
6pub type InstanceId = uuid::Uuid;
7
8/// Resource types that can be limited
9#[derive(Debug, Clone)]
10pub enum ResourceKind {
11    Memory,
12    CpuTime,
13    Fuel,
14    FileHandles,
15    NetworkConnections,
16    ExecutionTime,
17}
18
19/// Security context providing details about attempted operations
20#[derive(Debug, Clone)]
21pub struct SecurityContext {
22    pub attempted_operation: String,
23    pub required_capability: String,
24    pub available_capabilities: Vec<String>,
25}
26
27/// Enhanced error types for the wasm-sandbox crate with detailed context
28#[derive(Error, Debug)]
29pub enum SandboxError {
30    /// Security violation with detailed context
31    #[error("Security violation: {violation}")]
32    SecurityViolation { 
33        violation: String, 
34        instance_id: Option<InstanceId>,
35        context: SecurityContext,
36    },
37    
38    /// Resource exhausted with specific usage details
39    #[error("Resource exhausted: {kind:?} used {used}/{limit}")]
40    ResourceExhausted { 
41        kind: ResourceKind, 
42        limit: u64, 
43        used: u64,
44        instance_id: Option<InstanceId>,
45        suggestion: Option<String>,
46    },
47    
48    /// WASM runtime error with function context
49    #[error("WebAssembly runtime error in function '{function}': {message}")]
50    WasmRuntime {
51        function: String,
52        instance_id: Option<InstanceId>,
53        message: String,
54    },
55    
56    /// Configuration error with helpful suggestions
57    #[error("Configuration error: {message}")]
58    Configuration { 
59        message: String,
60        suggestion: Option<String>,
61        field: Option<String>,
62    },
63    
64    /// Module compilation or loading error
65    #[error("Module error: {operation} failed - {reason}")]
66    Module {
67        operation: String,
68        reason: String,
69        suggestion: Option<String>,
70    },
71    
72    /// Instance management error
73    #[error("Instance error: {operation} failed for instance {instance_id:?} - {reason}")]
74    Instance {
75        operation: String,
76        instance_id: Option<InstanceId>,
77        reason: String,
78    },
79    
80    /// Communication channel error
81    #[error("Communication error: {channel} channel failed - {reason}")]
82    Communication {
83        channel: String,
84        reason: String,
85        instance_id: Option<InstanceId>,
86    },
87    
88    /// Timeout error with context
89    #[error("Operation timed out after {duration:?}: {operation}")]
90    Timeout {
91        operation: String,
92        duration: Duration,
93        instance_id: Option<InstanceId>,
94    },
95
96    /// Function call error
97    #[error("Failed to call function '{function_name}': {reason}")]
98    FunctionCall {
99        function_name: String,
100        reason: String,
101    },
102
103    /// File system error with path context
104    #[error("Filesystem error: {operation} failed on '{path:?}' - {reason}")]
105    Filesystem {
106        operation: String,
107        path: PathBuf,
108        reason: String,
109    },
110
111    /// Network error with context
112    #[error("Network error: {operation} failed - {reason}")]
113    Network {
114        operation: String,
115        reason: String,
116        endpoint: Option<String>,
117    },
118
119    /// Serialization/deserialization error
120    #[error("Serialization error: {format} {operation} failed - {reason}")]
121    Serialization {
122        format: String, // json, messagepack, etc.
123        operation: String, // serialize, deserialize
124        reason: String,
125    },
126
127    /// Invalid input with validation details
128    #[error("Invalid input: {field} - {reason}")]
129    InvalidInput {
130        field: String,
131        reason: String,
132        suggestion: Option<String>,
133    },
134
135    /// Resource not found
136    #[error("Resource not found: {resource_type} '{identifier}' not found")]
137    NotFound {
138        resource_type: String, // instance, module, etc.
139        identifier: String,
140    },
141
142    /// Operation not supported
143    #[error("Unsupported operation: {operation} is not supported in this context")]
144    Unsupported {
145        operation: String,
146        context: String,
147        suggestion: Option<String>,
148    },
149
150    /// Generic error with a message
151    #[error("Generic error: {message}")]
152    Generic { message: String },
153    
154    /// Template rendering error
155    #[error("Template error: {message}")]
156    Template { message: String },
157    
158    /// Compilation error
159    #[error("Compilation error: {message}")]
160    Compilation { message: String },
161
162    /// Instance creation error
163    #[error("Instance creation error: {reason}")]
164    InstanceCreation {
165        reason: String,
166        instance_id: Option<InstanceId>,
167    },
168    
169    /// Wrapper generation error
170    #[error("Wrapper generation error: {reason}")]
171    WrapperGeneration {
172        reason: String,
173        wrapper_type: Option<String>,
174    },
175
176    /// Module load error
177    #[error("Module load error: {message}")]
178    ModuleLoad { message: String },
179
180    /// Runtime initialization error
181    #[error("Runtime initialization error: {message}")]
182    RuntimeInitialization { message: String },
183
184    /// IO error with context
185    #[error("IO error: {message}")]
186    IoError { message: String },
187
188    /// Capability error
189    #[error("Capability error: {message}")]
190    Capability { message: String },
191
192    /// Resource limit error
193    #[error("Resource limit error: {message}")]
194    ResourceLimit { message: String },
195
196    /// Unsupported operation error
197    #[error("Unsupported operation: {message}")]
198    UnsupportedOperation { message: String },
199
200    // Wrapped errors from external sources
201    #[error("IO error: {0}")]
202    Io(#[from] std::io::Error),
203
204    #[error("JSON error: {0}")]
205    Json(#[from] serde_json::Error),
206
207    #[error("MessagePack encode error: {0}")]
208    MessagePack(#[from] rmp_serde::encode::Error),
209
210    #[error("MessagePack decode error: {0}")]
211    MessagePackDecode(#[from] rmp_serde::decode::Error),
212
213    #[error(transparent)]
214    Other(#[from] anyhow::Error),
215}
216
217// Legacy aliases for backward compatibility
218impl SandboxError {
219    /// Create a security violation error
220    pub fn security_violation(violation: impl Into<String>, context: SecurityContext) -> Self {
221        Self::SecurityViolation {
222            violation: violation.into(),
223            instance_id: None,
224            context,
225        }
226    }
227
228    /// Create a resource exhausted error
229    pub fn resource_exhausted(
230        kind: ResourceKind,
231        used: u64,
232        limit: u64,
233        suggestion: Option<String>,
234    ) -> Self {
235        Self::ResourceExhausted {
236            kind,
237            limit,
238            used,
239            instance_id: None,
240            suggestion,
241        }
242    }
243
244    /// Create a configuration error with suggestion
245    pub fn config_error(message: impl Into<String>, suggestion: Option<String>) -> Self {
246        Self::Configuration {
247            message: message.into(),
248            suggestion,
249            field: None,
250        }
251    }
252
253    /// Create a module loading error
254    pub fn module_load_error(reason: impl Into<String>) -> Self {
255        Self::Module {
256            operation: "load".to_string(),
257            reason: reason.into(),
258            suggestion: Some("Check that the WASM file is valid and accessible".to_string()),
259        }
260    }
261
262    /// Create a function call error
263    pub fn function_call_error(function_name: impl Into<String>, reason: impl Into<String>) -> Self {
264        Self::FunctionCall {
265            function_name: function_name.into(),
266            reason: reason.into(),
267        }
268    }
269}
270
271impl Clone for SandboxError {
272    fn clone(&self) -> Self {
273        match self {
274            SandboxError::SecurityViolation { violation, instance_id, context } => {
275                SandboxError::SecurityViolation {
276                    violation: violation.clone(),
277                    instance_id: *instance_id,
278                    context: context.clone(),
279                }
280            }
281            SandboxError::ResourceExhausted { kind, limit, used, instance_id, suggestion } => {
282                SandboxError::ResourceExhausted {
283                    kind: kind.clone(),
284                    limit: *limit,
285                    used: *used,
286                    instance_id: *instance_id,
287                    suggestion: suggestion.clone(),
288                }
289            }
290            SandboxError::WasmRuntime { function, instance_id, message } => {
291                SandboxError::WasmRuntime {
292                    function: function.clone(),
293                    instance_id: *instance_id,
294                    message: message.clone(),
295                }
296            }
297            SandboxError::Configuration { message, suggestion, field } => {
298                SandboxError::Configuration {
299                    message: message.clone(),
300                    suggestion: suggestion.clone(),
301                    field: field.clone(),
302                }
303            }
304            SandboxError::Module { operation, reason, suggestion } => {
305                SandboxError::Module {
306                    operation: operation.clone(),
307                    reason: reason.clone(),
308                    suggestion: suggestion.clone(),
309                }
310            }
311            SandboxError::Instance { operation, instance_id, reason } => {
312                SandboxError::Instance {
313                    operation: operation.clone(),
314                    instance_id: *instance_id,
315                    reason: reason.clone(),
316                }
317            }
318            SandboxError::Communication { channel, reason, instance_id } => {
319                SandboxError::Communication {
320                    channel: channel.clone(),
321                    reason: reason.clone(),
322                    instance_id: *instance_id,
323                }
324            }
325            SandboxError::Timeout { operation, duration, instance_id } => {
326                SandboxError::Timeout {
327                    operation: operation.clone(),
328                    duration: *duration,
329                    instance_id: *instance_id,
330                }
331            }
332            SandboxError::FunctionCall { function_name, reason } => {
333                SandboxError::FunctionCall {
334                    function_name: function_name.clone(),
335                    reason: reason.clone(),
336                }
337            }
338            SandboxError::Filesystem { operation, path, reason } => {
339                SandboxError::Filesystem {
340                    operation: operation.clone(),
341                    path: path.clone(),
342                    reason: reason.clone(),
343                }
344            }
345            SandboxError::Network { operation, reason, endpoint } => {
346                SandboxError::Network {
347                    operation: operation.clone(),
348                    reason: reason.clone(),
349                    endpoint: endpoint.clone(),
350                }
351            }
352            SandboxError::Serialization { format, operation, reason } => {
353                SandboxError::Serialization {
354                    format: format.clone(),
355                    operation: operation.clone(),
356                    reason: reason.clone(),
357                }
358            }
359            SandboxError::InvalidInput { field, reason, suggestion } => {
360                SandboxError::InvalidInput {
361                    field: field.clone(),
362                    reason: reason.clone(),
363                    suggestion: suggestion.clone(),
364                }
365            }
366            SandboxError::NotFound { resource_type, identifier } => {
367                SandboxError::NotFound {
368                    resource_type: resource_type.clone(),
369                    identifier: identifier.clone(),
370                }
371            }
372            SandboxError::Unsupported { operation, context, suggestion } => {
373                SandboxError::Unsupported {
374                    operation: operation.clone(),
375                    context: context.clone(),
376                    suggestion: suggestion.clone(),
377                }
378            }
379            SandboxError::Generic { message } => {
380                SandboxError::Generic {
381                    message: message.clone(),
382                }
383            }
384            SandboxError::Template { message } => {
385                SandboxError::Template {
386                    message: message.clone(),
387                }
388            }
389            SandboxError::Compilation { message } => {
390                SandboxError::Compilation {
391                    message: message.clone(),
392                }
393            }
394            SandboxError::InstanceCreation { reason, instance_id } => {
395                SandboxError::InstanceCreation {
396                    reason: reason.clone(),
397                    instance_id: *instance_id,
398                }
399            }
400            SandboxError::WrapperGeneration { reason, wrapper_type } => {
401                SandboxError::WrapperGeneration {
402                    reason: reason.clone(),
403                    wrapper_type: wrapper_type.clone(),
404                }
405            }
406            SandboxError::ModuleLoad { message } => {
407                SandboxError::ModuleLoad {
408                    message: message.clone(),
409                }
410            }
411            SandboxError::RuntimeInitialization { message } => {
412                SandboxError::RuntimeInitialization {
413                    message: message.clone(),
414                }
415            }
416            SandboxError::IoError { message } => {
417                SandboxError::IoError {
418                    message: message.clone(),
419                }
420            }
421            SandboxError::Capability { message } => {
422                SandboxError::Capability {
423                    message: message.clone(),
424                }
425            }
426            SandboxError::ResourceLimit { message } => {
427                SandboxError::ResourceLimit {
428                    message: message.clone(),
429                }
430            }
431            SandboxError::UnsupportedOperation { message } => {
432                SandboxError::UnsupportedOperation {
433                    message: message.clone(),
434                }
435            }
436            // For wrapped errors that don't implement Clone, we create a new error with the string representation
437            SandboxError::Io(e) => SandboxError::Filesystem {
438                operation: "io".to_string(),
439                path: "unknown".into(),
440                reason: e.to_string(),
441            },
442            SandboxError::Json(e) => SandboxError::Serialization {
443                format: "json".to_string(),
444                operation: "parse".to_string(),
445                reason: e.to_string(),
446            },
447            SandboxError::MessagePack(e) => SandboxError::Serialization {
448                format: "messagepack".to_string(),
449                operation: "encode".to_string(),
450                reason: e.to_string(),
451            },
452            SandboxError::MessagePackDecode(e) => SandboxError::Serialization {
453                format: "messagepack".to_string(),
454                operation: "decode".to_string(),
455                reason: e.to_string(),
456            },
457            SandboxError::Other(e) => SandboxError::Configuration {
458                message: e.to_string(),
459                suggestion: None,
460                field: None,
461            },
462        }
463    }
464}
465
466/// Result type for the wasm-sandbox crate using the enhanced error type
467pub type Result<T> = std::result::Result<T, SandboxError>;
468
469/// Legacy error type alias for backward compatibility  
470pub type Error = SandboxError;