# REPRO-2026-00140: zenshin: OS command injection in /stream-to-vlc url query parameter ## Summary Status: published Severity: critical Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00140 CVE: CVE-2026-37281 ## Package Name: Unknown Ecosystem: Unknown Affected: Unknown Fixed: Unknown ## Root Cause # RCA Report — CVE-2026-37281 ## Summary `zenshin` is an Electron application with an embedded Express HTTP backend. The backend route `GET /stream-to-vlc` takes a `url` query parameter and interpolates it unsanitized into a shell command executed via `child_process.exec`. A crafted `url` value containing shell metacharacters (e.g., a closing quote followed by a command separator) allows arbitrary command execution on the host, resulting in unauthenticated remote code execution for any actor that can reach the backend port. ## Impact - **Package/component affected**: `zenshin` (Electron/Express backend) - **Affected versions**: `< 2.7.0` - **Fixed version**: `2.7.0` (commit `7d31c6edfbac978f0ad44c66d761bab9dcd2fa27`) - **Risk level**: Critical — CVSS 3.1 base 9.8 - **Consequences**: Unauthenticated attackers on the local network (or any host that can reach the Express port) can execute arbitrary OS commands as the user running zenshin. ## Root Cause In the vulnerable commit (`7d31c6edfbac978f0ad44c66d761bab9dcd2fa27~1`), the `/stream-to-vlc` route in `Electron/zenshin-electron/src/main/index.js` constructs a shell command by directly interpolating the user-supplied `url` query parameter: ```javascript const vlcPath = '"C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe"' const vlcCommand = `${vlcPath} "${url}"` exec(vlcCommand, (error) => { ... }) ``` Because `exec` invokes `/bin/sh -c` (or `cmd.exe /c` on Windows), any shell metacharacters in `url` are interpreted by the shell. An attacker can break out of the double-quote wrapper with a payload such as `x"; touch /tmp/pwned; echo "x`, causing the shell to execute the injected `touch /tmp/pwned` command after the (non-existent) VLC executable fails. The fix commit (`7d31c6edfbac978f0ad44c66d761bab9dcd2fa27`) completely removes the `/stream-to-vlc` Express endpoint and replaces the `ipcMain` handler with `spawn`, passing the player path and URL as separate array arguments with `shell: false`, which prevents shell interpretation entirely. ## Reproduction Steps The reproduction script is `repro/reproduction_steps.sh`. It performs the following steps: 1. Checks out the vulnerable commit (`7d31c6edfbac978f0ad44c66d761bab9dcd2fa27~1`). 2. Installs dependencies and builds the project with `npm run build` inside `Electron/zenshin-electron`. 3. Runs the actual built `out/main/index.js` with a Node.js mock harness that stubs `electron`, `@electron-toolkit/utils`, and `electron-deeplink`, allowing the Express backend to start without a real Electron runtime. 4. Sends a crafted `GET /stream-to-vlc?url=...` request with the payload `x"; touch /tmp/pwned; echo "x`. 5. Verifies that `/tmp/pwned` is created, proving arbitrary command execution. 6. Repeats steps 1–5 with the fixed commit (`7d31c6edfbac978f0ad44c66d761bab9dcd2fa27`). 7. Verifies that the endpoint returns `404 Cannot GET /stream-to-vlc` and `/tmp/pwned` is NOT created. ## Evidence - **Vulnerable commit result**: `statusCode=200`, `responseBody="VLC launched successfully"`, `pwned_exists=true` - **Fixed commit result**: `statusCode=404`, `responseBody="Cannot GET /stream-to-vlc"`, `pwned_exists=false` - **Log files**: - `logs/vulnerable.json` — HTTP response and sentinel file state for the vulnerable build - `logs/fixed.json` — HTTP response and sentinel file state for the fixed build - `logs/run.log` — full build and execution logs - **Runtime manifest**: `repro/runtime_manifest.json` captures the structured verdict. ## Recommendations / Next Steps - **Fix approach**: Remove or replace `exec` with `spawn` using an array of arguments and `shell: false`, as the upstream fix does. This prevents the shell from parsing user input. - **Input validation**: If the endpoint must be retained, strictly validate the `url` parameter against an allow-list of safe stream URL patterns before passing it to any process launcher. - **Network binding**: Bind the Express backend to `127.0.0.1` only (also done in the fix) to reduce the attack surface. - **Testing**: Add integration tests that attempt shell injection against `/stream-to-vlc` and similar endpoints to prevent regressions. ## Additional Notes - **Idempotency**: The reproduction script was run twice consecutively with identical results on both runs. - **Edge cases / limitations**: The reproduction uses a mock Electron environment because a real Electron GUI cannot run in a headless CI/container environment. The mock stubs only Electron-specific APIs (window creation, IPC, deeplinks); the Express backend and the vulnerable route handler are the actual transpiled code from the project build. ## Reproduction Details Reproduced: 2026-05-22T10:22:33.455Z Duration: 1683 seconds Tool calls: 190 Turns: 146 Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00140 pruva-verify CVE-2026-37281 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00140&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00140/artifacts/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-37281 ## Artifacts - repro/rca_report.md (analysis, 4648 bytes) - repro/reproduction_steps.sh (reproduction_script, 6613 bytes) - vuln_variant/rca_report.md (analysis, 7043 bytes) - vuln_variant/reproduction_steps.sh (reproduction_script, 11144 bytes) - bundle/context.json (other, 2658 bytes) - bundle/metadata.json (other, 643 bytes) - bundle/ticket.md (ticket, 3220 bytes) - repro/runtime_manifest.json (other, 614 bytes) - repro/validation_verdict.json (other, 1819 bytes) - vuln_variant/root_cause_equivalence.json (other, 1136 bytes) - vuln_variant/patch_analysis.md (documentation, 5200 bytes) - vuln_variant/variant_manifest.json (other, 3089 bytes) - vuln_variant/runtime_manifest.json (other, 2132 bytes) - vuln_variant/validation_verdict.json (other, 2287 bytes) - logs/fixed.json (other, 256 bytes) - logs/fixed-npm-build.log (log, 14258 bytes) - logs/worktree-fix.log (log, 134 bytes) - logs/fixed-runner.log (log, 376 bytes) - logs/vulnerable.json (other, 119 bytes) - logs/fix_static_analysis.log (log, 245 bytes) - logs/fix_original-npm-install.log (log, 1063 bytes) - logs/vuln_original.json (other, 186 bytes) - logs/vuln_alt.json (other, 174 bytes) - logs/fix_original-runner.log (log, 387 bytes) - logs/fix_alt-runner.log (log, 380 bytes) - logs/spawn-injection-runner.log (log, 0 bytes) - logs/worktree-vuln.log (log, 109 bytes) - logs/mock-runner-variant.js (other, 3574 bytes) - logs/vuln_alt-runner.log (log, 562 bytes) - logs/variant_attempt_summary.json (other, 356 bytes) - logs/run.log (log, 63218 bytes) - logs/fix_alt.json (other, 322 bytes) - logs/fixed-npm-install.log (log, 1054 bytes) - logs/vuln_alt-npm-build.log (log, 14277 bytes) - logs/fix_alt-npm-build.log (log, 14277 bytes) - logs/vulnerable-npm-build.log (log, 14258 bytes) - logs/mock-runner.js (other, 3266 bytes) - logs/fix_alt-npm-install.log (log, 1063 bytes) - logs/vuln_original-npm-install.log (log, 1064 bytes) - logs/spawn_injection_test.log (log, 47 bytes) - logs/fix_original-npm-build.log (log, 14276 bytes) - logs/vulnerable-runner.log (log, 380 bytes) - logs/vuln_alt-npm-install.log (log, 1064 bytes) - logs/stale_vuln_scan.log (log, 359 bytes) - logs/vuln_original-npm-build.log (log, 14277 bytes) - logs/vulnerable-npm-install.log (log, 1054 bytes) - logs/fix_original.json (other, 327 bytes) - logs/vuln_original-runner.log (log, 387 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00140 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00140/artifacts/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00140 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev