Skip to content

fix(sandbox): resolve the no-web loopback endpoint per adapter - #940

Open
bingran-you wants to merge 1 commit into
mainfrom
bry/fix-no-web-loopback-base-url
Open

fix(sandbox): resolve the no-web loopback endpoint per adapter#940
bingran-you wants to merge 1 commit into
mainfrom
bry/fix-no-web-loopback-base-url

Conversation

@bingran-you

Copy link
Copy Markdown
Collaborator

Problem

Any task with sandbox.network_mode: no-network fails before the agent is
prompted on four of the five ACP adapters:

RuntimeError: No-web agent requires an HTTP loopback LLM_BASE_URL with a port

enforce_agent_egress_firewall reads the gateway URL from LLM_BASE_URL, but
_agent_env_for_route only sets that name for openhands:

adapter endpoint variable it is given passes the check today
openhands LLM_BASE_URL
opencode OPENAI_BASE_URL
codex-acp OPENAI_BASE_URL
claude-agent-acp ANTHROPIC_BASE_URL
pi-acp BENCHFLOW_PROVIDER_BASE_URL

The check is a precondition on a firewall that rejects all non-loopback egress
for the sandbox user — reasonable in intent, but it hardcoded one adapter's
spelling, so for the rest it can never be satisfied. The only workaround is
--sandbox-user null, which runs the agent as root and skips the uid-based
egress firewall this check exists to guard. So the practical effect is that
no-network tasks either don't run or run without the isolation they asked for.

Found while running a 29-task evaluation on opencode: 9 no-network tasks × 6
arms could not start.

Fix

Validate whichever endpoint variables are actually present, rather than one
hardcoded name.

_agent_env_for_route strips every provider endpoint variable
(_PROVIDER_ENDPOINT_ENV_NAMES) and re-points the agent at the loopback
gateway, so anything still set at firewall time must already be loopback.
Requiring every present endpoint to be loopback — not just one — is
strictly stronger than the previous single-variable check: a loopback route on
OPENAI_BASE_URL no longer excuses a routable ANTHROPIC_BASE_URL, which the
sandbox user is about to lose egress to regardless. An empty set still raises.

lockdown cannot import litellm_runtime (that module imports
benchflow.sandbox), so the name list is duplicated and pinned by
test_no_web_endpoint_names_cover_provider_endpoint_names. Adding a provider
endpoint variable there without adding it here now fails a test instead of
silently exempting it — the same class of bug this PR fixes.

Tests

  • Each adapter's own endpoint variable is accepted (parametrized over
    opencode / codex-acp / claude-agent-acp / pi-acp).
  • A non-loopback endpoint alongside a loopback one is rejected, naming the
    offender.
  • No endpoint set at all is rejected.
  • The drift guard above.

tests/test_sdk_lockdown.py 57/57; 332 passed across the lockdown/ACP suites.
ruff check and ruff format --check clean.

The one pre-existing test that asserted the old message text
(match="loopback LLM_BASE_URL") is updated to the new wording; its behavior
assertion is unchanged.

🤖 Generated with Claude Code

`enforce_agent_egress_firewall` read the gateway URL from `LLM_BASE_URL`
alone. Only openhands is wired that way. `_agent_env_for_route` gives
opencode and codex-acp `OPENAI_BASE_URL`, claude-agent-acp
`ANTHROPIC_BASE_URL`, and pi-acp `BENCHFLOW_PROVIDER_BASE_URL`, so for every
one of those adapters the precondition was unsatisfiable:

    RuntimeError: No-web agent requires an HTTP loopback LLM_BASE_URL with a port

raised before the agent was ever prompted. Any task declaring
`network_mode: no-network` was therefore impossible to run on four of five
ACP adapters unless the caller disabled the sandbox user entirely -- which
also skips the uid-based egress firewall this check exists to guard.

Validate whichever endpoint variables are actually set instead. All of them
are stripped and re-pointed at the loopback gateway by
`_agent_env_for_route`, so any that survives to firewall time must already be
loopback; requiring *every* present endpoint to be loopback (not just one) is
strictly stronger than the previous single-variable check, and the agent is
about to lose egress to a routable one either way.

lockdown cannot import `litellm_runtime` -- that module imports
`benchflow.sandbox` -- so the name list is duplicated and pinned by a test
asserting it covers `_PROVIDER_ENDPOINT_ENV_NAMES`. A new provider endpoint
variable now fails that test rather than silently escaping the precondition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@bingran-you
bingran-you deployed to pypi-internal-preview July 30, 2026 04:40 — with GitHub Actions Active

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

Copy link
Copy Markdown

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: dfa102b636

ℹ️ 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 on lines +486 to +488
"""Only openhands spells the gateway LLM_BASE_URL. Requiring that one
name made no-network tasks impossible for every other adapter: the
firewall raised before the agent was ever prompted."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Name the guarded fix in the regression-test docstring

This new test explicitly reproduces the four-adapter regression fixed by this commit, but its docstring does not identify the PR or commit it guards. Add the commit reference (for example, Guards the fix from commit 936edc7 ...) so the test follows the repository's regression-test convention.

AGENTS.md reference: AGENTS.md:L16-L17

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant