Skip to content

fix: attribut network requests to the exact exec on linux - #29697

Merged
viyatb-oai merged 9 commits into
mainfrom
jif/network-exec-attribution
Jul 6, 2026
Merged

fix: attribut network requests to the exact exec on linux#29697
viyatb-oai merged 9 commits into
mainfrom
jif/network-exec-attribution

Conversation

@jif-oai

@jif-oai jif-oai commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Why

Managed-network commands within one Codex conversation share the same HTTP and SOCKS proxy ingress. When several exec calls run concurrently, the proxy sees the requested destination but cannot tell which exec opened the connection.

For example:

exec A: curl https://example.com/a ─┐
                                    ├─> conversation proxy ─> Guardian
exec B: curl https://example.com/b ─┘                        host: example.com
                                                               trigger: unknown
```. Three parallel network execs reached Guardian without their triggering call IDs or commands. Guardian denied the requests, but Codex could not safely associate those outcomes with the individual tool calls.

## What changes

Keep the shared proxy ingress and tag each connection at the existing trusted Linux bridge:

```text
exec A ─> existing Linux bridge ─> [token A][proxy bytes] ─┐
                                                           ├─> shared HTTP/SOCKS ingress
exec B ─> existing Linux bridge ─> [token B][proxy bytes] ─┘
                                                                    │
                                              token A ─> exec A ─────┤
                                              token B ─> exec B ─────┘

The complete path is:

active exec registration
        │
        ├─ registers its UUID as a short-lived attribution token
        ├─ passes the token to the Linux sandbox helper
        ├─ helper removes the token before launching the user command
        ├─ existing host bridge prepends the token to each proxy connection
        ├─ shared proxy consumes the bounded attribution frame
        └─ proxy attaches the matching execution-scoped state
                ├─ Guardian receives the exact call ID and command
                └─ a denial finishes/cancels the matching tool call

Dropping the active or deferred exec registration removes the token. Connections that were already accepted retain their resolved attribution; new connections using an expired token fail closed.

Before and after

Before, Guardian could receive only the network destination:

{
  "tool": "network_access",
  "host": "www.17track.net",
  "port": 443,
  "protocol": "https"
}

After, the same request includes the action that caused it:

{
  "tool": "network_access",
  "host": "www.17track.net",
  "port": 443,
  "protocol": "https",
  "trigger": {
    "callId": "exec-network-first",
    "command": ["/bin/sh", "-c", "curl https://www.17track.net"]
  }
}

Listener accounting

This PR does not create proxy listeners per exec.

Existing topology:
  one conversation -> one HTTP listener + optional one SOCKS listener

Discarded per-exec approach:
  one conversation -> existing listener pair
                   + up to one additional listener pair per active exec

This PR:
  one conversation -> existing listener pair only
                   + one small token-map entry per active exec

The Linux sandbox already creates a trusted routing bridge for each sandboxed command. This PR adds a short frame write to that bridge rather than introducing another listener, task, or proxy process.

The existing conversation-scoped listener pair remains. Making a single proxy service shared across multiple conversations would be a separate multi-tenant architecture change involving per-conversation policy, configuration, audit, and Guardian routing.

Keeping the implementation small

The attribution is bound once, when the TCP connection enters the proxy. The ingress installs an execution-scoped clone of the existing NetworkProxyState, so the established HTTP, SOCKS, MITM, policy, audit, and blocked-request paths continue using their existing state lookup.

This avoids plumbing a new request-context type through every protocol handler. Outside the two ingress wrappers, protocol-specific request handling is unchanged.

Security behavior

  • Tokens are generated from the existing random execution registration IDs.
  • The trusted Linux helper consumes and removes the token before executing user code.
  • Attribution frames have a fixed magic prefix, bounded token length, and bounded read timeout.
  • Unknown or expired tokens close the connection.
  • A token presented to a proxy for another environment closes the connection.
  • Existing unframed callers preserve the current conservative attribution behavior.

Platform scope

Exact bridge attribution is enabled on Linux. macOS and Windows retain their current shared-proxy behavior.

Test coverage

The concurrent end-to-end test starts two managed-network execs together and synchronizes them so both are active before either connects. It then inspects the two Guardian requests and compares the complete attribution pairs:

(exec-network-first,  exact first command)
(exec-network-second, exact second command)

Focused proxy coverage verifies the bounded frame and that a registered framed connection receives the matching execution and environment state.

Scope

This fixes the Linux network-to-exec attribution path and records a denial against the exact matching tool call. It intentionally does not change:

  • delivery of an entirely unattributed denial to the parent turn;
  • how parallel denials count toward the Guardian circuit breaker;
  • how the UI displays the rejection reason or completed-turn state.

Those remain separate concerns from attribution.

Relationship to #29456 and #29668

#29456 made the proxy environment and sandbox policy come from the same prepared network context. This PR adds the execution token to that prepared launch and consumes it at the shared ingress.

This follows #29668's shared-ingress framing direction, but completes the production registration, Linux bridge, core call mapping, denial mapping, and concurrent end-to-end path. It also keeps attribution in the existing per-connection proxy state instead of introducing request-context plumbing through every HTTP, SOCKS, and MITM handler. This PR is intended to supersede #29668 for the Linux attribution fix.

@jif-oai
jif-oai requested a review from a team as a code owner June 23, 2026 19:32

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e522994a0a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/core/src/tools/network_approval.rs Outdated
Comment thread codex-rs/core/src/tools/network_approval.rs Outdated
@jif-oai
jif-oai force-pushed the jif/network-exec-attribution branch from e522994 to 4a71b1a Compare June 23, 2026 20:13
@jif-oai jif-oai changed the title Attribute managed network requests to the exact exec Attribute Linux network requests to the exact exec Jun 23, 2026

@viyatb-oai viyatb-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue before landing:

  1. codex-rs/network-proxy/src/network_policy.rs:305 — The execution ID is added only to the cloned request sent to NetworkPolicyDecider. The policy audit event below is still built from the original request and has no execution field; baseline-allowed and baseline-denied requests do not call the decider at all. Those audit records therefore remain unattributed even though the accepted connection has execution-scoped state.

can we derive the attribution from state before policy branching, include it in the audit event, and cover both baseline allow and deny paths.

@viyatb-oai
viyatb-oai force-pushed the jif/network-exec-attribution branch from 4a71b1a to c4c3ea4 Compare June 23, 2026 20:56
ddraper-oai added a commit that referenced this pull request Jun 23, 2026
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@viyatb-oai
viyatb-oai force-pushed the jif/network-exec-attribution branch from ed58547 to ef9631d Compare June 23, 2026 21:26
@viyatb-oai viyatb-oai changed the title Attribute Linux network requests to the exact exec fix: attribut network requests to the exact exec on linux Jun 23, 2026
@viyatb-oai
viyatb-oai force-pushed the jif/network-exec-attribution branch from ef9631d to a4e5878 Compare June 23, 2026 21:43
Comment thread codex-rs/core/src/tools/network_approval.rs Outdated
Comment thread codex-rs/linux-sandbox/src/proxy_routing.rs
Comment thread codex-rs/linux-sandbox/src/proxy_routing.rs
Comment thread codex-rs/core/tests/suite/network_approval.rs
Comment thread codex-rs/network-proxy/src/network_policy.rs
Comment thread codex-rs/network-proxy/src/proxy/execution_scope.rs Outdated
jif-oai and others added 6 commits June 25, 2026 10:51
Co-authored-by: Codex noreply@openai.com
Co-authored-by: Codex noreply@openai.com
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
@viyatb-oai
viyatb-oai force-pushed the jif/network-exec-attribution branch from 6af16d9 to 10f7126 Compare June 25, 2026 17:56
Co-authored-by: Codex noreply@openai.com
@viyatb-oai
viyatb-oai merged commit 1013295 into main Jul 6, 2026
90 of 97 checks passed
@viyatb-oai
viyatb-oai deleted the jif/network-exec-attribution branch July 6, 2026 21:51
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants