mirror of
https://github.com/openai/codex.git
synced 2026-11-09 20:50:33 +00:00
## What changed - Add `application.network` managed requirements with exact-domain allow/deny rules, default-enabled behavior, domain normalization, and normal managed TOML precedence. - Return the policy separately from agent network requirements through the experimental `configRequirements/read` API. The endpoint reports the policy but does not enforce it. - Export the new protocol types and schemas, and document the TOML format. ## Testing - Cover parsing, validation, normalization, layered precedence, and cloud requirement composition. - Verify app-server responses, defaults, separation from agent policy, and rejection of invalid domains. GitOrigin-RevId: 563eeed296d9748430b819797204a44a8db92f3b
252 lines
8.7 KiB
Rust
252 lines
8.7 KiB
Rust
//! Application requirements use managed precedence and deny unlisted destinations.
|
|
|
|
use super::*;
|
|
use crate::ConfigRequirements;
|
|
use crate::ConfigRequirementsToml;
|
|
use crate::ConfigRequirementsWithSources;
|
|
use crate::RequirementSource;
|
|
use crate::RequirementsLayerEntry;
|
|
use crate::Sourced;
|
|
use crate::compose_requirements_for_hostname;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
const INSTALLATION: &str = r#"
|
|
[application.network.domains]
|
|
"shared.example.com" = "allow"
|
|
"installed.example.com" = "allow"
|
|
"blocked.example.com" = "deny"
|
|
"#;
|
|
const WORKSPACE: &str = r#"
|
|
[application.network.domains]
|
|
"shared.example.com" = "allow"
|
|
"workspace.example.com" = "allow"
|
|
"blocked.example.com" = "allow"
|
|
"#;
|
|
const MERGED: &str = r#"
|
|
[application.network.domains]
|
|
"shared.example.com" = "allow"
|
|
"installed.example.com" = "allow"
|
|
"workspace.example.com" = "allow"
|
|
"blocked.example.com" = "allow"
|
|
"#;
|
|
|
|
fn parse(contents: &str) -> ConfigRequirementsToml {
|
|
toml::from_str(contents).expect("valid application requirements")
|
|
}
|
|
|
|
fn cloud_source() -> RequirementSource {
|
|
RequirementSource::EnterpriseManaged {
|
|
id: "workspace".to_string(),
|
|
name: "Workspace policy".to_string(),
|
|
}
|
|
}
|
|
|
|
fn managed_source() -> RequirementSource {
|
|
RequirementSource::MdmManagedPreferences {
|
|
domain: "com.openai.codex".to_string(),
|
|
key: "requirements".to_string(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn application_network_defaults_to_enabled_and_denies_unlisted_domains() {
|
|
assert!(parse("[application]").is_empty());
|
|
let requirements = parse("[application.network]");
|
|
assert!(!requirements.is_empty());
|
|
assert_eq!(
|
|
requirements.application,
|
|
Some(ApplicationRequirementsToml {
|
|
network: Some(ApplicationNetworkRequirementsToml {
|
|
enabled: true,
|
|
domains: BTreeMap::new(),
|
|
}),
|
|
}),
|
|
);
|
|
assert_eq!(
|
|
parse("[application.network.domains]\n\"EXAMPLE.com.\" = \"deny\""),
|
|
parse("[application.network.domains]\n\"example.com\" = \"deny\""),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn application_network_is_preserved_from_each_managed_source() {
|
|
for source in [
|
|
RequirementSource::SystemRequirementsToml {
|
|
file: crate::AbsolutePathBuf::try_from(std::env::temp_dir().join("requirements.toml"))
|
|
.expect("absolute requirements path"),
|
|
},
|
|
managed_source(),
|
|
cloud_source(),
|
|
] {
|
|
let requirements = compose_requirements_for_hostname(
|
|
[RequirementsLayerEntry::from_toml(
|
|
source.clone(),
|
|
INSTALLATION,
|
|
)],
|
|
/*hostname*/ None,
|
|
)
|
|
.expect("compose application requirements")
|
|
.expect("application requirements are not empty");
|
|
assert_eq!(
|
|
requirements,
|
|
ConfigRequirementsWithSources {
|
|
application: Some(Sourced::new(
|
|
parse(INSTALLATION).application.expect("application"),
|
|
source,
|
|
)),
|
|
..Default::default()
|
|
},
|
|
);
|
|
let normalized: ConfigRequirements =
|
|
requirements.clone().try_into().expect("runtime policy");
|
|
assert_eq!(normalized.application, requirements.application);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn application_network_uses_regular_toml_precedence() {
|
|
let installed_wins = MERGED.replace(
|
|
"\"blocked.example.com\" = \"allow\"",
|
|
"\"blocked.example.com\" = \"deny\"",
|
|
);
|
|
let disabled_installation = format!("[application.network]\nenabled = false\n{INSTALLATION}");
|
|
for (low, high, expected) in [
|
|
(INSTALLATION, WORKSPACE, MERGED),
|
|
(WORKSPACE, INSTALLATION, installed_wins.as_str()),
|
|
(
|
|
INSTALLATION,
|
|
"[application.network]\nenabled = false",
|
|
disabled_installation.as_str(),
|
|
),
|
|
(
|
|
disabled_installation.as_str(),
|
|
"[application.network]\nenabled = true",
|
|
INSTALLATION,
|
|
),
|
|
(
|
|
"[application.network]\nenabled = false",
|
|
INSTALLATION,
|
|
disabled_installation.as_str(),
|
|
),
|
|
(INSTALLATION, "[application.network]", INSTALLATION),
|
|
(INSTALLATION, "", INSTALLATION),
|
|
("[application]", INSTALLATION, INSTALLATION),
|
|
("[application.network]", WORKSPACE, WORKSPACE),
|
|
(
|
|
"[application.network.domains]\n'example.com' = 'allow'",
|
|
"[application.network.domains]\n'EXAMPLE.COM.' = 'deny'",
|
|
"[application.network.domains]\n'example.com' = 'deny'",
|
|
),
|
|
(
|
|
"[application.network.domains]\n'EXAMPLE.COM.' = 'deny'",
|
|
"[application.network.domains]\n'example.com' = 'allow'",
|
|
"[application.network.domains]\n'example.com' = 'allow'",
|
|
),
|
|
] {
|
|
let composed = compose_requirements_for_hostname(
|
|
[
|
|
RequirementsLayerEntry::from_toml(managed_source(), low),
|
|
RequirementsLayerEntry::from_toml(cloud_source(), high),
|
|
],
|
|
/*hostname*/ None,
|
|
)
|
|
.expect("compose application requirements")
|
|
.expect("configured requirements");
|
|
assert_eq!(
|
|
composed.into_toml(),
|
|
parse(expected),
|
|
"low: {low}, high: {high}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn legacy_application_merge_uses_whole_table_precedence() {
|
|
for (high, low) in [
|
|
(INSTALLATION, WORKSPACE),
|
|
(WORKSPACE, INSTALLATION),
|
|
("[application.network]\nenabled = false", INSTALLATION),
|
|
("[application]", INSTALLATION),
|
|
] {
|
|
let mut composed = ConfigRequirementsWithSources::default();
|
|
composed.merge_unset_fields(managed_source(), parse(high));
|
|
composed.merge_unset_fields(cloud_source(), parse(low));
|
|
assert_eq!(
|
|
composed,
|
|
ConfigRequirementsWithSources {
|
|
application: Some(Sourced::new(
|
|
parse(high).application.expect("application"),
|
|
managed_source(),
|
|
)),
|
|
..Default::default()
|
|
},
|
|
);
|
|
}
|
|
let mut composed = ConfigRequirementsWithSources::default();
|
|
composed.merge_unset_fields(managed_source(), parse(""));
|
|
composed.merge_unset_fields(cloud_source(), parse(INSTALLATION));
|
|
assert_eq!(composed.into_toml(), parse(INSTALLATION));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_application_policy_is_rejected_before_layer_overrides() {
|
|
for invalid in [
|
|
"[application.network]\nenabled = 'true'",
|
|
"[application.network]\nallowed_domains = ['example.com']",
|
|
"[application.network.domains]\n'example.com' = 'prompt'",
|
|
"[application.network.domains]\n'*.example.com' = 'allow'",
|
|
"[application.network.domains]\n'https://example.com' = 'allow'",
|
|
"[application.network.domains]\n'example.com:443' = 'allow'",
|
|
"[application.network.domains]\n'example..com' = 'allow'",
|
|
"[application.network.domains]\n' example.com' = 'allow'",
|
|
"[application.network.domains]\n'EXAMPLE.com' = 'allow'\n'example.com.' = 'deny'",
|
|
] {
|
|
assert!(
|
|
toml::from_str::<ConfigRequirementsToml>(invalid).is_err(),
|
|
"{invalid}"
|
|
);
|
|
assert!(
|
|
compose_requirements_for_hostname(
|
|
[
|
|
RequirementsLayerEntry::from_toml(cloud_source(), invalid),
|
|
RequirementsLayerEntry::from_toml(managed_source(), INSTALLATION),
|
|
],
|
|
/*hostname*/ None,
|
|
)
|
|
.is_err(),
|
|
"{invalid}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn cloud_bundle_application_network_uses_managed_precedence() {
|
|
let base_dir =
|
|
crate::AbsolutePathBuf::try_from(std::env::temp_dir()).expect("absolute base directory");
|
|
let bundle = crate::CloudConfigBundle {
|
|
requirements_toml: crate::CloudRequirementsTomlBundle {
|
|
enterprise_managed: vec![crate::CloudRequirementsFragment {
|
|
id: "workspace".to_string(),
|
|
name: "Workspace".to_string(),
|
|
contents: WORKSPACE.to_string(),
|
|
}],
|
|
},
|
|
..Default::default()
|
|
};
|
|
let mut layers = crate::CloudConfigBundleLayers::from_bundle(bundle, &base_dir)
|
|
.expect("load cloud bundle")
|
|
.enterprise_managed_requirements;
|
|
layers.push(RequirementsLayerEntry::from_toml(
|
|
managed_source(),
|
|
INSTALLATION,
|
|
));
|
|
let composed = compose_requirements_for_hostname(layers, /*hostname*/ None)
|
|
.expect("compose cloud and installation requirements")
|
|
.expect("requirements");
|
|
let expected = MERGED.replace(
|
|
"\"blocked.example.com\" = \"allow\"",
|
|
"\"blocked.example.com\" = \"deny\"",
|
|
);
|
|
assert_eq!(composed.into_toml(), parse(&expected));
|
|
}
|