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.
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).
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 — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
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 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 Proof of Reproduction for CVE-2026-58166
- 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
unauthenticated multipart upload filename with ../ traversal and uploaded workflow YAML/task prompt Python code
- /ws
- POST /api/uploads/{session_id}
- POST /api/workflow/run
reproduction_steps.sh 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
Root Cause and Exploit Chain for CVE-2026-58166
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.pyandserver/services/attachment_service.py(AttachmentService.save_upload_file). The demonstrated execution sink is the real workflow runtime reached byserver/routes/execute_sync.py(POST /api/workflow/run),runtime.sdk.run_workflow, andruntime/node/executor/python_executor.py. - Affected versions: ChatDev
<= 2.2.0, represented by vulnerable checkout4fd4da603801766b14ad8788649cfc1ad21f99a6^=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.txtandbundle/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 returned404from/api/workflow/runbecause 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
- Run
bundle/repro/reproduction_steps.shwithbash. - The script:
- Reuses the prepared project cache at
<project_cache_dir>/repoor cloneshttps://github.com/OpenBMB/ChatDev.gitif absent. - Resolves the fixed commit and checks out
4fd4da603801766b14ad8788649cfc1ad21f99a6^for vulnerable runs and4fd4da603801766b14ad8788649cfc1ad21f99a6for 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
/wsWebSocket 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/runendpoint to execute attacker-controlled Python that writes a marker file underbundle/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.
- Reuses the prepared project cache at
- Expected evidence:
bundle/repro/runtime_manifest.jsonwithentrypoint_kind=api_remote,service_started=true,healthcheck_passed=true, andtarget_path_reached=true.bundle/repro/eval_summary.jsonwith 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, andresult_fixed_2.json.bundle/repro/rce_marker_vuln_1.txtandbundle/repro/rce_marker_vuln_2.txtcontainingRCE_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/runfrom 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
4fd4da603801766b14ad8788649cfc1ad21f99a6or 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.pyshim is used only to avoid a Python import-cycle in the current environment when importingruntimesubmodules; it does not modifyAttachmentService, routes, workflow execution logic, or the vulnerable/fixed behavior under test.
Variant Analysis & Alternative Triggers for CVE-2026-58166
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 toupload.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:
Parent baseline control: POSIX
../traversal filename throughAttachmentService.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.
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 multipartUploadFile.filenameto the same sink. The runtime test uses a directUploadFile-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 / filename→temp_path.open("wb"). - Vulnerable behavior: because
pathlib.Pathdiscards 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 undertemp_dir; the symlink target is not created.
- Entry point equivalence: same production route as parent,
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.pyis the only route that accepts a multipart upload and callsAttachmentService.save_upload_file().server/routes/batch.pyacceptsUploadFile, butserver/services/batch_parser.pyonly uses the upload filename to select/label CSV/XLS parsing (Path(filename).suffixand.stem), andBatchRunService._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.pyroute throughvalidate_workflow_filename()and reject traversal/absolute filenames.server/routes/tools.pyrestricts local tool filenames with an alphanumeric/underscore/dash regex plus a post-resolutionrelative_to()check.server/routes/vuegraphs.pystoresfilenameas a SQLite key rather than a filesystem path.Package/component affected: OpenBMB/ChatDev workflow server upload attachment handling, primarily
server/services/attachment_service.pyandserver/routes/uploads.py.Affected version tested: vulnerable parent commit
a6a5cda5560053136897aa44301eacc6c48d8168(4fd4da603801766b14ad8788649cfc1ad21f99a6^).Fixed/latest version tested:
4fd4da603801766b14ad8788649cfc1ad21f99a6; in this runorigin/mainresolved 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:
partialfor the vulnerable-only alternate trigger;noneas 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
- Run
bundle/vuln_variant/reproduction_steps.shwithbash. - The script:
- resolves the vulnerable commit (
4fd4da603801766b14ad8788649cfc1ad21f99a6^), fixed commit (4fd4da603801766b14ad8788649cfc1ad21f99a6), and latestorigin/mainwhen available; - creates isolated source clones under
bundle/vuln_variant/and does not mutate the repro checkout; - runs an
UploadFile-compatible driver againstAttachmentService.save_upload_file()for the parent baseline and two absolute-filename candidates; - writes structured results for vulnerable, fixed, and latest targets; and
- exits
0only if a fixed/latest bypass is reproduced, otherwise exits1after successful evaluation.
- resolves the vulnerable commit (
- Expected evidence for this run:
bundle/vuln_variant/eval_summary.jsonshowsbypass_confirmed=false,vulnerable_only_alternate_trigger_observed=true, andfixed_blocks_parent_baseline=true.bundle/vuln_variant/result_vulnerable.jsonshows the absolute filename candidates escape/delete on the vulnerable commit.bundle/vuln_variant/result_fixed.jsonshows 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.txtandbundle/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 resolvingtemp_pathand verifying it is insidetemp_dirbefore opening, even after basename sanitization. - Consider opening temp files with symlink-resistant APIs where practical.
- Separately review unauthenticated workflow execution and absolute
yaml_filehandling inPOST /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.mdor 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.
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/nulltotal 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 .
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{"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]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{"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]Artifacts and Evidence for CVE-2026-58166
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-58166
FAQ: CVE-2026-58166
How does the ChatDev path-traversal-to-RCE chain work?
Which ChatDev versions are affected by CVE-2026-58166?
How severe is CVE-2026-58166?
How can I reproduce CVE-2026-58166?
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.