Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codex-rs/core/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub(crate) use hook_additional_context::HookAdditionalContext;
pub(crate) use image_generation_instructions::ImageGenerationInstructions;
pub(crate) use model_switch_instructions::ModelSwitchInstructions;
pub(crate) use network_rule_saved::NetworkRuleSaved;
pub use permissions_instructions::PermissionsInstructionOptions;
pub use permissions_instructions::PermissionsInstructions;
pub(crate) use personality_spec_instructions::PersonalitySpecInstructions;
pub(crate) use plugin_instructions::PluginInstructions;
Expand Down
27 changes: 19 additions & 8 deletions codex-rs/core/src/context/permissions_instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const APPROVAL_POLICY_ON_REQUEST_RULE: &str =
const APPROVAL_POLICY_ON_REQUEST_RULE_REQUEST_PERMISSION: &str =
include_str!("prompts/permissions/approval_policy/on_request_rule_request_permission.md");
const AUTO_REVIEW_APPROVAL_SUFFIX: &str = "`approvals_reviewer` is `auto_review`: Sandbox escalations with require_escalated will be reviewed for compliance with the policy. If a rejection happens, you should proceed only with a materially safer alternative, or inform the user of the risk and send a final message to ask for approval.";
const NETWORK_PROXY: &str = include_str!("prompts/permissions/network_proxy.md");

const SANDBOX_MODE_DANGER_FULL_ACCESS: &str =
include_str!("prompts/permissions/sandbox_mode/danger_full_access.md");
Expand All @@ -50,6 +51,15 @@ struct PermissionsPromptConfig<'a> {
exec_policy: &'a Policy,
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
network_proxy_active: bool,
}

#[derive(Debug, Clone, Copy)]
/// Feature-dependent knobs used while rendering permissions instructions.
pub struct PermissionsInstructionOptions {
pub exec_permission_approvals_enabled: bool,
pub request_permissions_tool_enabled: bool,
pub network_proxy_active: bool,
}

#[derive(Debug, Clone, PartialEq)]
Expand All @@ -66,8 +76,7 @@ impl PermissionsInstructions {
approvals_reviewer: ApprovalsReviewer,
exec_policy: &Policy,
cwd: &Path,
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
options: PermissionsInstructionOptions,
) -> Self {
let (sandbox_mode, writable_roots) = sandbox_prompt_from_profile(permission_profile, cwd);

Expand All @@ -78,8 +87,9 @@ impl PermissionsInstructions {
approval_policy,
approvals_reviewer,
exec_policy,
exec_permission_approvals_enabled,
request_permissions_tool_enabled,
exec_permission_approvals_enabled: options.exec_permission_approvals_enabled,
request_permissions_tool_enabled: options.request_permissions_tool_enabled,
network_proxy_active: options.network_proxy_active,
},
writable_roots,
)
Expand All @@ -92,17 +102,15 @@ impl PermissionsInstructions {
approvals_reviewer: ApprovalsReviewer,
exec_policy: &Policy,
cwd: &Path,
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
options: PermissionsInstructionOptions,
) -> Self {
Self::from_permission_profile(
&PermissionProfile::from_legacy_sandbox_policy(sandbox_policy),
approval_policy,
approvals_reviewer,
exec_policy,
cwd,
exec_permission_approvals_enabled,
request_permissions_tool_enabled,
options,
)
}

Expand All @@ -114,6 +122,9 @@ impl PermissionsInstructions {
) -> Self {
let mut text = String::new();
append_section(&mut text, &sandbox_text(sandbox_mode, network_access));
if config.network_proxy_active {
append_section(&mut text, NETWORK_PROXY.trim_end());
}
append_section(
&mut text,
&approval_text(
Expand Down
62 changes: 58 additions & 4 deletions codex-rs/core/src/context/permissions_instructions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ fn builds_permissions_with_network_access_override() {
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
Expand Down Expand Up @@ -68,8 +69,11 @@ fn builds_permissions_from_policy() {
ApprovalsReviewer::User,
&Policy::empty(),
&PathBuf::from("/tmp"),
/*exec_permission_approvals_enabled*/ false,
/*request_permissions_tool_enabled*/ false,
PermissionsInstructionOptions {
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
);
let text = instructions.body();
assert!(text.contains("Network access is enabled."));
Expand Down Expand Up @@ -97,15 +101,59 @@ fn builds_permissions_from_profile() {
ApprovalsReviewer::User,
&Policy::empty(),
&cwd,
/*exec_permission_approvals_enabled*/ false,
/*request_permissions_tool_enabled*/ false,
PermissionsInstructionOptions {
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
);
let text = instructions.body();
assert!(text.contains("`sandbox_mode` is `workspace-write`"));
assert!(text.contains("Network access is enabled."));
assert!(text.contains(writable_root.to_string_lossy().as_ref()));
}

#[test]
fn includes_network_proxy_guidance_when_proxy_is_active() {
let instructions = PermissionsInstructions::from_permissions_with_network(
SandboxMode::WorkspaceWrite,
NetworkAccess::Restricted,
PermissionsPromptConfig {
approval_policy: AskForApproval::Never,
approvals_reviewer: ApprovalsReviewer::User,
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: true,
},
/*writable_roots*/ None,
);

let text = instructions.body();
assert!(text.contains("# Network Proxy"));
assert!(text.contains("blocked-by-allowlist"));
assert!(text.contains("proxy-specific headers or messages"));
}

#[test]
fn omits_network_proxy_guidance_when_proxy_is_inactive() {
let instructions = PermissionsInstructions::from_permissions_with_network(
SandboxMode::WorkspaceWrite,
NetworkAccess::Restricted,
PermissionsPromptConfig {
approval_policy: AskForApproval::Never,
approvals_reviewer: ApprovalsReviewer::User,
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);

assert!(!instructions.body().contains("# Network Proxy"));
}

#[test]
fn includes_request_rule_instructions_for_on_request() {
let mut exec_policy = Policy::empty();
Expand All @@ -121,6 +169,7 @@ fn includes_request_rule_instructions_for_on_request() {
exec_policy: &exec_policy,
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
Expand All @@ -142,6 +191,7 @@ fn includes_request_permissions_tool_instructions_for_unless_trusted_when_enable
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
Expand All @@ -162,6 +212,7 @@ fn includes_request_permissions_tool_instructions_for_on_failure_when_enabled()
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
Expand All @@ -182,6 +233,7 @@ fn includes_request_permission_rule_instructions_for_on_request_when_enabled() {
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: true,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
Expand All @@ -202,6 +254,7 @@ fn includes_request_permissions_tool_instructions_for_on_request_when_tool_is_en
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
Expand All @@ -222,6 +275,7 @@ fn on_request_includes_tool_guidance_alongside_inline_permission_guidance_when_b
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: true,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
Expand Down
12 changes: 12 additions & 0 deletions codex-rs/core/src/context/prompts/permissions/network_proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Network Proxy

A network proxy is active for model-initiated shell commands. Codex applies proxy environment variables automatically so outbound traffic is checked against the configured network policy.

Honor any `<network>` allow/deny entries in the environment context. Use normal network tools without clearing or overriding proxy-related environment variables. If a required host is not allowed, request additional network permissions instead of working around the proxy.

Interpret proxy failures precisely:
- `blocked-by-allowlist` means the host is not allowed by the current network policy.
- `blocked-by-denylist` means the host is explicitly denied by policy.
- A message about local/private network addresses means the sandbox is blocking local or private targets.

Do not infer a proxy denial from a generic network failure alone. Proxy-mediated requests can themselves time out or hang. Treat timeouts, hangs, DNS errors, TLS errors, and connection failures as evidence of proxy policy only when they also include proxy-specific headers or messages.
12 changes: 10 additions & 2 deletions codex-rs/core/src/context_manager/updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::context::CollaborationModeInstructions;
use crate::context::ContextualUserFragment;
use crate::context::EnvironmentContext;
use crate::context::ModelSwitchInstructions;
use crate::context::PermissionsInstructionOptions;
use crate::context::PermissionsInstructions;
use crate::context::PersonalitySpecInstructions;
use crate::context::RealtimeEndInstructions;
Expand Down Expand Up @@ -62,8 +63,15 @@ fn build_permissions_update_item(
next.config.approvals_reviewer,
exec_policy,
&next.cwd,
next.features.enabled(Feature::ExecPermissionApprovals),
next.features.enabled(Feature::RequestPermissionsTool),
PermissionsInstructionOptions {
exec_permission_approvals_enabled: next
.features
.enabled(Feature::ExecPermissionApprovals),
request_permissions_tool_enabled: next
.features
.enabled(Feature::RequestPermissionsTool),
network_proxy_active: next.network.is_some(),
},
)
.render(),
)
Expand Down
16 changes: 10 additions & 6 deletions codex-rs/core/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::context::AvailableSkillsInstructions;
use crate::context::CollaborationModeInstructions;
use crate::context::ContextualUserFragment;
use crate::context::NetworkRuleSaved;
use crate::context::PermissionsInstructionOptions;
use crate::context::PermissionsInstructions;
use crate::context::PersonalitySpecInstructions;
use crate::default_skill_metadata_budget;
Expand Down Expand Up @@ -2573,12 +2574,15 @@ impl Session {
turn_context.config.approvals_reviewer,
self.services.exec_policy.current().as_ref(),
&turn_context.cwd,
turn_context
.features
.enabled(Feature::ExecPermissionApprovals),
turn_context
.features
.enabled(Feature::RequestPermissionsTool),
PermissionsInstructionOptions {
exec_permission_approvals_enabled: turn_context
.features
.enabled(Feature::ExecPermissionApprovals),
request_permissions_tool_enabled: turn_context
.features
.enabled(Feature::RequestPermissionsTool),
network_proxy_active: turn_context.network.is_some(),
},
)
.render(),
);
Expand Down
8 changes: 6 additions & 2 deletions codex-rs/core/tests/suite/permissions_messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use codex_config::ConfigLayerStack;
use codex_core::ForkSnapshot;
use codex_core::config::Constrained;
use codex_core::context::ContextualUserFragment;
use codex_core::context::PermissionsInstructionOptions;
use codex_core::context::PermissionsInstructions;
use codex_core::load_exec_policy;
use codex_protocol::models::PermissionProfile;
Expand Down Expand Up @@ -581,8 +582,11 @@ async fn permissions_message_includes_writable_roots() -> Result<()> {
test.config.approvals_reviewer,
&exec_policy,
test.config.cwd.as_path(),
/*exec_permission_approvals_enabled*/ false,
/*request_permissions_tool_enabled*/ false,
PermissionsInstructionOptions {
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: test.session_configured.network_proxy.is_some(),
},
)
.render();
let expected_normalized = normalize_line_endings(&expected);
Expand Down
Loading