The Gate Was Called and Threw the Answer Away: Fifteen Agent-Platform CVEs in Ten Seconds
Between 01:17:10.103Z and 01:17:20.410Z on 20 October 2026 — a window of ten and a half seconds — NVD published fifteen CVE records against ten open-source AI agent platforms. Every one was assigned by VulnCheck as CNA; every one carries a reference to a Lasso Security advisory; the three credits that are published name Noy Pearl, Michael Sromin and Ophir Dror. The ten projects hold roughly 128,000 GitHub stars between them, and the batch is not ten findings. It is one finding stated ten ways: the agent has a tool that runs shell commands, and the thing standing in front of that tool is absent, mocked, or hard-coded to say yes.
We read every line of source the advisories cite, at the exact commits they pin. What follows is that reading. The most useful part is not any single record — it is that four different teams, working in four different languages, independently built the same hole, and in three cases wrote a comment next to it explaining that they had.
The batch
Fifteen identifiers, ten projects, one publication window. Scores shown are the CVSS 4.0 base scores recorded at NVD.
- Letta (25,196★, Apache-2.0) — CVE-2026-77936 (9.3 critical, CWE-1188), CVE-2026-77937 (8.6 high, CWE-639), CVE-2026-77938 (7.1 high, CWE-526). Through 0.16.8.
- Coze Studio (21,731★, Apache-2.0) — CVE-2026-77934 (8.7 high, CWE-1188), CVE-2026-77935 (8.7 high, CWE-639).
- DB-GPT (20,164★, MIT) — CVE-2026-77932 (9.3 critical, CWE-306), CVE-2026-77933 (9.3 critical, CWE-1392). Through 0.8.2.
- Langchain-Chatchat (38,697★, Apache-2.0) — CVE-2026-75128 (9.3 critical, CWE-306). Through 0.3.1.3.
- OpenSRE (12,239★, Apache-2.0) — CVE-2026-109444 (8.7 high, CWE-862). Through 0.1.2026.10.19.
- LangAlpha (1,827★, Apache-2.0) — CVE-2026-109440 (8.6 high), CVE-2026-109441 (6.9 medium, CWE-653). Through 2026.09.07.
- EvoScientist (4,992★, Apache-2.0) — CVE-2026-77930 (8.7 high, CWE-862). 0.1.0 before 0.2.5.
- Aix-DB (2,253★, no license) — CVE-2026-77926 (8.7 high, CWE-749). Version 1.2.4.
- piflow (541★, no license) — CVE-2026-109442 (9.3 critical, CWE-306). 2.0.0 through 2.0.1.
- JoySafeter (314★, Apache-2.0) — CVE-2026-109443 (8.7 high, CWE-653). Through 1.0.0.
Six of the ten repositories have no SECURITY.md (Coze Studio, DB-GPT, Langchain-Chatchat, EvoScientist, piflow, LangAlpha); four do (Letta, OpenSRE, Aix-DB, JoySafeter). None of the fifteen identifiers appears in the CISA Known Exploited Vulnerabilities catalogue, which we checked directly: catalogue version 2026.10.16, 1,748 entries, zero hits.
“Mock User Info” — the comment that is also the vulnerability
The sharpest record in the batch is CVE-2026-77932, because the defect is annotated. DB-GPT’s packages/dbgpt-serve/src/dbgpt_serve/utils/auth.py is thirty-nine lines long. The whole of its authentication is this, read verbatim at the pinned commit 83609c47:
def get_user_from_headers(user_id: Optional[str] = Header(None)):
try:
# Mock User Info
if user_id:
return UserRequest(
user_id=user_id, role="admin", nick_name=user_id, real_name=user_id
)
else:
return UserRequest(
user_id="001", role="admin", nick_name="dbgpt", real_name="dbgpt"
)
Both branches return role="admin". The else branch — the one taken when no header is supplied at all — returns a hard-coded administrator named dbgpt. This is a FastAPI dependency, which means it is wired into route signatures and looks, at every call site, exactly like authentication. The comment is honest and two words long, and it has been load-bearing for long enough that /api/v1/chat/react-agent now sits behind it with a code_interpreter and a shell_interpreter attached. We have written about DB-GPT’s sandbox fallback and skill-upload RCEs before; those were bugs in a control, and this is the absence of one. Note the second record, CVE-2026-77933: the shipped docker-compose.yml publishes MySQL on all interfaces with a static root password, so the code execution lands next to a database that needs no further work.
“return True”, with the policy explicitly discarded
OpenSRE is an AI site-reliability agent: it triages alerts from Grafana and Loki and can act on them. CVE-2026-109444 points at tools/interactive_shell/subprocess_presenter.py, and the function is the entire story:
def execution_allowed(
self,
policy: ExecutionPolicyResult,
*,
action_summary: str,
) -> bool:
_ = (policy, action_summary)
return True
Read what that middle line does. _ = (policy, action_summary) is the Python idiom for I know these arguments exist and I am deliberately not using them. The approval machinery runs. A policy result is computed and handed to the gate. The gate assigns it to a throwaway and returns True. This is the headless presenter used on non-interactive surfaces — the Slack gateway and scheduled runs — so on exactly the paths where no human is watching, the gate that exists to summon a human approves unconditionally. The agent reads alert text an attacker can influence, and the commands inherit the process environment, which on an SRE agent is where production credentials live. The pinned commit 8eba8c93 dates from 19 October 2026, the day before publication: the researchers pinned to current main because there is nothing else to pin to.
One unauthenticated POST, one always-registered shell
CVE-2026-75128 is the simplest and, by population, probably the largest. Langchain-Chatchat has 38,697 stars — the most-starred project in the batch — and libs/chatchat-server/chatchat/server/api_server/tool_routes.py is forty-eight lines total:
@tool_router.post("/call", response_model=BaseResponse)
async def call_tool(
name: str = Body(examples=["calculate"]),
tool_input: dict = Body({}, examples=[{"text": "3+5/2"}]),
):
if tool := get_tool(name):
try:
result = await tool.ainvoke(tool_input)
There is no authentication dependency in the signature, no allow-list, and — per Lasso’s reading of server/utils.py — the per-tool use flag that operators set to keep dangerous tools away from the agent gates only the agent’s tool list, not this route. The shell tool is always imported and therefore always registered. One POST naming it runs a command as root in the shipped Docker image. The remediation question answers itself from the repository metadata: the last release is v0.3.1, July 2024, and the last push to the repository was 10 November 2025. This project has not moved in eleven months. Nearly thirty-nine thousand stars, an unauthenticated command-execution route, and no maintainer on the other end. We covered Langchain-Chatchat’s MCP RCE in April; the project was already quiet then.
Letta: the switch exists, and the file that starts the server does not flip it
Letta’s three records are the clearest illustration of a pattern we keep meeting: the security control is implemented, tested and documented, and the default deployment path never reaches it. letta/server/rest_api/app.py, lines 797–799:
if (os.getenv("LETTA_SERVER_SECURE") == "true") or "--secure" in sys.argv:
print(f"▶ Using secure mode with password: {random_password}")
app.add_middleware(CheckPasswordMiddleware, password=random_password)
The middleware is good. It is also opt-in, and the compose.yaml the project ships does not set LETTA_SERVER_SECURE — we read all sixty-six lines; the variable does not appear. The same file publishes the PostgreSQL port to the host ("${LETTA_PG_PORT:-5432}:5432") with POSTGRES_PASSWORD=${LETTA_PG_PASSWORD:-letta}, i.e. the password letta unless you knew to override it. That is CVE-2026-77936 at 9.3: every route open, including POST /v1/tools/run, which executes Python.
CVE-2026-77937 is the one that survives turning the switch on. In letta/server/rest_api/dependencies.py, the acting identity is a request header:
def get_headers(
actor_id: Optional[str] = Header(None, alias="user_id"),
The code immediately after validates the format of that value — it must match user-<uuid4> or the request is rejected with LettaInvalidArgumentError. Format is checked; authority is not. A caller asserts who they are and is believed, so a shared password buys you a perimeter and nothing inside it. This is the same shape as the caller-supplied identity defects we examined in Wanwu, Astron and THCLAWS: a validator that confirms a string is well-formed and is then mistaken for one that confirms it is true.
CVE-2026-77938 closes the loop. In letta/services/tool_sandbox/base.py, the environment handed to a local tool subprocess begins at line 487 with env = os.environ.copy() if is_local else {}. The layering comment above it is careful and correct about precedence, and the base layer is the entire server environment: LETTA_PG_URI, POSTGRES_PASSWORD, provider API keys. Anyone who can run a tool can read them. Letta’s newest release, 0.16.8, shipped 14 May 2026; the repository is actively developed (last push 18 October 2026) but has not cut a release in five months, so there is currently no tag to upgrade to.
The finding the metadata hides: Coze Studio’s fix went to the wrong file
This is where reading the source changes the answer. CVE-2026-77934 is recorded as affecting “Coze Studio before 0.5.0”, which reads as resolved — v0.5.0 was published on 29 October 2025, nearly a year before the identifier. The cited fix is commit fcf40d10, “feat(infra): Set the Code Runner environment to sandbox by default (#2339)”, dated 17 October 2025. We pulled it: one file changed, one line added, one line removed. It flips docker/.env.example from CODE_RUNNER_TYPE="local" to "sandbox".
But Lasso’s advisory does not name .env.example. It names .env.debug.example line 235, and describes the affected deployment as the one started with make or make debug. So we checked both files at main, today:
docker/.env.exampleatmain— noCODE_RUNNER_TYPEline at all (at tag v0.5.1 it reads"sandbox", line 263).docker/.env.debug.exampleatmain— line 235:export CODE_RUNNER_TYPE="local". Identical at tag v0.5.1.
The hardening landed in one file and its sibling was never touched. The affected-version metadata — before 0.5.0 — is therefore accurate about the file the NVD reference happens to pin and wrong about the deployment the advisory actually describes: the debug target’s default is local on current main, in v0.5.0 and in v0.5.1. An operator who reads “fixed in 0.5.0” and runs make debug is running the vulnerable configuration. And the surrounding comment block in that file has always said so plainly: “local: using venv, no env isolation”. The documentation was never wrong. The default was.
Two further notes. The import filter Coze Studio applies to user Python is a keyword filter, and Lasso reports __import__() walks past it — a blocklist outcome we have now recorded often enough to treat as the expected one. And CVE-2026-77935, the cross-space IDOR, is unfixed through 0.5.1. We read backend/application/workflow/workflow.go at the pinned commit: SaveWorkflow calls checkUserSpace(ctx, uid, req.SpaceID) and then, fifteen lines later, Save(ctx, mustParseInt64(req.WorkflowID), req.GetSchema()). The check confirms you belong to the space you named. Nothing confirms the workflow you named belongs to that space. Pair your own spaceID with someone else’s workflowID and the gate passes honestly, having answered a question nobody needed the answer to. The repository’s last push was 29 July 2026, so no fix is imminent.
The shared primitive: deepagents, and the gate you have to ask for
Four of the ten projects — EvoScientist, Aix-DB, piflow and LangAlpha — build on LangChain’s Deep Agents, and the defects cluster there in a way worth stating precisely. create_deep_agent supplies an execution tool by default; the human-in-the-loop gate is something the integrator opts into, via interrupt_on. Four teams did not opt in.
Aix-DB’s enhanced_common_agent.py is the most legible example, and the parameter that matters is not the backend but its second argument:
backend=LocalShellBackend(
root_dir=str(workdir),
inherit_env=True,
timeout=120
),
inherit_env=True is why CVE-2026-77926 is scored as credential exposure and not merely command execution: the shell the chat agent drives starts with the server’s datasource credentials and model API keys already in its environment. Directly beneath that call sits a large commented-out middleware=[...] block — summarisation and context-editing middleware, disabled. The middleware slot that would have carried an approval gate is present in the file, and empty.
piflow is the same construction without the authentication: @app.post("/chat") in server.py takes a request and a thread ID, logs a preview, and hands it to the engine — no dependency, no token — while agents/tools.py exposes @tool def exec_shell(command: str). LangAlpha assembles tools: list[Any] = [execute_code_tool, bash_tool, bash_output_tool, ...] with no interrupt_on, and its second record is a one-word default: network_mode: str = "bridge" in config/core.py, which puts every tenant’s sandbox container on Docker’s default bridge with inter-container communication on. The workspace-ownership checks above it are real; the network beneath them is flat. This is the same lesson as MaxKB’s deepagents execute tool and yesterday’s Cognition CVE-2026-109437, and it is becoming the defining integration error of the agent-framework era: a framework that ships a shell by default and an approval gate by request will be deployed, overwhelmingly, with the shell and without the gate.
EvoScientist: the one that was already fixed, and what the fix cost
Exactly one record in this batch has a patched release, and its history is the counter-example the rest of the batch needs. Issue EvoScientist/EvoScientist#387 was filed on 24 July 2026 by X-iZhang, titled “HITL: sub-agent shell execution is never approved (sync + async), and nothing guards injected curl | bash”. It received zero comments and was closed seven days later, on 31 July, by commit d249e320.
That commit is not a one-liner. Sixteen files: backends.py +280/−4, EvoScientist.py +39/−19, subagents/_factory.py +6/−1, middleware/background.py +19/−4, prompts.py +11/−0 — and, tellingly, tests/test_hitl.py +439/−39 plus a new tests/test_backends.py at +318/−0. More than seven hundred lines of new test coverage for the approval path alone. The fix shipped in v0.2.5; the project is now on v0.3.7 (17 October 2026). The CVE arrived 81 days after the fix, and the record correctly scopes itself to 0.1.0 before 0.2.5.
Read the diff next to the comment still sitting at line 96 of _factory.py, which explains that the async sub-agent path suppresses AskUserMiddleware because interrupt() would deadlock an async sub-agent. The team knew the gate was missing and why it was missing: approval is synchronous, and they had built something that is not. That is a real engineering constraint, not an oversight — and the fix required rebuilding the backend rather than adding a conditional. Which is the honest reason the other nine projects have not done it.
Where the scores disagree
Worth recording rather than averaging. VulnCheck assigned CVSS 4.0; Lasso’s advisory pages carry CVSS 3.1 figures, and on three records the two frameworks land on different severity bands:
- CVE-2026-77934 (Coze Studio): NVD 8.7 high · Lasso 9.9 critical
- CVE-2026-77930 (EvoScientist): NVD 8.7 high · Lasso 9.9 critical
- CVE-2026-109444 (OpenSRE): NVD 8.7 high · Lasso 9.0 critical
- CVE-2026-77936 (Letta), CVE-2026-75128 (Chatchat), CVE-2026-77932 (DB-GPT): NVD 9.3 critical · Lasso 9.8 critical
The gap is mostly structural — CVSS 4.0 separates vulnerable-system from subsequent-system impact, and all fifteen vectors here record SC:N/SI:N/SA:N, declining to credit onward reach into the MySQL, MinIO, Redis and etcd services the advisories describe at length. If your prioritisation pipeline filters on critical and ingests NVD’s 4.0 score, three records that the researchers consider critical will not reach your queue. That is a tuning problem worth knowing about before the next batch, not after.
What to do
Version numbers help with exactly one of these. EvoScientist: upgrade past v0.2.5 (v0.3.7 is current) and stop using the documented EvoSci serve --auto-approve deployment. For the rest there is no patched release, so the controls are configuration and architecture.
Letta: set LETTA_SERVER_SECURE=true, remove the PostgreSQL port mapping from compose.yaml, override LETTA_PG_PASSWORD, and treat the user_id header as untrusted — multi-tenant separation is not available at 0.16.8 regardless of the password. Coze Studio: do not run make debug on any reachable host; check CODE_RUNNER_TYPE in the .env file your deployment actually loads rather than trusting the version number, and close self-registration. DB-GPT and Langchain-Chatchat and piflow: these have no authentication to configure — put them behind an authenticating proxy or take them off the network. For Langchain-Chatchat specifically, weigh that the project has been unmaintained for eleven months; there will be no patch. OpenSRE: require approval for shell execution on gateway and scheduled paths, and scope the process environment, since the credentials an SRE agent holds are the point of the attack. Aix-DB: set inherit_env=False. LangAlpha: set a per-tenant Docker network; the default bridge is not isolation.
The generalisable audit is one question asked of your own stack, and it is cheap: for every agent you run, find the tool that executes code, then find the gate in front of it, then prove the gate is reachable on the path you actually deploy. All fifteen of these records are a different answer to the third clause. The gate was not there (piflow, Langchain-Chatchat); the gate was there and said yes to everyone (DB-GPT); the gate was there, was called, and threw the answer away (OpenSRE); the gate was there and the default start-up file routed around it (Letta, Coze Studio). Only the fourth kind is visible in a configuration review, and only the second and third are visible in the source. None of them is visible in a version number.
Our verification was read-only. All fifteen CVE identifiers, their publication timestamps, the disclosure@vulncheck.com source identifier, vulnerability statuses, full descriptions, CWE assignments, CVSS 4.0 vectors and scores, and complete reference lists were read first-hand from the NVD 2.0 API, as was the ten-second publication window (01:17:10.103Z to 01:17:20.410Z) and the presence of one unrelated record, CVE-2026-109395, inside it. Every quoted line of Python, Go, YAML and shell configuration was fetched from the exact commit SHA pinned in the corresponding NVD reference and is quoted verbatim, including the # Mock User Info comment, the _ = (policy, action_summary) discard, the LETTA_SERVER_SECURE conditional, the os.environ.copy() base layer, the inherit_env=True argument and the network_mode default. Repository metadata (star counts, licences, last-push dates, SECURITY.md presence) and all release tags and publication dates were read first-hand from the GitHub API on 20 October 2026. The Coze Studio finding was measured directly by fetching docker/.env.example and docker/.env.debug.example at main, at tag v0.5.1 and at the NVD-pinned commit, and by reading commit fcf40d10’s complete file list and line counts. EvoScientist issue #387’s title, author, state, open and close timestamps and zero comment count, and commit d249e320’s sixteen-file diff with per-file line counts, were read first-hand from the GitHub API. Severity figures attributed to Lasso Security, the researcher credits, and descriptions of files we did not quote (Coze Studio’s direct/runner.go, Langchain-Chatchat’s server/utils.py and shell tool factory, OpenSRE’s policy.py and execution.py, Letta’s tools.py) are reported as the advisories state them and are not our measurements; the __import__() filter bypass is Lasso’s reproduction, not ours. KEV status was checked directly against the published catalogue (version 2026.10.16, 1,748 entries). Brave Search was used for discovery only. We installed nothing, stood up none of the ten platforms, sent no request to any endpoint described above, executed no proof of concept, and sent no third-party request beyond reading the cited public records.
Sources:
- NVD — CVE-2026-77936 (Letta insecure default, CVSS 4.0 9.3; published 2026-10-20T01:17:20.107Z)
- NVD — CVE-2026-75128 (Langchain-Chatchat unauthenticated /tools/call, CVSS 4.0 9.3)
- NVD — CVE-2026-77932 (DB-GPT mock authentication, CVSS 4.0 9.3)
- NVD — CVE-2026-77934 (Coze Studio unsandboxed code runner, CVSS 4.0 8.7; “before 0.5.0”)
- NVD — CVE-2026-109444 (OpenSRE approval gate bypass, CVSS 4.0 8.7)
- VulnCheck — OpenSRE approval gate bypass advisory (credits Noy Pearl, Michael Sromin, Ophir Dror)
- Lasso Security — CVE-2026-77934 advisory (names
.env.debug.exampleline 235; CVSS 3.1 9.9) - coze-studio commit fcf40d10 — the one-line default change to
docker/.env.example(17 October 2025) - EvoScientist#387 — the HITL sub-agent report (filed 24 July 2026, closed 31 July 2026, zero comments)
- DB-GPT
auth.pyat the pinned commit —get_user_from_headersin full - CISA — Known Exploited Vulnerabilities catalogue (none of the fifteen listed; checked 20 October 2026, 1,748 entries)
- al-ice.ai — Cognition CVE-2026-109437 and the unauthenticated agent API
- al-ice.ai — MaxKB shipped a deepagents shell tool nobody asked for
- al-ice.ai — DB-GPT’s sandbox fallback and skill-upload RCEs
- al-ice.ai — Langchain-Chatchat CVE-2026-30617, RCE via MCP STDIO configuration
- al-ice.ai — caller-supplied identity in Wanwu, Astron and THCLAWS