earl_protocol_bash/
builder.rs1use anyhow::{Result, bail};
2use serde_json::Value;
3
4use crate::schema::BashOperationTemplate;
5use crate::{PreparedBashScript, ResolvedBashSandbox};
6use earl_core::render::{TemplateRenderer, render_key_value_map};
7
8#[derive(Debug, Clone, Default)]
10pub struct GlobalBashLimits {
11 pub allow_network: bool,
12 pub max_time_ms: Option<u64>,
13 pub max_output_bytes: Option<u64>,
14 pub max_memory_bytes: Option<u64>,
15 pub max_cpu_time_ms: Option<u64>,
16}
17
18pub fn build_bash_request(
20 bash_op: &BashOperationTemplate,
21 context: &Value,
22 renderer: &dyn TemplateRenderer,
23 global_limits: &GlobalBashLimits,
24) -> Result<PreparedBashScript> {
25 let script = renderer.render_str(&bash_op.bash.script, context)?;
26 if script.trim().is_empty() {
27 bail!("operation.bash.script rendered empty");
28 }
29
30 let env = render_key_value_map(bash_op.bash.env.as_ref(), context, renderer)?;
31
32 let cwd = bash_op
33 .bash
34 .cwd
35 .as_ref()
36 .map(|value| renderer.render_str(value, context))
37 .transpose()?
38 .filter(|value| !value.trim().is_empty());
39
40 let template_sandbox = &bash_op.bash.sandbox;
43
44 let network = template_sandbox
45 .as_ref()
46 .and_then(|s| s.network)
47 .unwrap_or(false)
48 && global_limits.allow_network;
49
50 let writable_paths = template_sandbox
51 .as_ref()
52 .and_then(|s| s.writable_paths.clone())
53 .unwrap_or_default();
54
55 let max_time_ms = most_restrictive_option(
56 template_sandbox.as_ref().and_then(|s| s.max_time_ms),
57 global_limits.max_time_ms,
58 );
59
60 let max_output_bytes = most_restrictive_option(
61 template_sandbox.as_ref().and_then(|s| s.max_output_bytes),
62 global_limits.max_output_bytes,
63 )
64 .map(|v| v as usize);
65
66 let max_memory_bytes = most_restrictive_option(
67 template_sandbox.as_ref().and_then(|s| s.max_memory_bytes),
68 global_limits.max_memory_bytes,
69 )
70 .map(|v| v as usize);
71
72 let max_cpu_time_ms = most_restrictive_option(
73 template_sandbox.as_ref().and_then(|s| s.max_cpu_time_ms),
74 global_limits.max_cpu_time_ms,
75 );
76
77 Ok(PreparedBashScript {
78 script,
79 env,
80 cwd,
81 stdin: None,
82 sandbox: ResolvedBashSandbox {
83 network,
84 writable_paths,
85 max_time_ms,
86 max_output_bytes,
87 max_memory_bytes,
88 max_cpu_time_ms,
89 },
90 })
91}
92
93fn most_restrictive_option(a: Option<u64>, b: Option<u64>) -> Option<u64> {
95 match (a, b) {
96 (Some(a), Some(b)) => Some(a.min(b)),
97 (Some(a), None) => Some(a),
98 (None, Some(b)) => Some(b),
99 (None, None) => None,
100 }
101}