Skip to content

CVE-2026-58166: Verified Repro With Script Download

CVE-2026-58166: OpenBMB ChatDev unauthenticated path traversal file upload

CVE-2026-58166 is verified against OpenBMB/ChatDev · github. Affected versions: ChatDev <= 2.2.0 (v2.2.0 is latest release dated Mar 23, 2026). Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00238.

REPRO-2026-00238 OpenBMB/ChatDev · github RCE Variant found Jul 6, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.1
Confidence
HIGH
Reproduced in
28m 27s
Tool calls
275
Spend
$9.38
01 · Overview

What Is CVE-2026-58166?

CVE-2026-58166 is a critical, unauthenticated path traversal (CWE-22) file-upload vulnerability in OpenBMB ChatDev's workflow server that Pruva chained into remote code execution (reproduction REPRO-2026-00238).

02 · Severity & CVSS

CVE-2026-58166 Severity & CVSS Score

CVE-2026-58166 is rated critical severity, with a CVSS base score of 9.1 out of 10.

CRITICAL threat level
9.1 / 10 CVSS base
03 · Affected Versions

Affected OpenBMB/ChatDev Versions

OpenBMB/ChatDev · github versions ChatDev <= 2.2.0 (v2.2.0 is latest release dated Mar 23, 2026) are affected.

How to Reproduce CVE-2026-58166

$ pruva-verify REPRO-2026-00238
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00238/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-58166

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

unauthenticated multipart upload filename with ../ traversal and uploaded workflow YAML/task prompt Python code

Attack chain
  1. /ws
  2. POST /api/uploads/{session_id}
  3. POST /api/workflow/run
Runnable proof: reproduction_steps.sh
Captured evidence: server fixed 1driver fixed 1server fixed 2driver fixed 2
Variants tested

Absolute multipart filenames are a same-root-cause alternate trigger for ChatDev's vulnerable upload filename path join, but the fixed/latest commit blocks them by reducing filenames to basenames before filesystem writes. No fixed-version bypass was confirmed.

How the agent worked 566 events · 275 tool calls · 28 min
28 minDuration
275Tool calls
118Reasoning steps
566Events
1Dead-ends
Agent activity over 28 min
Support
13
Repro
377
Judge
54
Variant
117
0:0028:27

Root Cause and Exploit Chain for CVE-2026-58166

Versions: ChatDev <= 2.2.0, represented by vulnerable checkout 4fd4da603801766b14ad8788649cfc1ad21f99a6^ = a6a5cda5560053136897aa44301eacc6c48d8168.Fixed: version/commit: 4fd4da603801766b14ad8788649cfc1ad21f99a6, which adds upload filename basename sanitization via _safe_upload_filename().

OpenBMB ChatDev through 2.2.0 exposes an unauthenticated file-upload path traversal in the product workflow server. The original POST /api/uploads/{session_id} route passes the raw multipart filename into AttachmentService.save_upload_file(), which joins it under a temporary upload directory without sanitization at the vulnerable commit. A remote unauthenticated attacker can first mint a session through the product /ws WebSocket, then upload a filename containing ../ path traversal segments. In this run, that primitive was chained on the original ChatDev product surface into code execution: the upload wrote a malicious workflow YAML outside the intended upload directory, and the product POST /api/workflow/run endpoint then executed attacker-controlled Python through ChatDev's real workflow python node runtime.

  • Package/component affected: OpenBMB/ChatDev workflow server, specifically server/routes/uploads.py and server/services/attachment_service.py (AttachmentService.save_upload_file). The demonstrated execution sink is the real workflow runtime reached by server/routes/execute_sync.py (POST /api/workflow/run), runtime.sdk.run_workflow, and runtime/node/executor/python_executor.py.
  • Affected versions: ChatDev <= 2.2.0, represented by vulnerable checkout 4fd4da603801766b14ad8788649cfc1ad21f99a6^ = a6a5cda5560053136897aa44301eacc6c48d8168.
  • Fixed version/commit: 4fd4da603801766b14ad8788649cfc1ad21f99a6, which adds upload filename basename sanitization via _safe_upload_filename().
  • Risk level and consequences: Critical. A remote unauthenticated attacker can write/delete files reachable by the server process via traversal. This run shows that the write primitive can be chained into product-level code execution by writing a workflow YAML to a traversed path and invoking ChatDev's workflow execution API to run attacker-controlled Python.

Impact Parity

  • Disclosed/claimed maximum impact: Remote code execution (code_execution) after unauthenticated path traversal upload.
  • Reproduced impact from this run: Full code execution on the original product API surface. Two vulnerable attempts created attacker-controlled marker files (bundle/repro/rce_marker_vuln_1.txt and bundle/repro/rce_marker_vuln_2.txt) from Python code executed by ChatDev's real workflow runtime after the malicious YAML was written via the unauthenticated upload path. Two fixed-commit attempts sanitized the filename, did not create the traversed YAML target, and returned 404 from /api/workflow/run because the target YAML file was not written.
  • Parity: full.
  • Not demonstrated: No privilege escalation or sandbox escape beyond the privileges of the ChatDev server process. The RCE demonstrated is arbitrary Python execution within the server's normal workflow execution environment.

Root Cause

At the vulnerable checkout, AttachmentService.save_upload_file() trusts UploadFile.filename and joins it directly onto a temporary directory:

filename = upload.filename or "upload.bin"
temp_dir = Path(tempfile.mkdtemp(prefix="mac_upload_"))
temp_path = temp_dir / filename
with temp_path.open("wb") as buffer:
    ...

Because Python path joining preserves ../ segments, a multipart filename such as ../../../../tmp/chatdev_cve58166_vuln_1_link.yaml escapes the temporary upload directory. The product upload route is reachable remotely at POST /api/uploads/{session_id} and only requires a known session id. The reproduction obtains that session without authentication by opening the product /ws WebSocket; the server returns a fresh UUID session and records it in active_connections, after which ensure_known_session(..., require_connection=False) accepts uploads for that session.

The vulnerable function also removes temp_path in a finally block. A direct traversal write is therefore deleted after registration, but the reproduction uses a symlink at the traversed path. open("wb") follows the symlink and writes the malicious YAML to the symlink target, while unlink() removes only the symlink. The resulting YAML file persists outside the upload directory. The product /api/workflow/run endpoint accepts an absolute yaml_file path and runs the referenced workflow; the uploaded YAML contains a single ChatDev python node, causing PythonNodeExecutor to execute attacker-supplied Python from task_prompt with subprocess.run().

The fix commit 4fd4da603801766b14ad8788649cfc1ad21f99a6 adds AttachmentService._safe_upload_filename(), normalizing path separators and reducing the multipart filename to a safe basename before joining it with the temporary directory. In the fixed negative controls, the same traversal filename is returned as chatdev_cve58166_fixed_*_link.yaml, the /tmp/...fixed_*.yaml target is not created, and the follow-on workflow execution cannot find the malicious YAML.

Fix commit: https://github.com/OpenBMB/ChatDev/commit/4fd4da603801766b14ad8788649cfc1ad21f99a6

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh with bash.
  2. The script:
    • Reuses the prepared project cache at <project_cache_dir>/repo or clones https://github.com/OpenBMB/ChatDev.git if absent.
    • Resolves the fixed commit and checks out 4fd4da603801766b14ad8788649cfc1ad21f99a6^ for vulnerable runs and 4fd4da603801766b14ad8788649cfc1ad21f99a6 for fixed runs.
    • Starts the original product server via server_main.py --host 127.0.0.1 --port <port> and waits for /health.
    • Opens the real /ws WebSocket to mint an unauthenticated session.
    • Sends a real multipart POST /api/uploads/{session_id} request with a traversal filename targeting a symlink under /tmp.
    • In vulnerable runs, persists a malicious workflow YAML outside the upload directory and then calls the real POST /api/workflow/run endpoint to execute attacker-controlled Python that writes a marker file under bundle/repro/.
    • In fixed runs, verifies the same request is sanitized, the traversed YAML target is not created, and the workflow run fails closed with no marker file.
  3. Expected evidence:
    • bundle/repro/runtime_manifest.json with entrypoint_kind=api_remote, service_started=true, healthcheck_passed=true, and target_path_reached=true.
    • bundle/repro/eval_summary.json with both vulnerable code-execution attempts true and both fixed-blocked attempts true.
    • bundle/repro/result_vuln_1.json, result_vuln_2.json, result_fixed_1.json, and result_fixed_2.json.
    • bundle/repro/rce_marker_vuln_1.txt and bundle/repro/rce_marker_vuln_2.txt containing RCE_OK_FROM_CHATDEV_WORKFLOW ....
    • Runtime logs in bundle/logs/.

Evidence

The script was run twice consecutively and succeeded both times. The final run produced:

{
  "vuln_attempts_code_execution": [true, true],
  "vuln_attempts_persistent_yaml_write": [true, true],
  "vuln_response_names": [
    "../../../../../../../../../../../../../../../../tmp/chatdev_cve58166_vuln_1_link.yaml",
    "../../../../../../../../../../../../../../../../tmp/chatdev_cve58166_vuln_2_link.yaml"
  ],
  "vuln_marker_contents": [
    "RCE_OK_FROM_CHATDEV_WORKFLOW role=vuln attempt=1\n",
    "RCE_OK_FROM_CHATDEV_WORKFLOW role=vuln attempt=2\n"
  ],
  "fixed_attempts_blocked_chain": [true, true],
  "fixed_response_names": [
    "chatdev_cve58166_fixed_1_link.yaml",
    "chatdev_cve58166_fixed_2_link.yaml"
  ],
  "fixed_target_created": [false, false],
  "confirmed": true
}

Important artifact locations:

  • Main proof log: bundle/logs/reproduction_steps.log.
  • Vulnerable server logs: bundle/logs/server_vuln_1.log, bundle/logs/server_vuln_2.log.
  • Vulnerable driver logs: bundle/logs/driver_vuln_1.log, bundle/logs/driver_vuln_2.log.
  • Fixed server logs: bundle/logs/server_fixed_1.log, bundle/logs/server_fixed_2.log.
  • Fixed driver logs: bundle/logs/driver_fixed_1.log, bundle/logs/driver_fixed_2.log.
  • Structured runtime evidence: bundle/repro/runtime_manifest.json.
  • Marker files proving attacker-controlled Python execution: bundle/repro/rce_marker_vuln_1.txt, bundle/repro/rce_marker_vuln_2.txt.

Example vulnerable result (bundle/repro/result_vuln_1.json): the upload response echoed the raw traversal filename, the traversed YAML target existed after upload, the symlink was removed, /api/workflow/run returned HTTP 200 with final_message RCE_MARKER_WRITTEN role=vuln attempt=1, and the marker contained RCE_OK_FROM_CHATDEV_WORKFLOW role=vuln attempt=1.

Example fixed result (bundle/repro/result_fixed_1.json): the upload response name was sanitized to chatdev_cve58166_fixed_1_link.yaml, the traversed YAML target did not exist, /api/workflow/run returned 404 (YAML file not found), and no marker file was created.

Recommendations / Next Steps

  • Keep the fixed _safe_upload_filename() behavior: normalize both POSIX and Windows separators, strip directory components, and reject or replace empty/special names such as ., .., and blank filenames.
  • Consider adding defense in depth to save_upload_file() by resolving the final path and verifying it remains under the temporary directory before opening it.
  • Avoid following symlinks for temporary upload writes where possible, or create temp files with safe APIs that prevent symlink traversal.
  • Restrict /api/workflow/run from accepting arbitrary absolute YAML paths unless this is an explicit trusted-admin feature. Prefer resolving workflow files only under the configured workflow directory.
  • Add end-to-end regression tests that send multipart filenames with ../, absolute paths, Windows separators, symlink targets, and the RCE chain pattern demonstrated here.
  • Upgrade users to a version containing commit 4fd4da603801766b14ad8788649cfc1ad21f99a6 or later.

Additional Notes

  • The reproduction is idempotent: the script removes old /tmp/chatdev_cve58166_* files and marker files before each attempt, chooses free local ports, and runs two vulnerable and two fixed attempts per invocation.
  • The script uses the original product server entrypoint (server_main.py) and real product routes. It does not reimplement the vulnerable upload handler or workflow execution sink.
  • A small sitecustomize.py shim is used only to avoid a Python import-cycle in the current environment when importing runtime submodules; it does not modify AttachmentService, routes, workflow execution logic, or the vulnerable/fixed behavior under test.

Variant Analysis & Alternative Triggers for CVE-2026-58166

Versions: version tested: vulnerable parent commit a6a5cda5560053136897aa44301eacc6c48d8168 (4fd4da603801766b14ad8788649cfc1ad21f99a6^).Fixed: /latest version tested: 4fd4da603801766b14ad8788649cfc1ad21f99a6; in this run origin/main resolved to the same commit.

No fixed-version bypass was confirmed. The variant analysis found one same-sink, vulnerable-only alternate filename form: an absolute multipart filename reaches the same AttachmentService.save_upload_file() path-join flaw as the parent ../ traversal on vulnerable commit a6a5cda5560053136897aa44301eacc6c48d8168. However, the fixed commit 4fd4da603801766b14ad8788649cfc1ad21f99a6 reduces client-controlled filenames to a basename before joining with the temporary upload directory, so both the parent ../ traversal and the absolute-filename alternate trigger are blocked on the fixed/latest tested source. The stage therefore records a negative bypass verdict with a bounded candidate matrix.

Fix Coverage / Assumptions

The original fix relies on the invariant that AttachmentService.save_upload_file() never joins attacker-controlled directory components into temp_dir / filename. Commit 4fd4da603801766b14ad8788649cfc1ad21f99a6 adds AttachmentService._safe_upload_filename(raw), which:

  • replaces Windows backslashes with POSIX separators,
  • applies os.path.basename(...),
  • trims whitespace,
  • maps empty, ., and .. names to upload.bin, and
  • uses the sanitized basename for the temporary write and attachment display name.

This explicitly covers the vulnerable upload route POST /api/uploads/{session_id} because server/routes/uploads.py::upload_attachment() calls only manager.attachment_service.save_upload_file(session_id, file) for multipart uploads. Source scanning found no other server-side multipart endpoint that calls save_upload_file().

The fix does not attempt to change unrelated high-risk product behavior such as unauthenticated session creation over /ws, arbitrary absolute yaml_file handling in POST /api/workflow/run, or local-tool/workflow authoring APIs. Those behaviors may deserve separate hardening, but they are not alternate ways to reach the same upload filename path traversal sink.

Target threat-model review: no SECURITY.md or explicit repository threat model file was present in the tested checkout. The product documentation exposes Web UI and HTTP workflow execution surfaces, and no documentation was found that excludes unauthenticated upload path traversal from security scope.

Variant / Alternate Trigger

Tested candidates:

  1. Parent baseline control: POSIX ../ traversal filename through AttachmentService.save_upload_file().

    • Vulnerable commit: escaped the temporary upload directory via a symlink target and deleted the supplied symlink path.
    • Fixed/latest commit: blocked; response/record basename was sanitized.
  2. Candidate alternate trigger: absolute multipart filename with symlink target.

    • Entry point equivalence: same production route as parent, POST /api/uploads/{session_id}, because the route passes multipart UploadFile.filename to the same sink. The runtime test uses a direct UploadFile-compatible object against the service sink for deterministic side-by-side source checks; the parent RCA already validated the remote API route to this sink.
    • Code path: server/routes/uploads.py::upload_attachment()server/services/attachment_service.py::AttachmentService.save_upload_file()temp_path = temp_dir / filenametemp_path.open("wb").
    • Vulnerable behavior: because pathlib.Path discards the left-hand side when the right-hand operand is absolute, temp_dir / "/attacker/path" writes to the attacker-specified absolute path. With a symlink at that path, the write follows the symlink and persists data outside the temp directory while cleanup removes only the symlink.
    • Fixed/latest behavior: _safe_upload_filename() converts the absolute path to a basename, so the write stays under temp_dir; the symlink target is not created.
  3. Candidate alternate trigger: absolute multipart filename pointing at an existing file.

    • Same sink and same absolute-path semantics as candidate 2.
    • Vulnerable behavior: the existing target is overwritten and then removed by the cleanup unlink().
    • Fixed/latest behavior: the existing target remains unchanged; sanitized basename is stored under the temp/attachment workspace.

Bounded source scan results:

  • server/routes/uploads.py is the only route that accepts a multipart upload and calls AttachmentService.save_upload_file().

  • server/routes/batch.py accepts UploadFile, but server/services/batch_parser.py only uses the upload filename to select/label CSV/XLS parsing (Path(filename).suffix and .stem), and BatchRunService._sanitize_label() constrains derived task directories. It does not join the raw multipart filename into an arbitrary write path.

  • Workflow CRUD endpoints in server/routes/workflows.py route through validate_workflow_filename() and reject traversal/absolute filenames.

  • server/routes/tools.py restricts local tool filenames with an alphanumeric/underscore/dash regex plus a post-resolution relative_to() check.

  • server/routes/vuegraphs.py stores filename as a SQLite key rather than a filesystem path.

  • Package/component affected: OpenBMB/ChatDev workflow server upload attachment handling, primarily server/services/attachment_service.py and server/routes/uploads.py.

  • Affected version tested: vulnerable parent commit a6a5cda5560053136897aa44301eacc6c48d8168 (4fd4da603801766b14ad8788649cfc1ad21f99a6^).

  • Fixed/latest version tested: 4fd4da603801766b14ad8788649cfc1ad21f99a6; in this run origin/main resolved to the same commit.

  • Risk level and consequences: On vulnerable versions, the absolute-filename form has the same arbitrary file write/delete primitive as the parent bug and can support the same RCE chain if delivered through the unauthenticated upload route. On the fixed/latest commit, the tested candidates do not escape the upload temp directory.

Impact Parity

  • Disclosed/claimed maximum impact for parent: remote code execution after unauthenticated upload path traversal.
  • Reproduced impact from this variant run: vulnerable-only arbitrary file write/delete at the same sink using an absolute filename. The fixed/latest commit blocked this behavior.
  • Parity: partial for the vulnerable-only alternate trigger; none as a fixed-version bypass.
  • Not demonstrated: No code execution was demonstrated for the absolute-filename candidate in the variant stage because the purpose was to test fix coverage. The parent reproduction already demonstrated the RCE chain for the same sink on vulnerable code. No fixed-version write/delete/RCE bypass was observed.

Root Cause

The underlying vulnerable root cause is the same as the parent issue: pre-fix AttachmentService.save_upload_file() trusted UploadFile.filename and joined it into a server-side temporary path before opening it for writing. In Python pathlib, both ../ components and absolute right-hand operands can escape a base directory when joined naively. The parent bug used ../ traversal; the variant candidate used an absolute filename. Both exploit the same missing basename/path-confinement invariant before temp_path.open("wb") and cleanup temp_path.unlink().

The fixed commit closes this class for upload filenames by reducing all slash/backslash-containing input to a final basename before any path join. Therefore, the alternate absolute-filename candidate is not a bypass of commit 4fd4da603801766b14ad8788649cfc1ad21f99a6.

Fix commit: https://github.com/OpenBMB/ChatDev/commit/4fd4da603801766b14ad8788649cfc1ad21f99a6

Reproduction Steps

  1. Run bundle/vuln_variant/reproduction_steps.sh with bash.
  2. The script:
    • resolves the vulnerable commit (4fd4da603801766b14ad8788649cfc1ad21f99a6^), fixed commit (4fd4da603801766b14ad8788649cfc1ad21f99a6), and latest origin/main when available;
    • creates isolated source clones under bundle/vuln_variant/ and does not mutate the repro checkout;
    • runs an UploadFile-compatible driver against AttachmentService.save_upload_file() for the parent baseline and two absolute-filename candidates;
    • writes structured results for vulnerable, fixed, and latest targets; and
    • exits 0 only if a fixed/latest bypass is reproduced, otherwise exits 1 after successful evaluation.
  3. Expected evidence for this run:
    • bundle/vuln_variant/eval_summary.json shows bypass_confirmed=false, vulnerable_only_alternate_trigger_observed=true, and fixed_blocks_parent_baseline=true.
    • bundle/vuln_variant/result_vulnerable.json shows the absolute filename candidates escape/delete on the vulnerable commit.
    • bundle/vuln_variant/result_fixed.json shows the same candidates do not escape/delete on the fixed commit.

Evidence

Primary logs and outputs:

  • Main run log: bundle/logs/vuln_variant_reproduction_steps.log.
  • Vulnerable candidate log: bundle/logs/vuln_variant_driver_vulnerable.log.
  • Fixed candidate log: bundle/logs/vuln_variant_driver_fixed.log.
  • Structured summary: bundle/vuln_variant/eval_summary.json.
  • Structured vulnerable evidence: bundle/vuln_variant/result_vulnerable.json.
  • Structured fixed evidence: bundle/vuln_variant/result_fixed.json.
  • Tested revision records: bundle/logs/vuln_variant_fixed_version.txt and bundle/logs/vuln_variant_latest_version.txt.

Key final-run summary:

{
  "vulnerable_commit": "a6a5cda5560053136897aa44301eacc6c48d8168",
  "fixed_commit": "4fd4da603801766b14ad8788649cfc1ad21f99a6",
  "latest_commit": "4fd4da603801766b14ad8788649cfc1ad21f99a6",
  "bypass_confirmed": false,
  "vulnerable_only_alternate_trigger_observed": true,
  "fixed_blocks_parent_baseline": true
}

For candidate_absolute_filename_symlink, the vulnerable result has escaped_temp_dir_via_write=true and deleted_supplied_path=true. The fixed result has escaped_temp_dir_via_write=false, deleted_supplied_path=false, and a sanitized record name fixed_candidate_absolute_filename_symlink_link.txt.

For candidate_absolute_filename_delete, the vulnerable result has deleted_supplied_path=true. The fixed result has deleted_supplied_path=false and target_preserved_original=true.

The script was run twice consecutively. Both runs completed deterministically and exited 1, which is the expected negative-bypass result for this variant stage.

Recommendations / Next Steps

  • Keep _safe_upload_filename() as a shared invariant for every path that accepts multipart upload filenames.
  • Add regression tests for absolute multipart filenames in addition to ../, Windows backslashes, blank, ., and .. names. The current fix logic should pass these tests.
  • Consider defense-in-depth in save_upload_file() by resolving temp_path and verifying it is inside temp_dir before opening, even after basename sanitization.
  • Consider opening temp files with symlink-resistant APIs where practical.
  • Separately review unauthenticated workflow execution and absolute yaml_file handling in POST /api/workflow/run; these are not bypasses of this upload filename fix, but the parent RCE chain shows they materially increase impact when combined with any file-write primitive.

Additional Notes

  • The variant script uses isolated source clones under bundle/vuln_variant/ and does not leave the project cache/repro checkout in a different state.
  • The direct service-sink test is appropriate for fix-coverage analysis because the parent reproduction already proved the remote unauthenticated API path reaches AttachmentService.save_upload_file() unchanged.
  • No repository SECURITY.md or explicit threat model document was found in the tested source tree.

CVE-2026-58166 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:59
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-58166 · REPRO-20
0:02
0:04
web search
0:05
web search
0:08
0:10
0:25
0:25
extract_facts
no facts extracted
0:26
0:27
0:27
supportrepro
0:28
0:28
0:28
0:29
0:29
0:29
0:31
0:31
$ls -la /data/pruva/project-cache/cb19670c-9322-4afb-8baf-ad6441138574/ 2>/dev/null; echo "---REPO---"; ls -la /data/pruva/project-cache/cb19670c-9322-4afb-8baf-ad6441138574/repo 2>/dev/null | head -30; echo "---MIRRORS---"; ls -la /data/pruva/project-cache/cb19670c-9322-4afb-8baf-ad6441138574/repo-mirrors 2>/dev/null
total 0
drwxr-xr-x 1 vscode vscode   24 Jul  5 14:44 .
drwxr-xr-x 1 vscode vscode 4558 Jul  5 14:44 ..
drwxr-xr-x 1 vscode vscode    0 Jul  5 14:44 repo-mirrors
---REPO---
---MIRRORS---
total 0
drwxr-xr-x 1 vscode vscode  0 Jul  5 14:44 .
0:33
web search
0:37
0:37
0:37
$curl -sL https://github.com/OpenBMB/ChatDev/commit/4fd4da603801766b14ad8788649cfc1ad21f99a6.patch -o /tmp/fix.patch && echo "=== PATCH SIZE ===" && wc -l /tmp/fix.patch && echo "=== PATCH ===" && cat /tmp/fix.patch
0.3s
{"cwd":"/data/pruva/runs/3283e9ad-873d-4160-b2a4-c262fdf0e854","duration_ms":266,"exit_code":0,"stderr":"","stdout":"=== PATCH SIZE ===\n141 /tmp/fix.patch\n=== PATCH ===\nFrom 0014dbba7c6c3278091285bd7ea0e96154ad3a15 Mon Sep 17 00:00:00 2001\nFrom: swaylq <5630262+swaylq@users.noreply.github.com>\nDate: Tue, 16 Jun 2026 13:22:34 +0800\nSubject: [PATCH] fix: sanitize upload filename to prevent path traversal\n (#638)\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n\n`AttachmentService.save_upload_file` joined the client-supplied multipart\nfilename onto a fresh temp directory without sanitization. A filename with\n`../` segments escaped the temp dir, so the upload wrote attacker-controlled\nbytes to an arbitrary host path and the `finally` cleanup `unlink` then\ndeleted that same traversed path — arbitrary file write and delete behind an\nendpoint that only requires an unauthenticated session id.\n\nReduce the filename to its basename (normalising POSIX and Windows\nseparators, falling back to `upload.bin` for empty/`.`/`..`) before building\nthe temp path, so writes stay confined to the temp directory.\n\nAdds regression tests: a param… [truncated]
0:43
0:44
0:45
$echo "=== ISSUE 638 ==="; curl -sL "https://api.github.com/repos/OpenBMB/ChatDev/issues/638" 2>/dev/null | jq -r '{title, state, body}' 2>/dev/null | head -120; echo; echo "=== PR 641 ==="; curl -sL "https://api.github.com/repos/OpenBMB/ChatDev/pulls/641" 2>/dev/null | jq -r '{title, state, merged, body, base: .base.ref, merge_commit_sha}' 2>/dev/null | head -60
0.8s
{"cwd":"/data/pruva/runs/3283e9ad-873d-4160-b2a4-c262fdf0e854","duration_ms":825,"exit_code":0,"stderr":"","stdout":"=== ISSUE 638 ===\n{\n  \"title\": \"Arbitrary file write and delete via unsanitized upload filename in the ChatDev workflow server\",\n  \"state\": \"closed\",\n  \"body\": \"\\n\\nThe ChatDev workflow server saves uploaded files to a path built by joining a freshly created temporary directory with the client-supplied multipart filename, with no sanitization. A filename containing parent-directory segments escapes the temporary directory and writes attacker-controlled bytes to an arbitrary location on the host. The function's cleanup step then unlinks the same traversed path, so the upload also deletes an arbitrary file. The endpoint requires only a known session id, which is minted without authentication. Confirmed by calling the real `save_upload_file` with a traversal filename: the file was written outside the temp directory, and an existing victim file was deleted.\\n\\n### Details\\n\\n`server/services/attachment_service.py` (`save_upload_file`, around lines 46 to 80):\\n\\n```python\\nfilename = upload.filename or \\\"upload.bin\\\"\\ntemp_dir = Path(tempfile.… [truncated]
0:59

Artifacts and Evidence for CVE-2026-58166

Scripts, logs, diffs, and output captured during the reproduction.

bundle/artifact_promotion_manifest.json13.9 KB
bundle/vuln_variant/source_identity.json1.1 KB
bundle/vuln_variant/root_cause_equivalence.json1.0 KB
bundle/repro/reproduction_steps.sh18.3 KB
bundle/repro/runtime_manifest.json1.6 KB
bundle/repro/validation_verdict.json0.7 KB
bundle/repro/rca_report.md10.4 KB
bundle/repro/result_vuln.json1.9 KB
bundle/logs/reproduction_steps.log3.8 KB
bundle/repro/eval_summary.json0.7 KB
bundle/repro/result_vuln_1.json3.0 KB
bundle/repro/result_fixed_1.json1.8 KB
bundle/repro/real_product_rce_driver.py5.9 KB
bundle/logs/server_vuln_1.log0.0 KB
bundle/logs/driver_vuln_1.log0.4 KB
bundle/logs/server_vuln_2.log0.0 KB
bundle/logs/driver_vuln_2.log0.4 KB
bundle/repro/result_vuln_2.json3.0 KB
bundle/logs/server_fixed_1.log0.0 KB
bundle/logs/driver_fixed_1.log0.3 KB
bundle/logs/server_fixed_2.log0.0 KB
bundle/logs/driver_fixed_2.log0.3 KB
bundle/repro/result_fixed_2.json1.8 KB
bundle/repro/rce_marker_vuln_1.txt0.0 KB
bundle/repro/rce_marker_vuln_2.txt0.0 KB
bundle/vuln_variant/reproduction_steps.sh12.5 KB
bundle/vuln_variant/validation_verdict.json3.6 KB
bundle/vuln_variant/variant_manifest.json4.2 KB
bundle/logs/vuln_variant_reproduction_steps.log12.8 KB
bundle/vuln_variant/eval_summary.json2.2 KB
bundle/vuln_variant/rca_report.md11.5 KB
bundle/vuln_variant/patch_analysis.md6.9 KB
bundle/vuln_variant/runtime_manifest.json1.0 KB
bundle/logs/vuln_variant_driver_vulnerable.log5.2 KB
bundle/logs/vuln_variant_driver_fixed.log4.7 KB
bundle/vuln_variant/result_vulnerable.json5.2 KB
bundle/vuln_variant/result_fixed.json4.7 KB
bundle/vuln_variant/result_latest.json4.7 KB
08 · How to Fix

How to Fix CVE-2026-58166

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-58166 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-58166

How does the ChatDev path-traversal-to-RCE chain work?

An unauthenticated attacker mints a session over ChatDev's /ws WebSocket, then uploads a filename containing ../ segments to write a malicious workflow YAML outside the intended upload directory; the product's POST /api/workflow/run endpoint then executes attacker-controlled Python through ChatDev's real workflow python node runtime (runtime/node/executor/python_executor.py).

Which ChatDev versions are affected by CVE-2026-58166?

ChatDev <= 2.2.0 is affected (v2.2.0 was the latest release, dated Mar 23, 2026); the reproduction verified the vulnerable checkout at commit 4fd4da603801766b14ad8788649cfc1ad21f99a6^.

How severe is CVE-2026-58166?

It is rated critical - an unauthenticated attacker can traverse outside the upload directory and reach code execution via the workflow runtime, with no authentication required.

How can I reproduce CVE-2026-58166?

Download the verified script from this page and run it in an isolated environment against ChatDev <=2.2.0; it establishes a session via /ws, uploads a filename with path-traversal sequences to plant a malicious workflow YAML outside the upload directory, then triggers POST /api/workflow/run to execute attacker-controlled Python.
11 · References

References for CVE-2026-58166

Authoritative sources for CVE-2026-58166 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.