Sanitize extension environment maps (#9884)
Signed-off-by: Jasper Hugo <jasper@spiral.xyz>
This commit is contained in:
@@ -58,7 +58,7 @@ pub enum ExtensionError {
|
||||
|
||||
pub type ExtensionResult<T> = Result<T, ExtensionError>;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Default, ToSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Default, ToSchema, PartialEq)]
|
||||
pub struct Envs {
|
||||
/// A map of environment variables to set, e.g. API_KEY -> some_secret, HOST -> host
|
||||
#[serde(default)]
|
||||
@@ -66,6 +66,16 @@ pub struct Envs {
|
||||
map: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Envs {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let map = HashMap::<String, String>::deserialize(deserializer)?;
|
||||
Ok(Self::new(map))
|
||||
}
|
||||
}
|
||||
|
||||
impl Envs {
|
||||
/// List of sensitive env vars that should not be overridden
|
||||
const DISALLOWED_KEYS: [&'static str; 31] = [
|
||||
@@ -665,6 +675,16 @@ available_tools: []
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn envs_deserialization_filters_disallowed_keys() {
|
||||
let envs: extension::Envs =
|
||||
serde_yaml::from_str("LD_PRELOAD: /tmp/injected.so\nSAFE_VAR: ok\n").unwrap();
|
||||
let map = envs.get_env();
|
||||
|
||||
assert!(!map.contains_key("LD_PRELOAD"));
|
||||
assert_eq!(map.get("SAFE_VAR"), Some(&"ok".to_string()));
|
||||
}
|
||||
|
||||
#[test_case(
|
||||
ExtensionConfig::Builtin {
|
||||
name: "developer".into(),
|
||||
|
||||
@@ -498,7 +498,7 @@ pub(crate) async fn merge_environments(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(all_envs)
|
||||
Ok(Envs::new(all_envs).get_env())
|
||||
}
|
||||
|
||||
/// Substitute environment variables in a string. Supports both ${VAR} and $VAR syntax.
|
||||
|
||||
@@ -260,4 +260,33 @@ mod tests {
|
||||
other => panic!("unexpected extension variant: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recipe_stdio_envs_deserialization_filters_disallowed_keys() {
|
||||
let wrapper: Wrapper = serde_json::from_value(json!({
|
||||
"extensions": [{
|
||||
"type": "stdio",
|
||||
"name": "test-stdio",
|
||||
"cmd": "echo",
|
||||
"args": [],
|
||||
"envs": {
|
||||
"LD_PRELOAD": "/tmp/injected.so",
|
||||
"SAFE_VAR": "ok"
|
||||
}
|
||||
}]
|
||||
}))
|
||||
.expect("failed to deserialize extensions");
|
||||
|
||||
let extensions = wrapper.extensions.expect("expected extensions");
|
||||
assert_eq!(extensions.len(), 1);
|
||||
|
||||
match &extensions[0] {
|
||||
ExtensionConfig::Stdio { envs, .. } => {
|
||||
let map = envs.get_env();
|
||||
assert!(!map.contains_key("LD_PRELOAD"));
|
||||
assert_eq!(map.get("SAFE_VAR"), Some(&"ok".to_string()));
|
||||
}
|
||||
other => panic!("unexpected extension variant: {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user