mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
committed by
GitHub
Unverified
parent
85203d8872
commit
3a4fa77ad7
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user