Skip to content

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

  1. 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-supplied workdir, each paired with a marker file outside the root. This is the same repository-trust genealogy as the mise .tool-versions and Entire CLI checkpoint-restore pages — validate-at-check ≠ validate-at-write.
  2. "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_xml inside SELECT — 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, and SELECT ... INTO. A keyword denylist is a fingerprint, not a gate.
  3. Sandbox env/network inheritance is a five-minute post-foothold check. mini-swe-agent's missing --clearenv means 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): run env / read /proc/self/environ inside 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/*_TOKEN names exist — presence, not value, is the finding.
  4. 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.
  5. 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.
  6. 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.
  7. 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-Id headers 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 workdir variants; 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_import on 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 env dump 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-id headers 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.