The Caller Chooses the Owner: Eight Agent-Platform CVEs in Three Minutes
This morning we covered thirteen advisories from the 11 October 14:17 UTC batch — the credential-disclosure half, all assigned by VulnCheck and all credited to one researcher, hieuPenguinnn. These are the remaining eight verifiable agent-platform findings from the same three-minute window, and they share a theme of their own: in every one, the server lets the caller name the owner. An application ID, a key ID, an event ID, a file path, a report body, a chat message — each arrives from the caller, and each is trusted as an ownership decision.
Two records from the same window are deliberately excluded. CVE-2026-108697 is a CoreShop e-commerce authorization flaw, out of scope for this site. The four CowAgent records (CVE-2026-108681 through 108683 and CVE-2026-108768) are thin VulnDB-sourced denial-of-service entries from a different disclosure pipeline, with no pinned source to verify against. Everything below was read first-hand: NVD records, VulnCheck advisories, researcher notes, and the referenced repositories at the pinned tags.
WanWu: four CVEs, two fixed, two open — and the open two are the worse two
UnicomAI WanWu (2,362 stars) is a self-hosted, multi-tenant AI agent platform — agents, RAG apps, workflows, MCP servers — and it accounts for half of this briefing. The four CVEs split cleanly into a fixed pair and an open pair, and the split itself is the story.
CVE-2026-108853 — 7.2 high, CWE-639, CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N, affecting versions before 0.6.3. DELETE /v1/appspace/app with a caller-supplied appId permanently deletes other tenants’ agent, RAG, workflow or chatflow applications — conversations and data included — with sequential assistant IDs as the only discovery requirement. We read DeleteAppSpaceApp at v0.6.2, and the ownership gap is structural: the function signature accepts userId and orgId, then never consults either before issuing the destructive downstream calls, which take only the caller’s appId and appType:
func DeleteAppSpaceApp(ctx *gin.Context, userId, orgId, appId, appType string) error {
// delete publish app
_, err := app.DeleteApp(ctx.Request.Context(), &app_service.DeleteAppReq{
AppId: appId,
AppType: appType,
})
CVE-2026-108854 — 5.3 medium, CWE-639, before 0.6.3. The same class one layer down: DELETE /v1/appspace/app/key with a numeric apiId revokes other users’ legacy AppKeys across organizations. The advisory pins the pre-fix ORM lines, where the delete filtered by primary key alone.
Both are genuinely fixed. Commit 13d0b22 (30 July 2026, “fix(security): mitigate IDOR on delete operations across all services”) describes the defect class in its own message — delete-path ORM filters keyed only on the primary key, with no user_id/org_id ownership predicate — and adds the ownership filters across six services and fourteen delete entry points. One documentation wrinkle worth recording precisely: the NVD range says “before 0.6.3”, the v0.6.3 tag exists, but the repository’s releases page has no v0.6.3 entry — it jumps from v0.6.4 to v0.6.2. The fix is tagged, not released with notes.
The open pair is worse, and both are open at the newest release, v0.6.5 (18 September 2026) — we diffed both files against main HEAD and they are identical, so there is no branch fix either. CVE-2026-108855 — 5.3 medium, CWE-862 — runs through the unpublish endpoint: supply a victim’s appId and appType from the exploration marketplace, and UnPublishApp deletes every other user’s API-key rows for that application. The code comments its own behaviour, in Chinese: “取消发布的时候删除其他用户的ApiKey” — when unpublishing, delete other users’ ApiKeys:
if err := sqlopt.SQLOptions(
sqlopt.WithAppID(appId),
sqlopt.WithAppType(appType),
sqlopt.WithExcludeUserID(userId),
).Apply(tx).Delete(&model.ApiKey{}).Error; err != nil {
WithExcludeUserID is the tell: the delete set is defined as everyone except the caller, keyed to a caller-chosen application. Any enabled user can cut off every other tenant’s MCP and OpenAPI clients for any marketplace app.
CVE-2026-108856 — 2.3, CWE-639, CVSS:4.0/AV:N/AC:H/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N — mints an AppKey bound to another user’s MCP server via POST /v1/appspace/app/key, then uses it to open MCP sessions and invoke that server’s tools under the victim’s upstream authentication. We traced all six links first-hand. The route sits in the common group (JWTUser plus CheckUserEnable only — no object-level permission handler, unlike the resource groups). The request body’s validation is literally nothing:
func (g GenAppKeyRequest) Check() error {
return nil
}
The BFF service forwards the caller-chosen AppId with the caller’s own identity, gRPC passes it through, and the ORM inserts the row with no SELECT before the Create — nothing ever loads the referenced application to ask who owns it. On the consumption side, all four MCP routes (/mcp/server/sse, /mcp/server/message, both streamable variants) are guarded solely by AuthAppKeyByQuery, which compares only the key’s AppType and then copies the stored AppId into the request context. The server’s identity is the only barrier, and the attacker supplied it at key creation.
A note on the 2.3: the score’s AC:H prices in the need to know the victim’s MCP server UUID. That is a real prerequisite — but the sibling finding in the same batch shows application IDs in this platform are guessable sequential values, and the unpublish finding shows the marketplace itself as the ID source. Whether UUIDs specifically are enumerable is unproven either way; the score assumes they are not. The researcher’s own write-up (linked from the NVD references, source-reviewed rather than runtime-tested, as is ours) reaches the same six-link chain we independently traced, which is a useful cross-check on the reading if not on the exploitability.
Astron: the resume endpoint adopts the victim’s identity
CVE-2026-108864 — iFlytek Astron Agent, 2.3, CWE-639, 8,890 stars. POST /workflow/v1/resume with another application’s event_id resumes that application’s paused workflow: the attacker injects resume content into the victim’s workflow and reads its continuation output stream. We read resume_open at v1.1.2 end to end. The lookup is EventRegistry().get_event(event_id=event_id) straight from the request body, and three lines later the handler becomes the victim event:
span.app_id = event.app_id
span.uid = event.uid
span.chat_id = event.chat_id
No comparison between the caller’s application and the event’s application exists anywhere in the handler. The Snowflake event-ID predictability the advisory relies on is the advisory’s claim, not something we tested — but the missing ownership check is unconditional once an ID is known. This is the sequel to the unsandboxed code-node RCE (CVE-2026-108263) we covered earlier in the week, which was fixed at v1.1.2. The new finding affects through v1.1.2 — and v1.1.2, released 7 September, is still the newest release. The code-execution hole was closed one release ago; the cross-tenant isolation hole is open in that same release.
thClaws: the string says inputs/, the filesystem says otherwise
CVE-2026-108839 — thClaws, 2.3, CWE-59. thClaws is a Rust AI-agent harness (GUI, CLI, headless and webapp from one binary; MCP, skills, agent teams), 1,239 stars — and v0.141.0 was released at 01:18:57Z on the morning of disclosure day. The POST /v1/inputs handler stages files into a worker’s workspace ahead of a dispatch — the code comments describe the coder-to-reviewer handoff explicitly — and it validates each requested path as a string:
fn path_allowed(rel: &str, prefixes: &[String]) -> bool {
if rel.is_empty()
|| rel.starts_with('/')
|| rel.contains("..")
...
prefixes.iter().any(|p| p == "*" || rel.starts_with(p.as_str()))
Empty, absolute, parent-traversal, dot-directories: all rejected. Then the write:
let dest = ws.join(&f.path);
...
std::fs::write(&dest, &bytes).map_err(io_err)?;
ws.join plus fs::write follows symlinks. A path like inputs/evil passes the string check — it starts with inputs/ — while resolving wherever a pre-planted inputs/evil symlink points, with daemon privileges, truncating or overwriting files outside the workspace. There is no canonicalize, no symlink_metadata, no O_NOFOLLOW anywhere on the path. The check validates the name; the kernel resolves the file. v0.141.0 is both the pinned version and the newest release, published roughly thirteen hours before the NVD record — no fixed version.
The output surfaces: an unauthenticated SSRF and a chat-rendering XSS
The last two are not tenant-isolation flaws but they belong to the same batch and the same theme: caller-supplied content the server renders as something more powerful than text.
CVE-2026-108850 — company-research-agent, 6.9 medium, CWE-918, 2,298 stars. The /generate-pdf endpoint is reachable with no authentication — the application file imports no auth dependency, only CORS middleware — and forwards the request body straight through:
@app.post("/generate-pdf")
async def generate_pdf(data: PDFGenerationRequest):
"""Generate a PDF from markdown content and stream it to the client."""
try:
success, result = pdf_service.generate_pdf_stream(data.report_content, data.company_name)
generate_pdf_stream hands the markdown to generate_pdf_from_md, which converts markdown links into ReportLab <link> markup and appends raw line text into Paragraph objects before doc.build. The only sanitization in the entire chain is applied to company_name — for use in a filename. The report content itself is never escaped, so inline ReportLab markup including <img> elements reaches the renderer, and the server fetches the referenced hosts — internal or external — embedding responses in the returned PDF. The fetch mechanics are the advisory’s stated mechanism; what we verified is the unauthenticated, unescaped path that delivers attacker markup to the renderer. Newest release v2.2.0 (21 September) is the affected ceiling: no fix. This is an agent that researches companies and the report pipeline phones home to whoever the requester names.
CVE-2026-108852 — Deep Chat, 2.1, CWE-79, 3,729 stars. The entire defect is one line in remarkableConfig.ts at 2.5.1, executed by every renderer instance the component creates:
public static createNew(customConfig?: RemarkableOptions) {
const remarkable = RemarkableConfig.instantiate(customConfig);
RemarkableConfig.addPlugins(remarkable, customConfig);
remarkable.inline.validateLink = () => true;
return remarkable;
}
Link validation unconditionally returns true, so javascript: URLs in markdown links survive rendering. The injection points the advisory names are what make this an AI-security item rather than a generic stored-XSS note: crafted links arrive in AI responses, in addMessage content, or in loaded history — the chat component renders model output as privileged markup in the embedding page, and a click executes script. Newest release 2.5.1 (27 August) is the ceiling here too.
Six of eight with nowhere to go
The fix-status ledger: WanWu’s delete pair is fixed (tag v0.6.3, with the release-notes gap noted above). Everything else — the WanWu key pair at v0.6.5, Astron at v1.1.2, thClaws at v0.141.0, company-research-agent at v2.2.0, Deep Chat at 2.5.1 — describes the newest release of its project. Six of eight records offer the reader “wait,” the same closing line as PraisonAI, openPDC and yesterday’s JeecgBoot second wave.
What is configurable without a patch: unpublish no key cascade you can switch off, so treat WanWu’s marketplace apps as untrusted input to your key inventory and audit api_key rows for keys you did not mint; do not expose Astron’s resume endpoint across application trust boundaries; confine thClaws workspaces so a symlink escape lands somewhere boring, and do not widen THCLAWS_INPUTS_PREFIXES past inputs/; keep the research agent’s PDF endpoint off the network or behind auth; and treat Deep Chat’s rendered history as untrusted content until link validation is restored.
The shape of it: twenty-one advisories in three minutes from one researcher against the agent stack’s middle layer, and the model never comes up. Gateways, key issuers, workflow registries, workspace stagers, report renderers, chat components — the infrastructure that decides whose something is, implemented as string comparisons against caller-supplied values. The permission model does not come along by default, and the newest surfaces have the thinnest coverage. We have now written that sentence about MCP servers, JeecgBoot’s AI module, and four more platforms in a single day. It keeps being true.
Verification note. All CVE identifiers, CVSS 4.0 base scores and vectors, CWE assignments, publication timestamps (14:17:03Z–14:17:06Z on 11 October 2026), NVD status values and the assigning source (disclosure@vulncheck.com) were read first-hand from the NVD 2.0 API. The credit to hieuPenguinnn was read from the VulnCheck advisory pages we opened; the HackMD researcher notes are linked from the NVD references and were used as cross-checks on mechanism, not as sources for scores or affected ranges. Every source excerpt — WanWu’s DeleteAppSpaceApp, GenAppKeyRequest, BFF and gRPC GenAppKey, UnPublishApp, the common route group and middleware init, the openapi MCP routes and AuthAppKeyByQuery, Astron’s resume_open, thClaws’ path_allowed and post_inputs, company-research-agent’s generate_pdf chain, and Deep Chat’s createNew — was read directly from the referenced repository at the pinned tag and is quoted verbatim; main-HEAD comparisons for the two open WanWu findings were computed with diff. Release tags and dates, star counts and push timestamps came from the GitHub API. The exclusions (CoreShop, CowAgent) and the closing argument are our editorial analysis. We found no evidence of exploitation of any of these and do not imply any. No request was sent to any third-party instance of any product named here, and no proof-of-concept was executed.
Sources:
- VulnCheck advisory — UnicomAI Wanwu before 0.6.3 IDOR via DELETE /v1/appspace/app (CVE-2026-108853, 7.2 high, CWE-639)
- VulnCheck advisory — Wanwu before 0.6.3 IDOR AppKey deletion via DELETE /v1/appspace/app/key (CVE-2026-108854, 5.3 medium, CWE-639)
- VulnCheck advisory — UnicomAI Wanwu through 0.6.5 missing authorization via DELETE /v1/appspace/app/publish (CVE-2026-108855, 5.3 medium, CWE-862)
- VulnCheck advisory — UnicomAI Wanwu through 0.6.5 authorization bypass via AppKey minting (CVE-2026-108856, 2.3 low, CWE-639)
- VulnCheck advisory — iFlytek Astron Agent through 1.1.2 authorization bypass via workflow resume (CVE-2026-108864, 2.3 low, CWE-639)
- VulnCheck advisory — thClaws through 0.141.0 symlink-following file write via POST /v1/inputs (CVE-2026-108839, 2.3 low, CWE-59)
- VulnCheck advisory — Company Research Agent through 2.2.0 SSRF via generate-pdf ReportLab markup (CVE-2026-108850, 6.9 medium, CWE-918)
- VulnCheck advisory — Deep Chat through 2.5.1 XSS via markdown link validation bypass (CVE-2026-108852, 2.1 low, CWE-79)
- Wanwu —
internal/bff-service/service/appspace.goat v0.6.2, the ownership-freeDeleteAppSpaceApp - Wanwu —
internal/app-service/client/orm/app.goat v0.6.5, theUnPublishAppkey cascade - Astron Agent —
core/workflow/api/v1/chat/open.pyat v1.1.2, the identity-adoptingresume_open - thClaws —
crates/core/src/api_v1/artifacts.rsat v0.141.0, the string-checked, symlink-followingpost_inputs