mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat(app-server): enforce managed remote control disable (#27961)
## Why Managed deployments need a reliable deny gate for remote control. Persisted enablement and explicit startup requests currently remain able to start the transport, while the removed `features.remote_control` key is intentionally only a compatibility no-op. This adds a dedicated requirement that administrators can use to force remote control off without deleting the user's persisted preference. Removing the requirement and restarting restores the prior choice. ## What Changed - Added top-level `allow_remote_control` requirements parsing, sourced layer precedence, debug output, and `configRequirements/read` exposure as `allowRemoteControl`. - Added a typed transport policy captured from the startup requirements snapshot. Managed disable forces the initial state to disabled and prevents enrollment, refresh, connection, and persisted-preference mutation. - Rejected every `remoteControl/*` RPC before parameter deserialization with JSON-RPC `-32600` and `remote control is disabled by managed requirements`. - Preserved the existing disabled status notification and the previous behavior when the requirement is `true` or omitted. - Regenerated app-server protocol schemas and documented the new requirement. ## Verification - Confirmed all remote-control RPCs, including a malformed request, return the managed-policy error while the initial status notification remains `disabled`. - Confirmed explicit ephemeral startup and persisted enablement make no backend connection and leave the SQLite preference unchanged. - Confirmed `allow_remote_control = true` does not enable or block remote control and `configRequirements/read` returns `allowRemoteControl: false` for the deny policy. Related issue: N/A (managed-policy hardening).
This commit is contained in:
@@ -196,6 +196,17 @@ fn render_debug_config_lines(
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(allow_remote_control) = requirements_toml.allow_remote_control {
|
||||
requirement_lines.push(requirement_line(
|
||||
"allow_remote_control",
|
||||
allow_remote_control.to_string(),
|
||||
requirements
|
||||
.allow_remote_control
|
||||
.as_ref()
|
||||
.map(|sourced| &sourced.source),
|
||||
));
|
||||
}
|
||||
|
||||
if requirements_toml.guardian_policy_config.is_some() {
|
||||
requirement_lines.push(requirement_line(
|
||||
"guardian_policy_config",
|
||||
@@ -712,6 +723,10 @@ mod tests {
|
||||
/*value*/ false,
|
||||
RequirementSource::LegacyManagedConfigTomlFromMdm,
|
||||
)),
|
||||
allow_remote_control: Some(Sourced::new(
|
||||
/*value*/ false,
|
||||
RequirementSource::LegacyManagedConfigTomlFromMdm,
|
||||
)),
|
||||
feature_requirements: Some(Sourced::new(
|
||||
FeatureRequirementsToml {
|
||||
entries: BTreeMap::from([("guardian_approval".to_string(), true)]),
|
||||
@@ -753,6 +768,7 @@ mod tests {
|
||||
allowed_web_search_modes: Some(vec![WebSearchModeRequirement::Cached]),
|
||||
allow_managed_hooks_only: Some(true),
|
||||
allow_appshots: Some(false),
|
||||
allow_remote_control: Some(false),
|
||||
computer_use: None,
|
||||
windows: None,
|
||||
guardian_policy_config: Some("Use the managed guardian policy.".to_string()),
|
||||
@@ -795,6 +811,9 @@ mod tests {
|
||||
.expect("config layer stack");
|
||||
|
||||
let rendered = render_stack_to_text(&stack);
|
||||
#[cfg(not(windows))]
|
||||
insta::assert_snapshot!("debug_config_requirement_sources", rendered.as_str());
|
||||
|
||||
let requirements_source = (RequirementSource::LegacyManagedConfigTomlFromMdm).to_string();
|
||||
assert!(rendered.contains(&format!(
|
||||
"allowed_approval_policies: on-request (source: {requirements_source})"
|
||||
@@ -820,6 +839,9 @@ mod tests {
|
||||
assert!(rendered.contains(&format!(
|
||||
"allow_appshots: false (source: {requirements_source})"
|
||||
)));
|
||||
assert!(rendered.contains(&format!(
|
||||
"allow_remote_control: false (source: {requirements_source})"
|
||||
)));
|
||||
assert!(rendered.contains(&format!(
|
||||
"guardian_policy_config: configured (source: {requirements_source})"
|
||||
)));
|
||||
@@ -1112,6 +1134,7 @@ approval_policy = "never"
|
||||
allowed_web_search_modes: Some(Vec::new()),
|
||||
allow_managed_hooks_only: None,
|
||||
allow_appshots: None,
|
||||
allow_remote_control: None,
|
||||
computer_use: None,
|
||||
windows: None,
|
||||
guardian_policy_config: None,
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
---
|
||||
source: tui/src/debug_config.rs
|
||||
expression: rendered.as_str()
|
||||
---
|
||||
/debug-config
|
||||
|
||||
Config layer stack (lowest precedence first):
|
||||
1. user (/home/alice/.codex/config.toml) (enabled)
|
||||
|
||||
Requirements:
|
||||
- allowed_approval_policies: on-request (source: MDM managed_config.toml (legacy))
|
||||
- allowed_approvals_reviewers: auto_review (source: MDM managed_config.toml (legacy))
|
||||
- allowed_sandbox_modes: read-only (source: /etc/codex/requirements.toml)
|
||||
- allowed_web_search_modes: cached, disabled (source: MDM managed_config.toml (legacy))
|
||||
- allow_managed_hooks_only: true (source: MDM managed_config.toml (legacy))
|
||||
- allow_appshots: false (source: MDM managed_config.toml (legacy))
|
||||
- allow_remote_control: false (source: MDM managed_config.toml (legacy))
|
||||
- guardian_policy_config: configured (source: MDM managed_config.toml (legacy))
|
||||
- features: guardian_approval=true (source: MDM managed_config.toml (legacy))
|
||||
- mcp_servers: docs (source: MDM managed_config.toml (legacy))
|
||||
- enforce_residency: us (source: MDM managed_config.toml (legacy))
|
||||
- experimental_network: enabled=true, domains={example.com=allow} (source: MDM managed_config.toml (legacy))
|
||||
- permissions.filesystem.deny_read: /home/alice/.gitconfig (source: /etc/codex/requirements.toml)
|
||||
Reference in New Issue
Block a user