Skip to content

fix(security): pin ACP npx launchers to reviewed versions (closes #3357)#3399

Open
Sanjays2402 wants to merge 1 commit into
OpenHands:mainfrom
Sanjays2402:security/pin-acp-npx-versions
Open

fix(security): pin ACP npx launchers to reviewed versions (closes #3357)#3399
Sanjays2402 wants to merge 1 commit into
OpenHands:mainfrom
Sanjays2402:security/pin-acp-npx-versions

Conversation

@Sanjays2402
Copy link
Copy Markdown

  • A human has tested these changes.

Why

Closes #3357.

The three built-in ACP providers in openhands.sdk.settings.acp_providers launch with floating npx -y <pkg> commands. That means the package version is resolved from npm at agent-launch time — after commit review, after the [tool.uv] exclude-newer guardrail on the Python lock, and after the agent-server image build that already pins these very packages. Each provider also defaults to a permission-disabling session mode (bypassPermissions, full-access, yolo), so a poisoned or unexpectedly broken latest publish would be fetched and executed under a high-trust mode on the next launch.

Hardening, not a report of an active exploit — the goal is to bring the npm ACP launchers under the same version discipline the repo already applies elsewhere.

Summary

  • Pin default_command for claude-code, codex, and gemini-cli in openhands-sdk/openhands/sdk/settings/acp_providers.py to the versions the agent-server Dockerfile already installs (@agentclientprotocol/claude-agent-acp@0.30.0, @zed-industries/codex-acp@0.11.1, @google/gemini-cli@0.39.1). Pins are declared as module-level constants so future bumps are one-line edits.
  • Bump @google/gemini-cli from 0.38.00.39.1 in both the SDK pin and openhands-agent-server/openhands/agent_server/docker/Dockerfile, matching the patched release called out in the issue.
  • Fix the OpenAPI example in openhands-agent-server/openhands/agent_server/conversation_router_acp.py:53 — previously launched npx -y claude-agent-acp (unscoped name, 404 on npm today, registerable by anyone → dependency-confusion footgun). Now matches the scoped, pinned provider default.
  • Update the one strict-equality test in tests/sdk/test_settings.py that asserted the full default acp_command list. The two substring assertions in tests/sdk/settings/test_acp_providers.py already use in, so they remain valid against the pinned commands.

Issue Number

Closes #3357

How to Test

uv run pytest tests/sdk/settings/test_acp_providers.py tests/sdk/test_settings.py

Or, by inspection:

from openhands.sdk.settings.acp_providers import ACP_PROVIDERS
for k, v in ACP_PROVIDERS.items():
    print(k, v.default_command)
# ('npx', '-y', '@agentclientprotocol/claude-agent-acp@0.30.0')
# ('npx', '-y', '@zed-industries/codex-acp@0.11.1')
# ('npx', '-y', '@google/gemini-cli@0.39.1', '--acp')

The SDK-side pins and the Dockerfile-side pins for the bundled agent-server image are now identical, so build-time and runtime versions cannot diverge.

I was not able to run the test suite locally — uv sync failed on a transient disk-space issue in my dev environment, not the change. CI will exercise the targeted tests above; the changes are config-only and the diff is small and reviewable end-to-end.

Video/Screenshots

N/A — no UI change.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

  • default_command is a tuple of str; only its contents changed. Callers that build their own commands via ACPAgent(acp_command=[...]) are unaffected.
  • The Dockerfile pin bump (0.38.0 → 0.39.1) is the same delta the issue calls out as >= 0.39.1. Holding at 0.39.1 rather than chasing the current latest (0.43.0) keeps the SDK pin and Dockerfile pin tightly aligned with what's already been reviewed for the image.
  • Future version bumps need to touch both acp_providers.py (constants block) and the Dockerfile npm install -g line. The constants block has a comment pointing at the Dockerfile to make that contract obvious.

This PR was prepared by an AI agent (Cake, a Hermes agent running on @Sanjays2402's machine) on behalf of @Sanjays2402, who reviewed the diff before submission. The "A human has tested these changes" box is intentionally left unchecked because the test suite was not run locally end-to-end (disk-space issue in the dev environment); CI will verify.

Closes OpenHands#3357.

The three built-in ACP providers in openhands.sdk.settings.acp_providers
launch with floating npx commands (`npx -y <pkg>`), which resolve npm's
`latest` tag at agent-launch time — after commit review, after the uv
lock's `exclude-newer` guardrail, and after the agent-server image
build. Because each provider also defaults to a permission-disabling
session mode (`bypassPermissions`, `full-access`, `yolo`), a poisoned
or unexpectedly broken `latest` publish would be fetched and executed
under a high-trust mode on the next launch.

This change pins each launch command to the same versions the
agent-server Dockerfile already installs at image build time
(`@agentclientprotocol/claude-agent-acp@0.30.0`,
`@zed-industries/codex-acp@0.11.1`,
`@google/gemini-cli@0.39.1`), so build-time and runtime versions
cannot diverge. The gemini-cli pin is bumped from 0.38.0 → 0.39.1
in both the SDK and the Dockerfile, matching the patched release
called out in the issue.

The versions are declared as module-level constants
(`CLAUDE_AGENT_ACP_VERSION`, `CODEX_ACP_VERSION`,
`GEMINI_CLI_VERSION`) so future bumps are one-line edits and the
intent is documented inline.

The OpenAPI example in `conversation_router_acp.py` is also fixed:
it previously launched `npx -y claude-agent-acp` (unscoped, 404 on
npm, registerable by anyone — a dependency-confusion footgun). It now
matches the provider default.

No public API change: the `default_command` tuple shape is unchanged,
only its contents.
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.

Pin ACP npx -y launch commands instead of resolving floating npm latest at runtime

1 participant