Skip to content

fix(agent-server): block request-body injection into agent constructor options - #1939

Open
ulivz wants to merge 2 commits into
mainfrom
fix/agent-server-unauthenticated-rce
Open

fix(agent-server): block request-body injection into agent constructor options#1939
ulivz wants to merge 2 commits into
mainfrom
fix/agent-server-unauthenticated-rce

Conversation

@ulivz

@ulivz ulivz commented Aug 2, 2026

Copy link
Copy Markdown
Member

Problem

POST /api/v1/sessions/create accepts an unfiltered agentOptions object that
is spread last into the Agent constructor parameters, overriding every
server-side default. An attacker supplying mcpServers entries with a command
field triggers StdioClientTransport, which spawns an arbitrary local process.
The same primitive allows sandbox downgrade (aioSandbox) and credential/model
endpoint redirection.

Three injection channels existed:

  1. agentOptions in the session-creation request body (both packages)
  2. runtimeSettings when no server-side transform is configured (spread into
    constructor options verbatim)
  3. sessionInfo.metadata.agentOptions in agent-server-next (persisted via
    POST /sessions/update, reapplied on every session init)

Amplifiers: the server binds all interfaces by default, has no authentication,
the CSRF token endpoint is unauthenticated and origin-unchecked, and CORS
explicitly allows requests with no Origin header.

Fix

Input boundary (primary):

  • sanitizeSessionAgentOptions: strict allowlist — only agentMode passes
    through. Prototype-pollution keys (__proto__, constructor, prototype)
    rejected. Values must be plain JSON data with bounded depth.
  • filterDeclaredRuntimeSettings: passes only keys declared in the server's
    runtime-settings JSON schema, and only when the value matches the declared
    primitive type (boolean/string/number). No schema configured = nothing
    passes. Object-typed values (like mcpServers) are structurally impossible.
  • Both filters applied at session creation (both packages) and at the
    runtime-settings update endpoint.

Default bind address (defense in depth):

Breaking change

The server previously listened on all interfaces (0.0.0.0). Deployments
relying on network access must now set server.host to 0.0.0.0 (or the
desired address) in their config, or pass --host 0.0.0.0 to the CLI.

Verification

  • New unit tests cover allowlist enforcement, prototype-pollution rejection,
    schema-based filtering, and host resolution defaults.
  • Additional route-level integration tests confirm the create-session endpoint
    rejects dangerous keys and returns them in the error response.

Not in this change

…r options

Session creation accepted an unfiltered `agentOptions` object that was spread
last into the Agent constructor, letting any caller override server configuration
including `mcpServers` (spawns local processes via StdioClientTransport),
`aioSandbox` (downgrades sandboxed deployments to host execution or redirects to
an attacker MCP server), and model endpoints. Combined with the server binding
all interfaces and having no authentication, this is unauthenticated remote
command execution.

The same injection existed through `runtimeSettings` when no server-side
`transform` was configured, and through `sessionInfo.metadata.agentOptions` in
agent-server-next.

Changes:
- Introduce `sanitizeSessionAgentOptions` (allowlist: only `agentMode`) and
  `filterDeclaredRuntimeSettings` (passes only schema-declared keys with matching
  primitive types) in `@tarko/shared-utils`.
- Apply both filters at session creation in agent-server and agent-server-next.
- Apply `filterDeclaredRuntimeSettings` at the runtime-settings update endpoint.
- Default server bind to 127.0.0.1 (`resolveServerHost`), with explicit opt-in
  via `server.host` config / `--host` CLI flag. Log the real bind address.
- Propagate `host` through agent-cli commands (start/serve/run) and interface
  types.

BREAKING CHANGE: The server now listens on 127.0.0.1 by default instead of all
interfaces. Deployments that rely on network access must set `server.host` to
`0.0.0.0` or the desired address in their config or pass `--host 0.0.0.0`.
@netlify

netlify Bot commented Aug 2, 2026

Copy link
Copy Markdown

Deploy Preview for agent-tars-docs ready!

Name Link
🔨 Latest commit 915d9b5
🔍 Latest deploy log https://app.netlify.com/projects/agent-tars-docs/deploys/6a72a49fbed4fe0008abd630
😎 Deploy Preview https://deploy-preview-1939--agent-tars-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Aug 2, 2026

Copy link
Copy Markdown

Deploy Preview for tarko ready!

Name Link
🔨 Latest commit 915d9b5
🔍 Latest deploy log https://app.netlify.com/projects/tarko/deploys/6a72a49e1ef924000888f183
😎 Deploy Preview https://deploy-preview-1939--tarko.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

continue;
}

filtered[key] = value;
…property write

- config-builder.test.ts inline snapshots updated to include `host: "127.0.0.1"`
  (the CI failure from the previous commit)
- Use `Object.create(null)` for the filtered-settings accumulator to silence
  CodeQL js/remote-property-injection (keys are already allowlisted through
  schema lookup + FORBIDDEN_KEYS guard, but the null-prototype object makes the
  guarantee structural)
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