Oct 10 21:31Z agent-harness wave: agent write-gates defeat themselves, "read-only" SQL gates that aren't, and sandboxes that inherit your env¶
Wave in one line: A dozen advisories published at 2026-10-10T21:31Z landed on the same surface — AI-agent harnesses (phi, open-multi-agent, mini-swe-agent, Tabularis, slide-maker), an agent-adjacent dashboard (9router), and dev/observability tooling (Helicone, OpenLIT, Floci, Cohere SDK, Mealie, InnoShop) — and nearly every leg is a directly replayable audit probe against the agent/LLM tooling already deployed on target workstations.
Signal: The notable cluster: GHSA-mhj6-8rvv-3grj (phi, CVE-2026-108599), GHSA-x8rp-9mrq-5584 (phi sub-agent, CVE-2026-108595), GHSA-h42w-g3ch-rjjm (open-multi-agent, CVE-2026-108600), GHSA-jj9h-m6r9-g37h (Tabularis, CVE-2026-108604), GHSA-p449-r4wm-3hpm (mini-swe-agent, CVE-2026-108592), GHSA-chcq-pfxc-99ww (9router, CVE-2026-108593), GHSA-c3c4-9g85-pfvc (Floci, CVE-2026-108598 critical), GHSA-xc29-5cqv-36w6 (slide-maker, CVE-2026-108603), GHSA-ch5j-5g34-4wwh (Helicone, CVE-2026-108602), GHSA-fx43-jvpp-9x44 (OpenLIT, CVE-2026-108596), GHSA-rg44-2cq2-qx25 (Cohere SDK, CVE-2026-108597), GHSA-rr7r-p99j-w4xq (Mealie, CVE-2026-108594), GHSA-93ww-wv4j-3wjq (InnoShop, CVE-2026-108591).
Advisory table¶
| GHSA | CVE | Product | Boundary |
|---|---|---|---|
| mhj6 | CVE-2026-108599 | phi 0.1.1–0.28.4 | workspace_only_writes gate is lexical-only (string path check): a repo can ship committed symlinks pointing outside the workspace, and prompt injection steers the write tool through them — attacker-influenced content lands in external files, no approval. |
| x8rp | CVE-2026-108595 | phi 0.3.0–0.28.4 | agent_spawn takes an unchecked workdir: prompt-injected content makes the agent spawn a sub-agent rooted elsewhere, escaping both workspace_only_writes and readonly mode. Child policy ≠ parent policy. |
| h42w | CVE-2026-108600 | open-multi-agent 1.5.0–1.21.2 | file_write sandbox defeated by a dangling symlink planted in the workspace: target doesn't exist yet so existence-based checks pass, then the write materializes content at whatever absolute path the link names. |
| jj9h | CVE-2026-108604 | Tabularis ≤0.27.0 | MCP run_query "read-only" gate allows side-effecting SELECTs: SELECT setval(...), nextval(...), or query_to_xml('DELETE ...') mutate data through a statement that parses as a read, skipping approval prompts. |
| p449 | CVE-2026-108592 | mini-swe-agent 1.10.0–2.4.6 | Bubblewrap sandbox omits --clearenv, so sandboxed commands inherit the host environment; prompt injection reads API keys from env and exfiltrates over the shared network namespace. |
| chcq | CVE-2026-108593 | 9router 0.4.1–0.5.99 | POST /api/cli-tools/hermes-settings writes user-supplied keys/values into the agent's config.yaml with no quoting/escaping: a baseUrl value containing quotes+newlines injects hooks_auto_accept and a hooks.post_llm_call shell command the agent executes after every LLM call without approval. Dashboard-user → agent-exec primitive. |
| c3c4 | CVE-2026-108598 | Floci 1.1.0–<2.2.0 (critical) | Local API-gateway emulator accepts MOCK-integration Velocity templates with no restriction; $util reflection reaches Runtime/ProcessBuilder → unauthenticated OS command execution in the JVM. Emulators replicate prod config surfaces without prod authn. |
| xc29 | CVE-2026-108603 | slide-maker ≤5.8.0 | Image-generation pipeline trusts manifest-supplied filenames: deck source material influences the prompt manifest to contain ../ or symlinked names → directory creation + file overwrite outside the output dir. Model-adjacent Zip-Slip-by-filename. |
| ch5j | CVE-2026-108602 | Helicone ≤v2025.08.21-1 | Jawn webhook sender validates hostname at creation time only: public hostnames that later resolve/DNS-rebind to loopback/internal addresses make the platform send blind POSTs to internal HTTPS services. |
| fx43 | CVE-2026-108596 | OpenLIT 2.1.0 | Trace read API authorizes on a client-supplied x-openlit-project-id header: any authenticated user who knows a victim project id + database config id reads their LLM prompts and completions. Tenant id in a header, socket identity ignored. |
| rg44 | CVE-2026-108597 | Cohere Python SDK 5.11.0–7.2.0 | _s3_models_dir_to_tarfile → tarfile.extractall with no member validation: anyone who can write model archives to the victim's S3 prefix gets arbitrary file write via absolute/../ tar members. Model-download path = archive-extraction sink. |
| rr7r | CVE-2026-108594 | Mealie 3.26.0–3.28.0 | OIDC avatar fetch allowlists the IdP hostname but ignores the port: a user-controlled picture URL hits arbitrary ports on the provider's internal address on every login. Allowlist-checks-hostname-only fingerprint. |
| 93ww | CVE-2026-108591 | InnoShop 0.9.2 | AI Core MCP file_upload tool passes source to file_get_contents() — file:// and php:// wrappers accepted; the tool then stores the file contents on the public media disk (.env with APP_KEY + DB creds). A legit permission (files_create) + wrapper = public disclosure. |
Why this is durable operator value¶
- Agent write-gates are lexical unless proven otherwise — symlink and spawn are the two universal probes. Three independent products (phi×2, open-multi-agent) fell to the same two shapes in one wave: (a) plant a symlink (live or dangling) inside the workspace and let prompt injection drive a write through it, (b) ask the harness to spawn a child/sub-agent rooted outside the workspace and write there. Dangling vs existing target matters: existence-based validators accept dangling links, then the write itself materializes the path. The audit kit for any agent framework with a workspace/policy gate: committed-symlink repo canary, dangling-symlink canary,
agent_spawn-style API with attacker-suppliedworkdir, each paired with a marker file outside the root. This is the same repository-trust genealogy as the mise.tool-versionsand Entire CLI checkpoint-restore pages — validate-at-check ≠ validate-at-write. - "Read-only" SQL safety gates must be tested against the language's side-effecting read grammar, not just DDL/DML keywords. Tabularis allowed
setval/nextval/query_to_xmlinsideSELECT— the gate parsed the statement as a read while PostgreSQL executed writes through functions. Sweep every SQL-safety wrapper (LLM SQL tools, MCP DB servers, reporting "read-only" modes) against the full function surface: sequence mutators,query_to_xml/dblink/dblink_exec,pg_sleep,lo_import/lo_export,COPY ... TO PROGRAM, andSELECT ... INTO. A keyword denylist is a fingerprint, not a gate. - Sandbox env/network inheritance is a five-minute post-foothold check. mini-swe-agent's missing
--clearenvmeans the model-supplied command sees every host secret. Operator probe for any agent-in-a-box (bwrap, container, VM-lite harness you control scope over): runenv/ read/proc/self/environinside the sandbox and diff against the launcher's environment; check whether the sandbox shares the host network namespace (the exfil channel in the advisory). Record which*_API_KEY/*_TOKENnames exist — presence, not value, is the finding. - Config-injection through "settings" endpoints that serialize YAML by string interpolation. 9router turned an authenticated dashboard write into agent-exec RCE: unquoted YAML values + attacker-controlled newlines = new top-level keys, and the injected key (
hooks.post_llm_call+hooks_auto_accept) is itself an execution primitive. Audit rule for every product with a settings→YAML/TOML round-trip: submit values containing\n,", and:and diff the on-disk config file against the submitted fields; then grep the consumer for settings whose names imply execution (hooks, commands, plugins, auto_accept). Second lesson: the pivot target here was an agent config — dashboards that manage agent frameworks are agent-compromise surfaces, no second bug needed. - Emulators/local-dev replicas carry the prod feature (template engines) without the prod trust model. Floci's unrestricted Velocity MOCK templates is the localstack-class pattern: scan emulator ports in dev/CI environments (10.10.10.x-style tool sprawl) and test template-expression injection (
$util.runtime, SSTI classics) into any mock/template/transform config surface. Same family as the Template-Engine-as-a-Service class, but reachable unauthenticated because nobody threat-models the emulator. - Model/artifact pipelines are archive-and-filename sinks. The Cohere SDK tar-slip (S3-prefix write → host file write) and slide-maker's manifest-filename traversal both show AI tooling trusting artifact metadata (tar members, generated filenames) that upstream tooling would sanitize. Sweep: any pipeline that extracts model archives or writes files named from model/manifest output — test absolute paths,
../, and symlink members against a temp canary root. - Tenant-id-in-header + check-hostname-not-port + DNS-rebind-at-send: three quick oracle wins on LLM-observability/SaaS panels. OpenLIT (forged project header reads other tenants' prompts/completions), Mealie (IdP-allowlist ignores port → internal-port SSRF on login), Helicone (creation-time-only URL check → DNS-rebind to internal). The reusable battery for any LLM-observability/multi-tenant panel you have scope over: (a) swap
X-*-Project/Tenant-Idheaders between two throwaway orgs and diff the read APIs; (b) for every outbound fetch (avatar, webhook, integration URL), re-test after DNS change and after port change; (c) treat webhook senders as blind internal scanners via a rebinding resolver on an owned domain.
Replayable validation boundaries (lab/authorized scope only)¶
- Symlink/spawn write battery (owned agent harness only): disposable workspace + marker dir outside root; committed symlink, dangling symlink, sub-agent
workdirvariants; evidence = marker files created and the tool-call trace. Never steer a third party's agent. - Side-effecting-SELECT sweep (own DB lab): run the denylist'd tool's gate against
setval('own_seq',1),query_to_xml('DELETE FROM scratch LIMIT 0'),lo_importon a temp file,pg_sleep(0.1)timing; table gate-verdict vs DB-effect. Use scratch tables only. - Sandbox env diff (owned harness): in-sandbox
envdump diffed against launcher env; report key names present; never exfiltrate values. - Settings→YAML injection (own instance): submit a value with
\n+ a benign top-level marker key, diff on-disk config; if a command-executing key exists, stop at the config diff — never save a live hook payload on shared systems. - Header-tenant diff (owned two-org lab): two throwaway orgs, swapped
x-*-project-idheaders on every read route; evidence = field-presence table, redacted prompt content. - Rebind/port SSRF (owned domain + owned internal listener only): attacker-owned domain with a TTL-1 record flipped to the owned listener between creation and send; port-mutation probe against a lab listener. No cloud-metadata endpoints, no third-party internal ranges.
- Emulator template check (dev boxes in scope only): point a MOCK/template field at
$util.encode-style inert reflection marker first; prove reachability without running destructive commands.
Related pages¶
- Aug 6 agent tool-policy/file-fetch boundaries — same harness-audit harness pattern (denied-sink proofs, prompt-injection reachability claims).
- Oct 10 HaiND MCP coordinated wave — morning wave of the same MCP-server audit families (guard-asymmetry SSRF ≈ Helicone; sibling-gate ≈ OpenLIT).
- July 28 zip-lib multi-stage symlink boundary page — Zip-Slip/symlink genealogy for the Cohere SDK and open-multi-agent legs.
- Sept 18 ignored-scope page and Oct 9 Vikunja page — scope/tenant-check drift genealogy for the OpenLIT leg.