PraisonAI agent/platform control boundaries and Formie submission overwrite batch¶
Source: GitHub Security Advisories REST fallback, updated 2026-05-29: GHSA-vg22-4gmj-prxw / CVE-2026-47391, GHSA-4mr5-g6f9-cfrh / CVE-2026-47392, GHSA-8444-4fhq-fxpq / CVE-2026-47393, GHSA-9cr9-25q5-8prj / CVE-2026-47394, GHSA-5cxw-77wg-jrf3 / CVE-2026-47395, GHSA-86qc-r5v2-v6x6 / CVE-2026-47396, GHSA-hvhp-v2gc-268q / CVE-2026-47397, GHSA-78r8-wwqv-r299 / CVE-2026-47398, GHSA-5c6w-wwfq-7qqm / CVE-2026-47390, GHSA-3qg8-5g3r-79v5 / CVE-2026-47410, GHSA-h8q5-cp56-rr65 / CVE-2026-47407, GHSA-6h6v-6m7w-7vxx / CVE-2026-47399, GHSA-gv23-xrm3-8c62 / CVE-2026-48169, GHSA-h37g-4h4p-9x97 / CVE-2026-47405, GHSA-w388-2392-px73 / CVE-2026-47409, GHSA-c2m8-4gcg-v22g / CVE-2026-47416, GHSA-4x6r-9v57-3gqw / CVE-2026-47406, GHSA-5jx9-w35f-vp65 / CVE-2026-47414, and GHSA-pgxq-p76c-x9cg / CVE-2026-47266.
This batch is durable because it captures reusable offensive validation patterns for agent stacks: public unauthenticated agent control planes, LLM-driven tool invocation into unsafe sinks, prompt/mention SSRF, MCP file-read boundaries, sandbox escape regression checks, untrusted project/module execution, workspace-scope IDOR, role escalation, hardcoded development secrets, and unauthenticated form-submission overwrite.
What changed¶
- PraisonAI A2A example tool execution — the first-party unauthenticated A2A example binds to
0.0.0.0, accepts/a2amessage/send, and registers an unsafecalculatetool implemented with Pythoneval(). - PraisonAI Python sandbox escape —
execute_code()subprocess mode missedprint.__self__andvars()paths that can recover the real builtins module despite earlier sandbox patches. - PraisonAI generated API server auth-disabled default —
praisonai deploy --type apican emit a Flask/chatand/agentsserver whereauth_enableddefaults to false while user workflows may bind externally. - PraisonAI call server fail-open auth —
CALL_SERVER_TOKENunset makes the agent invocation router return successfully from its auth dependency, exposing agent listing, metadata, invocation, and deletion. - PraisonAI MCP file-read residuals — the prior rules-path fix missed
workflow.show,workflow.validate, anddeploy.validate, leaving unauthenticated MCPtools/callpaths that read arbitrary host files or leak fragments through parser errors. - PraisonAI SSRF and file-write agent boundaries —
@url:prompt mentions and spider tools can fetch loopback/private-like URLs through weak normalization, while web-crawled hidden metadata can steerwrite_filewhen production passesworkspace=None. - PraisonAI untrusted module execution — YAML-controlled
module_pathvalues inagents_generator.pystill reach unguardedspec.loader.exec_module()sinks, bypassing earlier local-tool gates. - PraisonAI Platform tenant isolation failures — workspace routes check membership on the URL workspace but fetch inner resources by global ID; member-management routes only require ordinary membership; several advisories also cover hardcoded
dev-secret-change-meJWT defaults, role-promotion, member removal, dependency/label IDOR, and activity-log disclosure. - Formie front-end submission overwrite — unauthenticated users can post a known or guessed submission ID to the front-end save action and overwrite existing Craft CMS Formie submissions.
Operator triage¶
- Separate example risk from deployed pattern risk: the A2A
eval()issue is strongest where teams copied the official unauthenticated example or exposed an A2A service with comparable unsafe local tools. - Map every agent-facing network listener: check A2A, generated Flask API, call server, MCP server, and platform API separately; each has a distinct auth/default boundary.
- Treat prompt preprocessing as fetch-capable input:
@url:and crawler-derived content are pre-tool trust boundaries, not harmless context decoration. - Bind workspace ID to object ID: PraisonAI Platform findings are a clean test case for cross-tenant object substitution in routes that appear workspace-scoped.
- Use canaries, not secrets: validate file reads/writes, SSRF, and code execution with synthetic files, callback endpoints, marker values, and disposable workspaces only.
- For Formie, require owner-approved test records: submission overwrite validation should use a known lab submission ID, never guessed production records.
Replayable validation boundaries¶
PraisonAI exposed agent service checks¶
- Inventory reachable PraisonAI listeners and record component, bind address, version, and authentication state.
- For A2A, use a lab server copied from the affected example and a harmless calculation/canary tool. Prove that unauthenticated
message/sendcan reach tool invocation without running OS-impacting commands. - For generated Flask API, run
deploy --type apiin a disposable project and confirm whether/chatand/agentsaccept requests without an auth header under default config. - For the call server, start a lab instance with
CALL_SERVER_TOKENunset and verify unauthenticated agent listing/invocation against a benign echo agent; repeat with a token set to show the missing-token default is the boundary.
PraisonAI MCP and local filesystem checks¶
- Use a disposable MCP server process under a test Unix user.
- Create a canary file outside the expected workflow directory, then call
workflow.show,workflow.validate, ordeploy.validatewith that path. - Capture whether the response returns full content, file-existence evidence, or parser-error fragments.
- Avoid targeting real keys,
.envfiles, cloud credentials, or system secrets; the report should show the path-containment gap without collecting sensitive data.
PraisonAI prompt fetch, crawler, and write-file checks¶
- For
@url:, run the CLI with a tester-controlled loopback HTTP service that returns a synthetic marker and confirm it is prepended to model context. - Repeat with alternate loopback spellings only in a lab to show URL-normalization drift.
- For crawler-to-write-file, host a page containing benign hidden metadata that requests a temp-directory canary write. Verify that production
workspace=Noneskips containment and writes only the marker file. - Record the exact prompt/crawler path that introduced the attacker-controlled metadata; this distinguishes model behavior from tool-boundary failure.
PraisonAI sandbox and module-loading checks¶
- For
execute_code(), validate in an isolated container with no secrets mounted. Use a marker-only proof such as reading an environment variable set solely for the test or writing a temp canary. - Confirm the bypass survives the target version's existing blocked-attribute patches before claiming regression impact.
- For
agents_generator.py, supply a YAMLmodule_pathto a benign local module that only emits a marker, then show it executes withoutPRAISONAI_ALLOW_LOCAL_TOOLSor equivalent approval.
PraisonAI Platform workspace isolation checks¶
- Create two disposable workspaces and two low-privileged users.
- In workspace A, make the caller a normal member. In workspace B, create synthetic agents, projects, issues, labels, comments, and dependencies.
- Send requests under
/api/v1/workspaces/{workspace_A}/...while substituting object IDs from workspace B; stop at read-only proof where possible, or mutate only synthetic records. - Test member-management actions from a normal member account: self-promotion, adding an owner/admin, role changes, and member removal. Restore all lab memberships afterward.
- If
PLATFORM_ENVis unset, verify whether JWTs signed with the documented development secret are accepted against a lab account; do not forge real-user tokens.
Formie submission overwrite check¶
- In a lab Craft CMS/Formie instance or explicitly authorized staging form, create a harmless submission and record its ID.
- Submit to
actions/formie/submissions/save-submissionor the equivalent front-end action without authentication, using only the lab submission ID. - Prove that fields are overwritten or rejected based on the patched/unpatched version. Avoid guessing or enumerating production IDs.
Reporting heuristics¶
- For PraisonAI, report by boundary rather than dumping all CVEs together: exposed service auth, prompt/URL fetch, MCP file access, sandbox escape, untrusted module loading, and platform tenant isolation.
- Include the exact role used, environment variables set or unset, bind address, route, and whether the test came from a documented quickstart/example.
- For tenant IDOR, always include both IDs: the workspace authorized by the route prefix and the object whose owning workspace differed.
- For sandbox/module execution, keep evidence to marker-only execution and name the missing validation primitive (
__self__,vars, unguardedexec_module, or missing local-tool gate). - For Formie, include whether front-end editing is enabled, the affected Formie major version, and the unauthorized submission ID overwrite path.
August 25 follow-up: PraisonAI SSRF/origin/auth fail-open wave, mcp-shell allowlist bypasses, and Chainlit MCP stdio command injection¶
A second, larger PraisonAI wave plus two adjacent agent-runtime/MCP families landed on 2026-08-25. They reinforce the same boundary classes as this page but add three new, durable patterns: SSRF patch-bypass via redirect/DNS-rebinding, origin/auth allowlist fail-open, and MCP command-tool allowlist bypass.
PraisonAI / praisonaiagents (19 GHSAs, published 2026-08-25)¶
- SSRF protection is validated-once-then-fetched — bypass it two ways.
web_crawl(and the Jobswebhook_urlvalidator) resolves the host once, blocks private/loopback literals, then fetches withhttpx.Client(follow_redirects=True)(or a freshhttpx.AsyncClientfor webhooks) with no re-validation of the redirect target. Two independent bypasses: (a) a public URL that302-redirects to169.254.169.254/127.0.0.1, and (b) DNS rebinding (TTL=1s host that resolves public at validation, private at fetch).spider_tools._host_is_blocked()is worse — it never does DNS resolution at all, so127.0.0.1.nip.ioreaches loopback directly. Theexcept socket.gaierror: passin the webhook validator is a fail-open on DNS error plus a validate-then-fetch TOCTOU. See GHSA-5r34-2g38-6569, GHSA-8hjw-25cg-g52h, GHSA-vg6p-v9vm-6fgj, GHSA-x44h-65qv-cw74, GHSA-rg5q-pp8p-f7jm, GHSA-hmfx-4v44-9qw9. - Origin allowlists that use
startswithor unanchored regex are bypassable.http://localhost.evil.examplepasses astartswith("http://localhost")origin check; the WebSocket Chrome-extension origin regexre.match(r"chrome-extension://[a-z0-9]{32}")is unanchored at the end, so any longer origin passes. Both enable browser-mediated unauthenticated MCPtools/call/ WebSocketstart_sessionagainst a local agent runtime. See GHSA-pvph-5j39-v8qc, GHSA-wj6g-v78p-6fx3, GHSA-6g6r-q6gw-w8fg. - Auth fail-open and ignored
--api-key.praisonai serve/serve agentsparse--api-keybut never wire it into the FastAPI app, soPOST /agentsruns unauthenticated even with a key set. The Recipe server fail-opens whenauth: api-key|jwtis set but no secret exists (if not expected_key: return await call_next(request)). The async Jobs API (/api/v1/runs) ships with no auth at all. See GHSA-pvxx-r596-f5qj, GHSA-7ww9-85pg-cv4x, GHSA-r7v3-x45f-g7hp, GHSA-gfq8-hmph-9gjv, GHSA-2jgc-f764-c5r2. - Workspace containment that keys on
abspathnotrealpath.praisonai.codetools callis_path_within_directory()withos.path.abspath(), so a symlink inside the workspace whose target is outside passes the check whileopen()follows it;list_files()never calls the containment helper at all.FileMemory.__init__builds paths from an unsanitizeduser_id, enabling../file writes.ast_grep_rewriterewrites arbitrary files without the@require_approvalgate every sibling mutation tool has. See GHSA-ch89-h4r2-c8f8, GHSA-gxmw-5f7x-6g22, GHSA-cfxv-8fw8-rwpv. - Workflow include executes an included recipe's
tools.pymodule-level code even when the documented autoload opt-in is unset, reachable throughpraisonai.recipe.run()without a network service. See GHSA-hxmv-c4g6-5fqc. - Unauthenticated unbounded MCP session accumulation (DoS) via
initializewith no TTL enforcement. See GHSA-wv94-5qcp-6m36.
mcp-shell allowlist bypasses (3 GHSAs)¶
mcp-shell "secure mode" restricts shell_exec to an allowlist, but the default config and validator are bypassable:
- Git shell-alias injection. The metacharacter blocklist omits
!, so/usr/bin/git -c alias.pwn=!<cmd>runs arbitrary OS commands as the process user. Default Docker image runs asmcpuser(UID 1000) with Git installed and secure mode on — exploitable out of the box. See GHSA-74hp-mggr-hv58. - Default
/bin/bashin the allowlist. The validator only checks the first token, so/bin/bash -c <cmd>runs anything in the container. See GHSA-3x77-wg38-92r3. - Security disabled by default in the bare-binary deploy path (
Security.Enabled: falseunlessMCP_SHELL_SEC_CONFIG_FILEis set). See GHSA-f5pj-2738-996m.
Chainlit MCP stdio command injection (CVE-2026-45018)¶
With features.mcp.enabled = true (MCP is disabled by default since v2.7.0), POST /mcp for stdio transport accepts a user-controlled fullCommand. validate_mcp_command() checks only the executable name against an allowlist and does not restrict arguments, so npx -y -c '<arbitrary command>' executes arbitrary shell commands as the Chainlit process, unauthenticated. Patched in 2.12.0. See GHSA-w3fx-mc44-mf6j. The durable pattern: an allowlist that validates the command token but not its arguments/flags is not a command-execution boundary.
Operator triage for the August 25 wave¶
- Treat every "validate the URL/origin/auth then use it" sink as two separate checks. Redirect-following and DNS re-resolution invalidate a one-time host check. Probe with an owned no-content peer that 302-redirects to loopback/metadata and with a TTL=1 rebind host; stop at the callback/marker, never at real internal data.
- Search allowlists for
startswith, unanchoredre.match, and "first-token-only" logic. Origin/host/executable allowlists that compare prefixes or only the argv[0] are the bypass class. - Assume
--api-key/authflags may be no-ops. Confirm the flag is actually read by the middleware, not just parsed by the CLI. - For workspace containment, key on
realpath/resolve, notabspath. Symlinks and../are the two escapes; check that every sibling tool (not just the patched one) enforces the boundary and the approval gate. - For MCP command tools, prove the argument boundary. Show an allowlisted executable whose
-c/alias.!/-y -cflag reaches arbitrary execution; keep the marker inert (id/pwd/marker file) and lab-only.
Notes on skipped items from this scan¶
- Bazaar
bzr+sshdash-prefixed hostname command execution was updated in GitHub Advisories but is an old 2017 issue; it remains useful supply-chain history, not fresh Skillz Wiki content. - PraisonAI activity-log disclosure and several narrower dependency/label/member-management advisories were folded into the broader platform tenant-isolation/role-escalation workflow instead of each receiving a standalone page.
- Stigmem items updated around the same window were configuration-obvious, defensive-hardening, or already reviewed in the prior scan.
- CISA KEV stayed catalog
2026.05.29with PAN-OS CVE-2026-0257 already reflected. PortSwigger, ProjectDiscovery, GitHub Security Blog, Trail of Bits/feed.xml, and Disclosed had no separate promotable deltas in this pass.
October 7 follow-up: AgentMail webhook mode accepts forged unsigned message.received events — attacker-chosen sender invokes the agent (CVE-2026-61428 + CVE-2026-61436 / GHSA-qj9c-59p6-8cgx, GHSA-7c92-x8vg-4258, high)¶
PraisonAI's AgentMail webhook mode (AgentMailBot(mode="webhook") / BotConfig.mode=webhook) binds an aiohttp receiver to 0.0.0.0 and _handle_email_webhook() trusts the parsed JSON body directly: it reads body["type"] == "message.received", schedules _process_webhook_payload(), and returns 200 — with zero verification of the svix-id / svix-timestamp / svix-signature headers AgentMail documents, and the secret returned by webhooks.create() is never even stored. The forged payload builds a BotMessage from attacker-controlled data.from, data.subject, data.extracted_text, data.thread_id, and dispatches it through _handle_message() — firing message hooks, running the configured agent session with the injected text, and replying to the attacker-chosen sender address.
Durable axes for agent/email-gateway platforms:
- Alternate ingress mode = alternate authz surface — the signature only ever lives on the provider path. PraisonAI's polling and WebSocket paths receive SDK-typed, provider-verified events; only the webhook receiver parses raw JSON. Same product, same handler, three transports, one unauthenticated door. Audit rule for any bot/agent platform with a
mode=switch: test every mode's ingress independently with a forged event; the default mode's auth guarantee does not transfer. (Sibling of this wiki's REST-vs-MCP entry-point parity rule.) - The handler's own accepted field names fingerprint the forgery. The provider's verified payload uses
event_type; PraisonAI's handler acceptstype+data. When probing any webhook receiver for signature-skip, don't copy the provider's documented schema — grep the handler (or fuzz both shapes) for the field names it reads. An endpoint returning 200 to a wrong-shape-but-handler-correct body while the provider's own test ping gets ignored is the tell. - Sender-spoofed agent invocation is a prompt-injection trigger leg with a reply channel. Forged
data.from+ injectedextracted_text= attacker picks both the prompt and, via hooks/tools, potentially an outbound channel. Frame findings as unauthenticated → agent tool invocation, not just "missing signature": prove in a lab bot with an inert tool (echo/marker), check whether tools/approval gates fire, and whether the auto-reply path sends agent output back to the spoofed address. Never drive a production email agent; owned test inboxes only. - Deploy fingerprint: webhook mode is opt-in (
mode=webhook, binds a configured port on all interfaces) — externally, an open port that answersPOST <configured webhook path>with 200 to{"type":"message.received","data":{}}while never requiringsvix-signatureis the test. GitHub's July 2026 advisories GHSA-4mp6-8448-9vgv / GHSA-m64w-vfg6-36ph were withdrawn as duplicates of these — same advisory, do not double-report.
October 7 follow-up: dependency-edge delete authorized against the caller-selected endpoint issue (CVE-2026-62179 / GHSA-mxmx-rh57-jx58, medium, fixed praisonai-platform 0.1.9; vulnerable >=0.1.6 <=0.1.8)¶
DELETE /workspaces/{ws}/issues/{issue_id}/dependencies/{dep_id} loads the edge by dep_id, accepts it when either dep.issue_id == issue_id or dep.depends_on_issue_id == issue_id, then runs require_delete_permission(resource_owner_id=issue.creator_id) — but issue here is the URL issue, which the caller chose. An ordinary workspace member cannot delete a dependency edge through the owner-created issue endpoint (403), yet the same edge is deletable through the member-owned related issue endpoint (204), destroying owner-side blocks/blocked_by workflow state. The same route family lets any member create edges on owner-created issues with only require_workspace_member (201), so a member can both inject false workflow relationships into owner issues and remove owner-created ones by endpoint selection. Advisory ships a runnable PoV with a full version sweep (0.1.4 masked the bypass by broader older member-delete authorization; 0.1.6+ exposes it).
Durable axes:
- Relationship/edge resources addressed from either end: authorization on the URL side is not authorization on the edge. Where a route accepts
parent_idmatching either endpoint of a relation, the permission check must bind to the canonical/primary side (or both), never the caller-selected one. Sweep every edge-addressed route family (dependencies, labels, comments, links, memberships, shares) with the two-endpoint probe: same edge id, both legitimate parent URLs, one low-priv caller. - Fix-history differential as a discovery map. The 403 on the "obvious" endpoint (owner-issue delete) was added by an earlier advisory round (GHSA-rh39-9c67-59mh broad member-delete fix); the bypass only became interesting after that hardening narrowed the surface. When a product has an advisory history of ownership-check fixes, re-probe alternate legitimate addressing paths for the same object before concluding the class is fixed — patched-guard vs unpatched-sibling-path is the standard drift this product has now shown three times (4x6r cross-workspace IDOR, rh39 broad delete, this endpoint-selection variant).
- Decision-table fingerprint for reporting: owner-endpoint 403 vs member-endpoint 204 for the same
dep_id, plus owner list-count 0 afterwards, plus member-create 201 on an owner issue — all provable with one owner, one member, and two synthetic issues in a single disposable workspace (in-memory SQLite harness in the advisory). Prove delete-then-list only on synthetic edges; never touch live planning state.
October 7 follow-up (16:1xZ): deploy generator interpolates agents_file into generated source via f-string — config/CLI field is a code-execution input (CVE-2026-62176 / GHSA-g6j7-pffp-8whg, critical, fixed 4.6.78)¶
deploy/api.py builds the deployable Flask server source by interpolating the agents_file value directly into an f-string template (PraisonAI(agent_file="{agents_file}")), writes it to disk, and runs it via subprocess.Popen(['python', server_file]). A value like "); import os; os.system("id"); # breaks out of the string context in the generated file; the identical pattern exists in deploy/docker.py for Dockerfile generation. agents_file is reachable from CLI arguments, configuration, or an upstream API — so a repo-local config or a CI-passed parameter reaches code execution at deploy time with no validation step in between.
Durable axes:
- Codegen interpolation = injection surface, quote-context is the boundary. Any field embedded inside quotes in a generated code/template string must be tested with the target language's breakout sequence (
");+statement+comment for Python). Audit grep for any generator: f-strings/.format()/template engines that emit code, and diff which interpolations get escaping/shlex/reprtreatment vs raw. Same class as the May wave's generated-report/SSTI legs; the deploy-time executor (Popen(['python', …])) is what makes it RCE instead of stored XSS. - Supply-chain leg: in agent platforms the
agents_file/config value routinely originates from a cloned repo or a shared template. Treat "config field → generated file → executed by the deployer" as a PR-to-RCE chain and report it as such, with the injection proven in a disposable directory on your own host (advisory ships a template-echo PoC that proves the string breakout without executing anything). - This is the third distinct PraisonAI bug class published the same day (webhook signature skip, edge-addressing authz, codegen injection) — the same-product full-re-sweep rule again: after any advisory lands on a small agent framework, audit every remaining sink family, not just the reported one.
October 7 20:2xZ follow-up: Crawl4AI backend re-resolves DNS itself (SSRF guard is backend-dependent) and prompt-injection defense defaults to log-only¶
Two legs from the 20:22–20:43Z PraisonAI wave (GHSA-6g59-gm2v-qhvq / CVE-2026-61429, fixed praisonaiagents 1.6.78; GHSA-fj8f-m44g-c479 / CVE-2026-61439, fixed 4.6.78):
- Per-connection SSRF guards die when the backend is a browser. The
web_crawlDNS-rebinding/redirect bypass originally proved on the httpx/urllib backend also works with thecrawl4ai(headless Chromium via Playwright) backend, which auto-selects when installed: the validator sees one IP, Chromium re-resolves DNS and follows redirects on its own with no guard in between. Advisory is runtime-confirmed with internal-canary read-back. Durable rule for every AI tool with pluggable fetchers: validate the backend actually used, not the component name — enumerate installed optional backends (crawl4ai, playwright, selenium, requests vs httpx) because the guard is written against one fetch path and the auto-selected backend silently swaps the resolver+redirect stack. The rebinding battery (short-TTL A→127.0.0.1, multi-record round-robin, redirect-to-internal) must be replayed per backend, not per product. - Security features with default-off enforcement are log features. The "Injection Defense" layer scans six categories but the block threshold defaults to CRITICAL = 3+ simultaneous detections; a single hit in its own "dangerous" categories (instruction override, financial manipulation) logs and passes. Audit rule for shipped AI-guardrails: read the default threshold config first, then craft inputs that trip exactly one detector — detection-count-gated blocking means the defense is bypassable by paraphrase-per-category. Report as defense-bypass with the single-detector PoC the advisory ships, not as "no injection defense."
October 8 17:1xZ follow-up: second coordinated praisonaiagents wave (<=1.6.77 → 1.6.78, seven distinct legs) — approval-cache keying, sibling-class validation parity, and repo-config-to-write sinks¶
The 16:36–16:49Z wave republishes the same product at scale (each leg paired with a duplicate GHSA marker — treat the dupes as bookkeeping, not findings). New durable legs, all fixed praisonaiagents 1.6.78:
- Human-in-the-loop approval is cached by tool name only and reused for all later calls with arbitrary arguments (GHSA-29r9-67vg-qj56 / CVE-2026-60087).
ApprovalRegistry.is_already_approvedkeys a per-run contextvar set ontool_name;approve_sync/asyncshort-circuit on it before consulting the backend;clear_approvedis never called in shipped code, and the cache is inherited by child tasks. Critical-tier tools (execute_command/execute_code) are exempt —write_fileis not: one benign approval ("overwrite this scratch file?") buys silent arbitrary-path writes for the rest of the run. Audit rule for every agent framework shipping approval gates: check what the approval cache key includes — name-only keys turn one reviewed argument set into a standing grant; grep foralready_approved/is_approvedshort-circuits ahead of the backend call, and test the approve-benign-then-abuse-same-tool sequence as a first-class case. - Sibling-class validation parity:
FileTools._validate_path()exists;SkillTools.run_skill_script()never calls it (GHSA-c44f-37qr-gw3f / CVE-2026-61443). The traversal detector lives in one tool class while another tool class execsscript_pathfrom LLM-directed args with zero containment — plus the YAMLapprove:field bypasses@require_approvalfor high-risk tools. Same guard-coverage-diff rule as the October wave's HTTP-client pages: diff containment-helper invocation sites across tool classes, not helper existence; and test whether declarative config (YAMLapprove:) can pre-answer an approval gate meant to be interactive. - Repo-config-to-write sink: project-local
.praisonai/config.tomlsetsdefaults.output.output_fileto an absolute/..path andagent.start()auto-saves there withopen(...,"w"), mkdir -p included (GHSA-qjw5-xwrp-xwpq / CVE-2026-60089). Cloning an untrusted repo + running its agent = attacker-chosen file write as the developer. Repo-trust fold (May 5 agent-git page genealogy): every config field that names an output path is a write primitive, not a preference. - FastContext search tools accept absolute +
..paths outsideworkspace_path(GHSA-4xxv-6wmf-xf45 / CVE-2026-61432) — code-search/read/grep/list tools return out-of-workspace file content into model context; prompt-influenced args reach it. The "root directory" documented as a boundary was only ever a default cwd. - Two zero-gate exec sinks from repo-controlled files:
AgentFlow._resolve_pydantic_classexec_module()s a siblingtools.pywhenever a workflow step uses a stringoutput_pydantic(GHSA-4gfv-wg42-7jw5 / CVE-2026-61437) — silent, unsandboxed, ignores thePRAISONAI_ALLOW_*_TOOLSenv gates used elsewhere; and plugin auto-discoveryexec_module()s any.pyin.praisonai/plugins/with only a docstring-field "check" (GHSA-m6wp-h223-4c8g / CVE-2026-61446). Both are clone-and-run PR-to-RCE legs; the durable grep isspec_from_file_location+exec_modulewithout an allow-list, and "the env gate that guards sink A does not guard sink B/C" parity check. CodeAgentignores its ownsandbox=Trueconfig: LLM-generated code runs viapython tempfile.pysubprocess withos.environ.copy()— full parent secrets, zero AST/import validation (GHSA-2xv2-w8cq-5gxw / CVE-2026-61447, critical). Config-flag-does-not-gate-the-sink rule (same shape as the SGLang PickleWrapper fold the same day): read what the "sandbox" option actually changes in the code path before trusting it; for any agent code-exec tool, prove with a marker env var that is absent in a real sandbox.web_crawlcheck-time-vs-connection-time DNS rebinding with response read-back, runtime-confirmed with internal canary markers (GHSA-qg25-6gc4-48mg / CVE-2026-61430) — the rebinding leg already covered by the October 7 follow-up above; this is the original guard's first spelling. Any validator that callsgetaddrinfoat check time and hands the URL string to the fetcher is rebinding-testable by construction.
Meta-lesson for this product: this is the third multi-leg wave in ten days on PraisonAI (May core wave, Oct 7 five-leg wave, Oct 8 seven-leg wave) — the same-product full-re-sweep rule keeps paying: after any advisory lands here, audit all remaining sink families (approval caches, tool-class parity, config-driven paths, discovery/exec sinks, code-exec sandboxing), because each wave confirms the audit was never finished.
October 8 17:5xZ follow-up: same-sweep continuation — three more legs in the next 30 minutes (≤1.6.78)¶
The 17:1xZ seven-leg fold was not the end of the sweep; three more legs landed 17:52–17:58Z:
ContextGathererinclude resolution reads outside the workspace (GHSA-q7m5-3jmv-vm48 / CVE-2026-61431): repo-controlled.praisoncontext/.praisonincludefiles name absolute or..paths; context gathering opens them and appends contents to the model prompt/caller bundle. Clone-to-file-read via the context builder — the sibling of the config.toml write leg: every "gather project context" feature that trusts repo-supplied include lists is an exfil channel.- Custom command templates interpolate
@pathreads into prompts (GHSA-xpx6-x8c2-mw5w / CVE-2026-60088):.praisonai/commands/*.mdbodies auto-discover, and@../outside_secret.txt/@/etc/passwdexpand relative to CWD with no canonicalization. Same clone-to-read shape through a second feature (commands vs context) — the guard-parity sweep rule again: one path-containment helper, N feature loaders. - PGVector/Cassandra knowledge stores interpolate
dimensionintoCREATE TABLEDDL (GHSA-wf65-4jjx-q444 / CVE-2026-60090): schema/keyspace names get validated; the numericdimensionargument is concatenated raw — Python type hints enforce nothing at runtime. Identifier validation without value validation. On any vector-store API where you can create a collection, fuzz the dimension/count numeric fields with SQL/CQL tokens; DDL-position injection class beside this repo's Oct 6 Payload join-predicate leg.
Meta-lesson strengthened: four batches in ~90 minutes on one product = a bulk advisory train, not a wave. When the same package publishes twice in one hour, treat the first batch as a sample of the backlog, wait for the train to end (re-check the updated feed before closing the run), and sweep the whole sink family at once — this page now catalogs approval caches, tool-class parity, config-path writes, discovery exec, sandbox no-op, rebinding, context-gather reads, command-template reads, and DDL interpolation.
October 8 20:1xZ follow-up: same codegen sink, second interpolated field; Host-header localhost gate (≤4.6.78 train continues)¶
- Second field into the same generated server source (GHSA-79fv-7hq9-w7xg / CVE-2026-61433, high):
generate_api_server_code()also splicesdeploy.api.hostfromagents.yamlraw intoapp.run(host='{config.host}', ...)andagents_fileagain into route-handler expressions — a second route-time injection site beside the October 7agents_fileleg. Same rule restated: fix the interpolation discipline of a code generator once, per-field; per-field fixes leave the next raw{...}slot live. On any config-to-generated-code audit, enumerate every interpolation in the template, not just the reported field. PRAISONAI_CALL_AUTH=disabledlocalhost gate reads request-derived URL host, not socket binding (GHSA-2gpf-2492-q9jh / CVE-2026-61435, high):verify_token()admits the auth-disabled fast path whenrequest.url.hostname∈ {127.0.0.1, localhost, ::1} — spoofable viaHost:header, so an unauthenticated network caller lists/invokes registered agents. Tracked as one more instance of the Host-is-data-not-identity family (July 8 loopback control-plane page, DeepSeek Harness 8m2g leg): the gate implementation (request.url.hostnamestring compare vs socket peer) is the fingerprint. Folded here only as product state.- Prompt-defense 3+-detector threshold re-listing (GHSA-4r3p-w3mc-5v34 / CVE-2026-60086) duplicates the already-covered fj8f detection-count-gated-blocking leg — tracked, no new axis.
October 8 22:1xZ follow-up: fifth batch in ~4 hours — npm AgentOS ships unauthenticated by default, SecurityPolicy is a config object the sandbox never reads, and find -exec beats the metacharacter block¶
The 21:57–22:10Z train (each leg carrying duplicate-GHSA bookkeeping markers) added the fifth PraisonAI batch today. Durable legs:
- Middleware registered only when a secret exists (GHSA-6wjp-v33h-5cvq / CVE-2026-61426, npm AgentOS; GHSA-4w49-gwv8-fpjg / CVE-2026-60091, Jobs API; GHSA-hc5v-gxvj-58wh / CVE-2026-61427, MCP http-stream): API-key/CORS/auth middleware is conditionally registered — absent key = no middleware at all,
0.0.0.0bind +CORS *with credentials. Quickstart-served AgentOS leaks agent names/roles/system prompts viaGET /api/agentsand executes agents viaPOST /api/chat; the MCP transport exposestools/list(~50 tools) with noAuthorization, and the dispatcher forwards arguments without validating against the advertisedinputSchema— schema is documentation, not enforcement. Same-shape trio, one rule: on any agent-platform surface, the auth gate's existence can be config-conditional — fingerprint "middleware only if secret" by requesting the admin-shaped route before authenticating; system-prompt read is a first-class bounty item (prompt/IP disclosure), and MCPtools/listfrom an unauthenticated peer is the standing recon probe. SecurityPolicyfields are fiction: the default SubprocessSandbox implements none of them (GHSA-5r6c-gj4g-r697 / CVE-2026-60085):blocked_commands/blocked_paths/allow_subprocess=False/strict()are a documented data class; the shipped executor writes code to a temp file andcreate_subprocess_execs it with zero checks against the policy. The doc-claim-vs-code-path audit rule from today's CodeAgentsandbox=Trueleg, generalized: grep the enforcement sites of every security-config field; a policy object with no reader is a compliance checkbox. For assessments: configure the strictest documented policy in a lab, then prove a blocked command/path still executes — that's the report.- Allowlist bypass via
find -exec ... {} +(GHSA-cv3g-hj65-pcfh / CVE-2026-61434): the fix for the earlier shell-chaining CVEs blocks metacharacters and spawns withshell:false— butfindstays allowlisted and is itself an executable runner:-exec cmd {} +uses the+batch terminator instead of the blocked;, zero metacharacters needed. First-token allowlists must ask of every entry "does this binary embed an exec primitive?" (find -exec,awk system(),git !,less !,man,zip -pipe...). The blocklist was on shell syntax; the danger was argument semantics. Four parallel implementations of the agent stack each carried the same gap — sweep every implementation, not just the package on PyPI. - AICoder UI tools: prompt injection → arbitrary write + exec, as root in Docker (GHSA-9mp3-24cc-77mg / CVE-2026-61445, critical):
write_to_file/execute_commandexposed to the LLM with no path validation/sanitization —authorized_keys//etc/crontabwrites, containers run root. The canonical prompt-injection-to-host chain; fold on the sandbox-fiction legs above (the missing piece was never a policy engine being buggy, it was a policy engine never being wired).
Meta: five batches in one day on one product line — the bulk-train lesson from 17:5xZ reconfirmed. For wiki purposes this page is now the PraisonAI sink catalog; future legs fold here only if they add a new class (default-off auth middleware registration, allowlist-embedded-runner, doc-claim-vs-enforcement are today's additions). The platform-side legs (c78w PATCH owner-rewrite/lead-reassignment, xxgv label rewrites) are re-spellings of the already-covered either-end edge-addressing and coarse-PATCH classes — tracked, no new axis.