mirror of
https://github.com/openai/codex.git
synced 2026-09-13 11:47:17 +00:00
Restrict fail-closed MDM requirements handling
This commit is contained in:
@@ -193,12 +193,10 @@ fn parse_managed_config_base64(encoded: &str) -> io::Result<Option<ManagedAdminC
|
||||
}
|
||||
|
||||
fn is_invalid_security_managed_config_entry(dropped_entry: &str) -> bool {
|
||||
let path = dropped_entry
|
||||
.split_once(':')
|
||||
.map_or(dropped_entry, |(path, _)| path)
|
||||
.trim();
|
||||
let top_level = path.split(['.', '[']).next().unwrap_or(path);
|
||||
matches!(top_level, "approval_policy" | "sandbox_mode")
|
||||
matches!(
|
||||
managed_entry_top_level_key(dropped_entry),
|
||||
"approval_policy" | "sandbox_mode"
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_managed_requirements_base64(encoded: &str) -> io::Result<ConfigRequirementsToml> {
|
||||
@@ -210,7 +208,44 @@ fn parse_managed_requirements_base64(encoded: &str) -> io::Result<ConfigRequirem
|
||||
)
|
||||
})?;
|
||||
|
||||
toml::from_str::<ConfigRequirementsToml>(&raw_toml).map_err(|err| {
|
||||
let parsed = toml::from_str::<TomlValue>(&raw_toml).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Error parsing managed requirements from {source}: {err}"),
|
||||
)
|
||||
})?;
|
||||
let TomlValue::Table(parsed) = parsed else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Error parsing managed requirements from {source}: root must be a table"),
|
||||
));
|
||||
};
|
||||
|
||||
let sanitized = sanitize_toml_value::<ConfigRequirementsToml>(TomlValue::Table(parsed))
|
||||
.map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Error parsing managed requirements from {source}: {err}"),
|
||||
)
|
||||
})?;
|
||||
if let Some(dropped_entry) = sanitized
|
||||
.dropped_entries
|
||||
.iter()
|
||||
.find(|entry| is_invalid_security_managed_requirements_entry(entry))
|
||||
{
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Error parsing managed requirements from {source}: {dropped_entry}"),
|
||||
));
|
||||
}
|
||||
for dropped_entry in &sanitized.dropped_entries {
|
||||
tracing::warn!(
|
||||
dropped_entry = %dropped_entry,
|
||||
"Ignoring invalid MDM managed requirements entry",
|
||||
);
|
||||
}
|
||||
|
||||
sanitized.value.try_into().map_err(|err: toml::de::Error| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Error parsing managed requirements from {source}: {err}"),
|
||||
@@ -218,6 +253,21 @@ fn parse_managed_requirements_base64(encoded: &str) -> io::Result<ConfigRequirem
|
||||
})
|
||||
}
|
||||
|
||||
fn is_invalid_security_managed_requirements_entry(dropped_entry: &str) -> bool {
|
||||
matches!(
|
||||
managed_entry_top_level_key(dropped_entry),
|
||||
"allowed_approval_policies" | "allowed_sandbox_modes" | "allowed_web_search_modes"
|
||||
)
|
||||
}
|
||||
|
||||
fn managed_entry_top_level_key(dropped_entry: &str) -> &str {
|
||||
let path = dropped_entry
|
||||
.split_once(':')
|
||||
.map_or(dropped_entry, |(path, _)| path)
|
||||
.trim();
|
||||
path.split(['.', '[']).next().unwrap_or(path)
|
||||
}
|
||||
|
||||
fn decode_managed_preferences_base64(encoded: &str) -> io::Result<String> {
|
||||
String::from_utf8(BASE64_STANDARD.decode(encoded.as_bytes()).map_err(|err| {
|
||||
tracing::error!("Failed to decode managed value as base64: {err}",);
|
||||
|
||||
@@ -578,6 +578,50 @@ async fn managed_preferences_invalid_requirements_fail_closed() -> anyhow::Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[tokio::test]
|
||||
async fn managed_preferences_ignore_invalid_non_security_requirements_entry() -> anyhow::Result<()>
|
||||
{
|
||||
use base64::Engine;
|
||||
|
||||
let tmp = tempdir()?;
|
||||
|
||||
let state = load_config_layers_state(
|
||||
tmp.path(),
|
||||
Some(AbsolutePathBuf::try_from(tmp.path())?),
|
||||
&[] as &[(String, TomlValue)],
|
||||
LoaderOverrides {
|
||||
managed_config_path: Some(tmp.path().join("managed_config.toml")),
|
||||
managed_preferences_base64: Some(String::new()),
|
||||
macos_managed_config_requirements_base64: Some(
|
||||
base64::prelude::BASE64_STANDARD.encode(
|
||||
r#"
|
||||
allowed_sandbox_modes = ["read-only"]
|
||||
|
||||
[features]
|
||||
personality = "bogus"
|
||||
"#
|
||||
.as_bytes(),
|
||||
),
|
||||
),
|
||||
},
|
||||
CloudRequirementsLoader::default(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
state.requirements_toml().allowed_sandbox_modes,
|
||||
Some(vec![crate::config_loader::SandboxModeRequirement::ReadOnly])
|
||||
);
|
||||
assert_eq!(state.requirements_toml().feature_requirements, None);
|
||||
assert_eq!(
|
||||
*state.requirements().sandbox_policy.get(),
|
||||
SandboxPolicy::new_read_only_policy()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[tokio::test]
|
||||
async fn managed_preferences_requirements_are_applied() -> anyhow::Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user