Skip to content

[security] fix(api): require explicit opt-in for agent shell tools#243

Merged
warren618 merged 2 commits into
HKUDS:mainfrom
Hinotoi-agent:fix/swarm-shell-tools-explicit-opt-in
Jun 15, 2026
Merged

[security] fix(api): require explicit opt-in for agent shell tools#243
warren618 merged 2 commits into
HKUDS:mainfrom
Hinotoi-agent:fix/swarm-shell-tools-explicit-opt-in

Conversation

@Hinotoi-agent

@Hinotoi-agent Hinotoi-agent commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens the API-started agent boundary so shell-capable tools are not enabled solely because a request appears to come from loopback.

  • Requires explicit operator opt-in via VIBE_TRADING_ENABLE_SHELL_TOOLS before API-created SWARM workers or session AgentLoop attempts can receive bash / background shell tools.
  • Adds regression coverage for DNS-rebinding-shaped requests to both /swarm/runs and /sessions/{session_id}/messages, proving shell tools are not enabled by default.
  • Preserves remote/LAN API-key behavior and the existing explicit shell-tool opt-in for trusted deployments.

Security issues covered

Issue Impact Severity
Loopback peer IP automatically enabled shell tools for API-started SWARM workers and session AgentLoop attempts A browser-to-localhost/DNS-rebinding path could combine loopback trust with agent routes that expose shell-capable tools, turning an unauthenticated local API request into a command-execution-capable workflow Critical

Before this PR

  • _shell_tools_enabled_for_request() returned true for any loopback client.
  • /swarm/runs passed that value into runtime.start_run(..., include_shell_tools=...).
  • /sessions/{session_id}/messages passed that value into svc.send_message(..., include_shell_tools=...).
  • Built-in SWARM presets and the normal AgentLoop registry can expose shell-capable tools such as bash when shell tools are enabled.
  • If a request reached the local API as a loopback peer, shell tools could be exposed without an explicit operator opt-in.

After this PR

  • API-started agents only receive shell tools when VIBE_TRADING_ENABLE_SHELL_TOOLS is explicitly enabled.
  • Loopback peer IP remains insufficient to grant host shell capability.
  • Regression tests cover loopback requests with attacker-controlled Host / Origin and verify include_shell_tools=False for both SWARM and session message flows.

Explicit operator choice

Secure default:

VIBE_TRADING_ENABLE_SHELL_TOOLS unset or false

Behavior:

  • API-created SWARM workers do not receive shell-capable tools, even for loopback clients.
  • Session AgentLoop attempts started through the API do not receive shell-capable tools, even for loopback clients.
  • Research workflows can still start; shell-capable tools are filtered out.

Explicit trusted opt-in:

VIBE_TRADING_ENABLE_SHELL_TOOLS=1

Behavior:

  • Operators who intentionally trust their deployment can still expose shell tools to API-started agents.

Why this matters

Shell-capable agent tools execute commands on the host as the API process user. Browser-origin requests can reach local services through localhost and DNS-rebinding patterns, so peer IP alone is not a reliable expression of operator intent.

A safer boundary is an explicit server-side operator opt-in for shell-capable tools.

How this differs from related PRs

  • [security] fix(api): reject rebound loopback hosts #242 hardens Host-header validation for the live-runner DNS-rebinding path.
  • This PR hardens shell-tool exposure itself. It prevents loopback status from granting bash / background shell capability to API-started SWARM workers or session AgentLoop attempts.
  • The fixes are complementary: Host validation blocks rebound admission paths, while this PR makes the dangerous shell-tool capability require explicit operator opt-in even when a request is local.

Attack flow

1. API_AUTH_KEY is configured.
2. Attacker hosts a web page at attacker.example:8899.
3. Browser resolves attacker.example to the attacker first, then rebinding points it at 127.0.0.1.
4. Browser sends same-origin JSON requests with Host/Origin attacker.example:8899 but loopback peer IP.
5. The local API treats the request as loopback.
6. The request reaches `/swarm/runs` or `/sessions/{session_id}/messages` without an Authorization header.
7. The route starts an agent workflow with `include_shell_tools=True` because the peer is loopback.
8. The agent runtime can receive shell-capable tools such as `bash`.

Affected code

Area File Notes
Shell-tool policy agent/api_server.py _shell_tools_enabled_for_request() inferred shell capability from loopback peer IP
Session agent route agent/api_server.py /sessions/{session_id}/messages passed the request-derived shell-tool flag into SessionService.send_message()
SWARM route agent/api_server.py /swarm/runs passed the request-derived shell-tool flag into SWARM runtime creation
Regression tests agent/tests/test_security_auth_api.py Covers default-deny shell tools, explicit opt-in, SWARM DNS-rebinding shape, and session DNS-rebinding shape

Root cause

  • Loopback peer IP was treated as sufficient intent to expose host shell tools.
  • Browser-to-localhost and DNS-rebinding threat models can make attacker-triggered requests appear as loopback.
  • Shell/process tools are a host capability and should require explicit server-side operator configuration, not request-locality inference.

CVSS assessment

  • Issue: DNS-rebinding-triggered API agent shell-tool exposure
  • Suggested severity: Critical
  • Suggested CVSS v3.1: 9.6
  • Suggested vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

Rationale: a remote attacker needs a victim to visit an attacker-controlled page, but no API key or Vibe-Trading credentials are required. The resulting workflow can expose host command-execution-capable tools in the local API process context.

Safe reproduction steps

Before the fix, a Docker proof using the real FastAPI app, real routes, real session/SWARM services, and a deterministic fake LLM showed:

[docker-proof:remote-control] create_status= 401
[docker-proof:remote-control] marker_exists= False
[docker-proof:dns-rebound-loopback] message_status= 200
[docker-proof:dns-rebound-loopback] marker_exists= True
[docker-proof:dns-rebound-loopback] marker_content= proof
[docker-proof] remote_blocked_but_rebound_session_rce_allowed= True

After this PR, the same session proof no longer exposes bash, so the marker is not created:

[docker-proof:remote-control] create_status= 401
[docker-proof:remote-control] marker_exists= False
[docker-proof:dns-rebound-loopback] create_status= 201
[docker-proof:dns-rebound-loopback] message_status= 200
[docker-proof:dns-rebound-loopback] fake_llm_calls= 1
[docker-proof:dns-rebound-loopback] marker_exists= False
[docker-proof] remote_blocked_but_rebound_session_rce_allowed= False

Expected vulnerable behavior

On vulnerable code, a rebound loopback request can cause an API-started agent workflow to receive shell tools without VIBE_TRADING_ENABLE_SHELL_TOOLS being set.

Changes in this PR

  • Changes _shell_tools_enabled_for_request() to rely only on VIBE_TRADING_ENABLE_SHELL_TOOLS.
  • Keeps explicit operator opt-in behavior intact.
  • Adds/updates tests proving:
    • loopback requests do not enable shell tools by default
    • remote requests do not enable shell tools by default
    • explicit opt-in enables shell tools
    • DNS-rebinding-shaped /swarm/runs requests get include_shell_tools=False
    • DNS-rebinding-shaped /sessions/{session_id}/messages requests get include_shell_tools=False

Files changed

File Change
agent/api_server.py Removes loopback peer IP as an automatic shell-tool grant
agent/tests/test_security_auth_api.py Adds regression coverage for the shell-tool policy and DNS-rebinding-shaped SWARM/session requests

Maintainer impact

  • Secure-by-default deployments no longer expose host shell tools through API-started agents based only on loopback locality.
  • Operators who intentionally need shell tools can enable VIBE_TRADING_ENABLE_SHELL_TOOLS=1.
  • Existing API auth behavior is otherwise unchanged.

Fix rationale

Host shell tools are a high-impact local capability. They should be gated by explicit server-side configuration so that browser-origin request tricks cannot turn request locality into host command execution capability.

Type of change

  • Security fix
  • Tests
  • Documentation
  • Refactor

Test plan

Local validation:

PYTHONPATH=agent python -m pytest agent/tests/test_security_auth_api.py -q
python -m py_compile agent/api_server.py agent/tests/test_security_auth_api.py
git diff --check

Result:

42 passed, 2 warnings in 0.25s

Docker validation:

docker run --rm -v "$PWD:/repo" -w /repo python:3.11-slim bash -lc '
apt-get update -qq && apt-get install -y -qq git >/dev/null &&
python -m pip install -q pytest fastapi httpx rich pydantic python-multipart sse-starlette pyyaml python-dotenv fastmcp &&
PYTHONPATH=agent python -m pytest agent/tests/test_security_auth_api.py -q &&
python -m py_compile agent/api_server.py agent/tests/test_security_auth_api.py
'

Result:

42 passed, 3 warnings in 0.22s

After-fix Docker proof for the session route:

[docker-proof:dns-rebound-loopback] marker_exists= False
[docker-proof] remote_blocked_but_rebound_session_rce_allowed= False

Disclosure notes

This PR is intentionally bounded to shell-tool exposure for API-started agent workflows. It does not claim to replace Host-header/DNS-rebinding admission hardening; it complements that boundary by requiring explicit operator opt-in before shell-capable tools are exposed to agents started through the API.

@Hinotoi-agent Hinotoi-agent changed the title [security] fix(api): require explicit opt-in for swarm shell tools [security] fix(api): require explicit opt-in for agent shell tools Jun 15, 2026
@warren618
warren618 merged commit 7eb8dea into HKUDS:main Jun 15, 2026
1 check passed
warren618 added a commit to Hinotoi-agent/Vibe-Trading that referenced this pull request Jun 15, 2026
…KUDS#242 host gate

HKUDS#242's _reject_untrusted_loopback_host middleware now 403s loopback requests
carrying an attacker-controlled Host before they reach the route. Update the
DNS-rebound tests from HKUDS#243 (/swarm/runs, /sessions/messages) and HKUDS#245
(/settings/llm) to assert the request is rejected at the host-gate layer (403)
and never reaches the shell-tool / settings-write decision. Full suite: 3217 passed.
warren618 pushed a commit that referenced this pull request Jun 15, 2026
Require a valid bearer token (not just loopback peer IP) for settings WRITE endpoints (/settings/llm, /settings/data-sources) when API_AUTH_KEY is configured, closing a DNS-rebinding / loopback-trust bypass while keeping zero-config local dev writes.

Also reconciles the DNS-rebound security tests across the #241-#245 sprint: now that #242's rebound-host middleware 403s loopback requests with an attacker-controlled Host before they reach the route, the end-to-end rebound tests for /swarm/runs and /sessions/messages (#243) and /settings/llm (#245) assert the request is rejected at the host-gate layer (403) and never reaches the shell-tool / settings-write decision. Full suite: 3217 passed.
@warren618

Copy link
Copy Markdown
Collaborator

Merged, thanks! Dropping request-locality from the shell-tool grant (explicit VIBE_TRADING_ENABLE_SHELL_TOOLS opt-in only) is the right call. One note: once #242's rebound-host middleware landed, your two end-to-end tests (test_dns_rebound_swarm_run_*, test_dns_rebound_session_message_*) failed on main — the middleware now 403s the attacker-Host request before it reaches /swarm/runs and /sessions/messages, so the include_shell_tools capture was never reached. I reconciled them in the #245 merge to assert the stronger combined behavior (403 at the host layer, route never invoked), which also extends host-gate coverage to those two endpoints. Your helper-level unit tests (test_shell_tools_*) still pin the core opt-in property. Full suite: 3217 passed.

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.

2 participants