File size: 2,830 Bytes
1d3f990
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
//! Sandboxing configuration for M4

use serde::{Deserialize, Serialize};

/// M4 sandbox limits
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SandboxLimits {
    pub max_expansion_depth: usize,
    pub max_output_size: usize,
    pub max_call_stack: usize,
    pub allowed_builtins: Vec<String>,
    pub denied_builtins: Vec<String>,
}

impl Default for SandboxLimits {
    fn default() -> Self {
        Self {
            max_expansion_depth: 100,
            max_output_size: 1_048_576, // 1MB
            max_call_stack: 1000,
            allowed_builtins: vec![
                "define".into(),
                "defn".into(),
                "ifdef".into(),
                "ifelse".into(),
                "eval".into(),
                "len".into(),
                "substr".into(),
                "translit".into(),
                "include".into(),
            ],
            denied_builtins: vec![
                "esyscmd".into(),
                "sysval".into(),
                "maketemp".into(),
                "mkstemp".into(),
                "changequote".into(),
                "changecom".into(),
            ],
        }
    }
}

/// Sandbox configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SandboxConfig {
    pub limits: SandboxLimits,
    pub enable_include: bool,
    pub enable_eval: bool,
    pub timeout_ms: u64,
}

impl Default for SandboxConfig {
    fn default() -> Self {
        Self {
            limits: SandboxLimits::default(),
            enable_include: false,
            enable_eval: true,
            timeout_ms: 5000,
        }
    }
}

impl SandboxConfig {
    pub fn permissive() -> Self {
        let mut config = Self::default();
        config.enable_include = true;
        config.timeout_ms = 30000;
        config
    }

    pub fn strict() -> Self {
        let mut config = Self::default();
        config.limits.max_expansion_depth = 50;
        config.limits.max_output_size = 262_144; // 256KB
        config.timeout_ms = 1000;
        config
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sandbox_limits_default() {
        let limits = SandboxLimits::default();
        assert_eq!(limits.max_expansion_depth, 100);
        assert_eq!(limits.max_output_size, 1_048_576);
    }

    #[test]
    fn test_sandbox_config_permissive() {
        let config = SandboxConfig::permissive();
        assert!(config.enable_include);
        assert_eq!(config.timeout_ms, 30000);
    }

    #[test]
    fn test_sandbox_config_strict() {
        let config = SandboxConfig::strict();
        assert_eq!(config.limits.max_expansion_depth, 50);
        assert_eq!(config.limits.max_output_size, 262_144);
    }
}