Make yolo skip managed-network tool enforcement (#18042)

## Summary

This makes `DangerFullAccess` / yolo tool execution fully opt out of
managed-network enforcement.

Previously, yolo turns could have `turn.network` stripped while tool
orchestration still derived `enforce_managed_network=true` from
`requirements.toml.network`. That created an inconsistent state where
the turn had no managed proxy attached, but tool execution still behaved
like managed networking was active.

This updates the tool orchestration and JS REPL paths to treat managed
networking as active only when the current turn actually has
`turn.network`.

## Behavior

- Yolo / `DangerFullAccess`: no managed proxy, no managed-network
enforcement.
- Guardian / workspace-write with managed proxy: managed-network
enforcement still applies.
- Avoids the half-state where yolo has no proxy but still gets
managed-network sandbox behavior.

## Tests

- `just fmt`
- `cargo test -p codex-core
danger_full_access_tool_attempts_do_not_enforce_managed_network --
--nocapture`
- `cargo test -p codex-core danger_full_access -- --nocapture`
- `just fix -p codex-core`

Co-authored-by: jgershen-oai <jgershen@openai.com>
This commit is contained in:
Won Park
2026-04-16 09:06:10 -07:00
committed by GitHub
Unverified
parent 85203d8872
commit 3a4fa77ad7
4 changed files with 125 additions and 24 deletions
+111
View File
@@ -765,6 +765,117 @@ async fn danger_full_access_turns_do_not_expose_managed_network_proxy() -> anyho
Ok(())
}
#[tokio::test]
async fn danger_full_access_tool_attempts_do_not_enforce_managed_network() -> anyhow::Result<()> {
#[derive(Default)]
struct ProbeToolRuntime {
enforce_managed_network: Vec<bool>,
}
impl crate::tools::sandboxing::Approvable<()> for ProbeToolRuntime {
type ApprovalKey = String;
fn approval_keys(&self, _req: &()) -> Vec<Self::ApprovalKey> {
vec!["probe".to_string()]
}
fn start_approval_async<'a>(
&'a mut self,
_req: &'a (),
_ctx: crate::tools::sandboxing::ApprovalCtx<'a>,
) -> futures::future::BoxFuture<'a, ReviewDecision> {
Box::pin(async { ReviewDecision::Approved })
}
}
impl crate::tools::sandboxing::Sandboxable for ProbeToolRuntime {
fn sandbox_preference(&self) -> codex_sandboxing::SandboxablePreference {
codex_sandboxing::SandboxablePreference::Auto
}
}
impl crate::tools::sandboxing::ToolRuntime<(), ()> for ProbeToolRuntime {
async fn run(
&mut self,
_req: &(),
attempt: &crate::tools::sandboxing::SandboxAttempt<'_>,
_ctx: &crate::tools::sandboxing::ToolCtx,
) -> Result<(), crate::tools::sandboxing::ToolError> {
self.enforce_managed_network
.push(attempt.enforce_managed_network);
Ok(())
}
}
let network_spec = crate::config::NetworkProxySpec::from_config_and_constraints(
NetworkProxyConfig::default(),
Some(NetworkConstraints {
enabled: Some(true),
..Default::default()
}),
&SandboxPolicy::DangerFullAccess,
)?;
let session = make_session_with_config(move |config| {
config.permissions.sandbox_policy =
codex_config::Constrained::allow_any(SandboxPolicy::DangerFullAccess);
config.permissions.network = Some(network_spec);
let layers = config
.config_layer_stack
.get_layers(
ConfigLayerStackOrdering::LowestPrecedenceFirst,
/*include_disabled*/ true,
)
.into_iter()
.cloned()
.collect();
let mut requirements = config.config_layer_stack.requirements().clone();
requirements.network = Some(Sourced::new(
NetworkConstraints {
enabled: Some(true),
..Default::default()
},
RequirementSource::CloudRequirements,
));
let mut requirements_toml = config.config_layer_stack.requirements_toml().clone();
requirements_toml.network = Some(crate::config_loader::NetworkRequirementsToml {
enabled: Some(true),
..Default::default()
});
config.config_layer_stack = ConfigLayerStack::new(layers, requirements, requirements_toml)
.expect("rebuild config layer stack with network requirements");
})
.await?;
let turn = session.new_default_turn().await;
assert!(turn.network.is_none());
let mut orchestrator = crate::tools::orchestrator::ToolOrchestrator::new();
let mut tool = ProbeToolRuntime::default();
let tool_ctx = crate::tools::sandboxing::ToolCtx {
session: Arc::clone(&session),
turn: Arc::clone(&turn),
call_id: "probe-call".to_string(),
tool_name: "probe".to_string(),
};
orchestrator
.run(
&mut tool,
&(),
&tool_ctx,
turn.as_ref(),
AskForApproval::Never,
)
.await
.expect("probe runtime should succeed");
assert_eq!(tool.enforce_managed_network, vec![false]);
Ok(())
}
#[tokio::test]
async fn workspace_write_turns_continue_to_expose_managed_network_proxy() -> anyhow::Result<()> {
let sandbox_policy = SandboxPolicy::new_workspace_write_policy();
+3 -8
View File
@@ -1033,18 +1033,13 @@ impl JsReplManager {
}
let sandbox = SandboxManager::new();
let has_managed_network_requirements = turn
.config
.config_layer_stack
.requirements_toml()
.network
.is_some();
let managed_network_active = turn.network.is_some();
let sandbox_type = sandbox.select_initial(
&turn.file_system_sandbox_policy,
turn.network_sandbox_policy,
SandboxablePreference::Auto,
turn.windows_sandbox_level,
has_managed_network_requirements,
managed_network_active,
);
let command = SandboxCommand {
program: node_path.into_os_string(),
@@ -1067,7 +1062,7 @@ impl JsReplManager {
file_system_policy: &turn.file_system_sandbox_policy,
network_policy: turn.network_sandbox_policy,
sandbox: sandbox_type,
enforce_managed_network: has_managed_network_requirements,
enforce_managed_network: managed_network_active,
network: None,
sandbox_policy_cwd: &turn.cwd,
codex_linux_sandbox_exe: turn.codex_linux_sandbox_exe.as_deref(),
+2 -2
View File
@@ -578,11 +578,11 @@ pub(crate) fn build_network_policy_decider(
pub(crate) async fn begin_network_approval(
session: &Session,
turn_id: &str,
has_managed_network_requirements: bool,
managed_network_active: bool,
spec: Option<NetworkApprovalSpec>,
) -> Option<ActiveNetworkApproval> {
let spec = spec?;
if !has_managed_network_requirements || spec.network.is_none() {
if !managed_network_active || spec.network.is_none() {
return None;
}
+9 -14
View File
@@ -55,7 +55,7 @@ impl ToolOrchestrator {
req: &Rq,
tool_ctx: &ToolCtx,
attempt: &SandboxAttempt<'_>,
has_managed_network_requirements: bool,
managed_network_active: bool,
) -> (Result<Out, ToolError>, Option<DeferredNetworkApproval>)
where
T: ToolRuntime<Rq, Out>,
@@ -63,7 +63,7 @@ impl ToolOrchestrator {
let network_approval = begin_network_approval(
&tool_ctx.session,
&tool_ctx.turn.sub_id,
has_managed_network_requirements,
managed_network_active,
tool.network_approval_spec(req, tool_ctx),
)
.await;
@@ -180,12 +180,7 @@ impl ToolOrchestrator {
}
// 2) First attempt under the selected sandbox.
let has_managed_network_requirements = turn_ctx
.config
.config_layer_stack
.requirements_toml()
.network
.is_some();
let managed_network_active = turn_ctx.network.is_some();
let initial_sandbox = match tool.sandbox_mode_for_first_attempt(req) {
SandboxOverride::BypassSandboxFirstAttempt => SandboxType::None,
SandboxOverride::NoOverride => self.sandbox.select_initial(
@@ -193,7 +188,7 @@ impl ToolOrchestrator {
turn_ctx.network_sandbox_policy,
tool.sandbox_preference(),
turn_ctx.windows_sandbox_level,
has_managed_network_requirements,
managed_network_active,
),
};
@@ -204,7 +199,7 @@ impl ToolOrchestrator {
policy: &turn_ctx.sandbox_policy,
file_system_policy: &turn_ctx.file_system_sandbox_policy,
network_policy: turn_ctx.network_sandbox_policy,
enforce_managed_network: has_managed_network_requirements,
enforce_managed_network: managed_network_active,
manager: &self.sandbox,
sandbox_cwd: &turn_ctx.cwd,
codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(),
@@ -221,7 +216,7 @@ impl ToolOrchestrator {
req,
tool_ctx,
&initial_attempt,
has_managed_network_requirements,
managed_network_active,
)
.await;
match first_result {
@@ -236,7 +231,7 @@ impl ToolOrchestrator {
output,
network_policy_decision,
}))) => {
let network_approval_context = if has_managed_network_requirements {
let network_approval_context = if managed_network_active {
network_policy_decision
.as_ref()
.and_then(network_approval_context_from_payload)
@@ -341,7 +336,7 @@ impl ToolOrchestrator {
policy: &turn_ctx.sandbox_policy,
file_system_policy: &turn_ctx.file_system_sandbox_policy,
network_policy: turn_ctx.network_sandbox_policy,
enforce_managed_network: has_managed_network_requirements,
enforce_managed_network: managed_network_active,
manager: &self.sandbox,
sandbox_cwd: &turn_ctx.cwd,
codex_linux_sandbox_exe: None,
@@ -359,7 +354,7 @@ impl ToolOrchestrator {
req,
tool_ctx,
&escalated_attempt,
has_managed_network_requirements,
managed_network_active,
)
.await;
retry_result.map(|output| OrchestratorRunResult {