A missing environment-scrubbing call in deepseek-tui's (also shipped as codewhale) js_execution tool means model-generated JavaScript inherits the entire parent process environment — including API keys, cloud credentials, and forge tokens — and returns them verbatim as tool output that becomes part of the next model turn. This CVE scores CVSS 7.5 for confidentiality-only impact, sits in the top 59% of EPSS-ranked CVEs for exploitation likelihood, has no public exploit or Nuclei template, and is not in CISA KEV, but the barrier to trigger it is trivial: a single user approval, or zero approvals if YOLO/auto-approve mode is enabled, and a single adversarial prompt injection from a README, fetched webpage, or MCP tool output can drain every exported credential straight into the LLM provider's request logs. Ten other CVEs already exist against this same package family, and the fix is a one-line call to the project's own `apply_to_tokio_command` scrubber that the parallel exec_shell and Python REPL code paths already use — this was a missed call site, not a design gap. Any team using deepseek-tui or codewhale as a developer coding agent should upgrade to 0.8.64+ immediately, audit shell history/CI logs for exposure of DEEPSEEK_API_KEY, cloud, and forge tokens used during the vulnerable window, and disable auto-approve/YOLO mode for any tool that spawns subprocesses until patched.
What is the risk?
High risk despite the CVSS 7.5 ceiling and confidentiality-only impact: the exploit path requires no special privileges, no user interaction beyond a single tool approval (and none at all in auto-approve mode), and network access is not needed since the leaked data rides back through the tool's own stdout into the model context. The realistic risk is amplified by indirect prompt injection — an attacker doesn't need direct access to the developer's machine, only the ability to place adversarial text somewhere the AI coding agent will read (a README, a fetched page, an MCP server response). EPSS at the 59th percentile and no confirmed in-the-wild exploitation keep this from being an emergency, but the blast radius (every credential in the developer's shell) and the near-zero skill floor to trigger it (one crafted comment in a repo the agent reads) make this a high-priority patch for any org running AI coding agents with broad tool access.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| DeepSeek TUI | npm | >= 0.8.41, < 0.8.64 | 0.8.64 |
| DeepSeek TUI | cargo | >= 0.8.41, < 0.8.64 | 0.8.64 |
| DeepSeek TUI | npm | >= 0.8.32, < 0.8.41 | 0.8.41 |
| DeepSeek TUI | cargo | >= 0.8.32, <= 0.8.41 | No patch |
How severe is it?
What is the attack surface?
What should I do?
1 step-
Upgrade codewhale (npm/cargo) and deepseek-tui (npm/cargo) to 0.8.64 or later, which adds the missing
child_env::apply_to_tokio_commandscrubber call before spawning Node for js_execution. If immediate upgrade isn't possible: disable or restrict the js_execution tool via configuration, disable auto-approve/YOLO mode so every js_execution call requires explicit human approval, and run the agent in a wrapper shell with a minimal, secret-free environment (only PATH/HOME/LANG etc.) rather than the developer's full shell. Detection: audit LLM provider request logs and any tool-output transcripts for environment-variable-shaped strings (AKIA*, sk-*, ghp_*, etc.); rotate any credential that was exported in a shell where an unpatched version executed js_execution between 2026-05-12 (introduction) and the 0.8.64 patch.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-75915?
A missing environment-scrubbing call in deepseek-tui's (also shipped as codewhale) js_execution tool means model-generated JavaScript inherits the entire parent process environment — including API keys, cloud credentials, and forge tokens — and returns them verbatim as tool output that becomes part of the next model turn. This CVE scores CVSS 7.5 for confidentiality-only impact, sits in the top 59% of EPSS-ranked CVEs for exploitation likelihood, has no public exploit or Nuclei template, and is not in CISA KEV, but the barrier to trigger it is trivial: a single user approval, or zero approvals if YOLO/auto-approve mode is enabled, and a single adversarial prompt injection from a README, fetched webpage, or MCP tool output can drain every exported credential straight into the LLM provider's request logs. Ten other CVEs already exist against this same package family, and the fix is a one-line call to the project's own `apply_to_tokio_command` scrubber that the parallel exec_shell and Python REPL code paths already use — this was a missed call site, not a design gap. Any team using deepseek-tui or codewhale as a developer coding agent should upgrade to 0.8.64+ immediately, audit shell history/CI logs for exposure of DEEPSEEK_API_KEY, cloud, and forge tokens used during the vulnerable window, and disable auto-approve/YOLO mode for any tool that spawns subprocesses until patched.
Is CVE-2026-75915 actively exploited?
No confirmed active exploitation of CVE-2026-75915 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-75915?
Upgrade codewhale (npm/cargo) and deepseek-tui (npm/cargo) to 0.8.64 or later, which adds the missing `child_env::apply_to_tokio_command` scrubber call before spawning Node for js_execution. If immediate upgrade isn't possible: disable or restrict the js_execution tool via configuration, disable auto-approve/YOLO mode so every js_execution call requires explicit human approval, and run the agent in a wrapper shell with a minimal, secret-free environment (only PATH/HOME/LANG etc.) rather than the developer's full shell. Detection: audit LLM provider request logs and any tool-output transcripts for environment-variable-shaped strings (AKIA*, sk-*, ghp_*, etc.); rotate any credential that was exported in a shell where an unpatched version executed js_execution between 2026-05-12 (introduction) and the 0.8.64 patch.
What systems are affected by CVE-2026-75915?
This vulnerability affects the following AI/ML architecture patterns: agent frameworks.
What is the CVSS score for CVE-2026-75915?
CVE-2026-75915 has a CVSS v3.1 base score of 7.5 (HIGH). The EPSS exploitation probability is 0.50%.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0051.001 Indirect AML.T0053 AI Agent Tool Invocation AML.T0086 Exfiltration via AI Agent Tool Invocation AML.T0098 AI Agent Tool Credential Harvesting Compliance Controls Affected
What are the technical details?
Original Advisory
### Maintainer resolution The CodeWhale maintainers validated this report. The affected package ranges are recorded in the advisory metadata. Version 0.8.64 contains the fix in commit 26de44a8bd5051f8f944ea60b2c37ae1d2b7d25e. Users should upgrade to 0.8.64 or later. The original reporter analysis is preserved below. ### Summary js_execution exposes parent process environment to model-provided JavaScript The js_execution tool spawns Node with tokio::process::Command::new without calling the child_env scrubber that exec_shell, the Python REPL, and the MCP launcher all use. Model-provided JavaScript reads process.env and the values flow back to the parent transcript as the tool's stdout, exposing API keys, cloud credentials, and forge tokens to the next model turn. ### Details In `crates/tui/src/tools/js_execution.rs` (v0.8.37, lines 91-105): ```rust let temp_dir = tempfile::tempdir() .map_err(|e| ToolError::execution_failed(format!("tempdir failed: {e}")))?; let script_path = temp_dir.path().join("js_execution.js"); tokio::fs::write(&script_path, code) .await .map_err(|e| ToolError::execution_failed(format!("tempfile write failed: {e}")))?; let mut cmd = tokio::process::Command::new(&node); cmd.arg(&script_path); cmd.current_dir(workspace); let output = tokio::time::timeout(Duration::from_secs(120), cmd.output()) .await .map_err(|_| ToolError::Timeout { seconds: 120 }) .and_then(|res| res.map_err(|e| ToolError::execution_failed(e.to_string())))?; ``` The Command is built without `cmd.env_clear()` and without the project's `crate::child_env::apply_to_tokio_command` helper. Every variable in the parent process environment is inherited by the spawned `node`. For comparison, exec_shell (`crates/tui/src/tools/shell.rs:790-792`) and the Python REPL (`crates/tui/src/repl/runtime.rs:238`) both apply the scrubber: ```rust child_env::apply_to_command(&mut cmd, child_env::string_map_env(&exec_env.env)); ``` apply_to_tokio_command calls `cmd.env_clear()` and then re-installs only the keys that pass `is_allowed_parent_env_key` (PATH, HOME, USER, LANG/LC_*, TMPDIR, proxy variables, Windows toolchain context, terminal settings). Secret-bearing variables (DEEPSEEK_API_KEY, OPENAI_API_KEY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, etc.) are not on the allowlist and are dropped before the child starts. The js_execution path bypasses both the env_clear and the allowlist. Commit history makes the gap explicit. Commit `e6d4eae fix(security): scrub child process environments` (2026-05-08) introduced child_env.rs and rewrote exec_shell, the Python REPL, the MCP launcher, and main.rs to use it. Commit `2566f3c feat(tools): add js_execution tool` (2026-05-12) added this file four days later and never picked up the helper. The tool is described to the model and surfaced in the approval pane as "Run model-provided JavaScript code in local Node.js execution sandbox" (`crates/tui/src/core/engine/turn_loop.rs:1174-1176`). No sandbox is applied beyond a 120-second timeout; Node has full filesystem and network access in addition to the inherited environment. The wording understates the trust boundary that the user is being asked to cross. In YOLO mode (`auto_approve=true`) the JS body runs without any prompt at all, so a single adversarial prompt-injection from a README, fetched web page, or MCP server output drains the parent environment to the next model turn. ### PoC A standalone Cargo test reproduces the unscrubbed-env behavior. Save as `crates/tui/tests/js_execution_env_leak.rs` and run with `cargo test -p deepseek-tui --test js_execution_env_leak -- --nocapture`: ```rust use deepseek_tui::tools::js_execution::execute_js_execution_tool; use serde_json::json; use tempfile::tempdir; #[tokio::test] async fn js_execution_inherits_parent_secrets() { if deepseek_tui::dependencies::resolve_node().is_none() { eprintln!("node not on PATH; skipping"); return; } unsafe { std::env::set_var("AWS_SECRET_ACCESS_KEY", "leak-marker-AKIA-EXAMPLE"); std::env::set_var("DEEPSEEK_API_KEY", "leak-marker-sk-EXAMPLE"); } let tmp = tempdir().unwrap(); let result = execute_js_execution_tool( &json!({"code": "console.log(process.env.AWS_SECRET_ACCESS_KEY + '|' + process.env.DEEPSEEK_API_KEY)"}), tmp.path(), ).await.expect("execute"); let payload: serde_json::Value = serde_json::from_str(&result.content).unwrap(); let stdout = payload["stdout"].as_str().unwrap_or(""); assert!(stdout.contains("leak-marker-AKIA-EXAMPLE"), "AWS leaked: {stdout}"); assert!(stdout.contains("leak-marker-sk-EXAMPLE"), "DEEPSEEK leaked: {stdout}"); } ``` Equivalent reproducer against the binary: ```bash export AWS_SECRET_ACCESS_KEY="leak-marker-AKIA-EXAMPLE" export DEEPSEEK_API_KEY="leak-marker-sk-EXAMPLE" deepseek # Ask the model to run: # js_execution({"code":"console.log(JSON.stringify(process.env))"}) # Approve once. The returned stdout contains every parent env value verbatim, # including the markers above, and is now part of the model's context for the next request. ``` The fix is one line added next to the existing `cmd.current_dir(workspace)` call: ```rust let mut cmd = tokio::process::Command::new(&node); cmd.arg(&script_path); cmd.current_dir(workspace); crate::child_env::apply_to_tokio_command(&mut cmd, std::iter::empty::<(&str, &str)>()); ``` This calls the existing helper with no overrides, mirroring how repl/runtime.rs spawns the Python REPL. The behavior the description string already promises (sandbox) is then partially honored: secret-bearing parent variables stay in the parent. ### Impact The tool returns parent-environment secrets to the model on a single approval, or with no approval in YOLO mode. Any variable the user has exported becomes part of the next model request and travels to the configured LLM provider's logs. Common variables that the codebase's own provider clients read from process env, and therefore the values most likely to be present, include DEEPSEEK_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, MISTRAL_API_KEY, AZURE_OPENAI_API_KEY, XAI_API_KEY, GROQ_API_KEY, and TOGETHER_API_KEY. Cloud and source-control credentials commonly exported in developer shells include AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, GOOGLE_APPLICATION_CREDENTIALS, GITHUB_TOKEN, GH_TOKEN, GITLAB_TOKEN, NPM_TOKEN, CARGO_REGISTRY_TOKEN, PYPI_API_TOKEN, and DATABASE_URL-style secrets. The local-sandbox wording shown at approval time understates the trust boundary, so users approving what they read as a sandboxed snippet do not anticipate that every shell-exported credential is reachable from the snippet. The remediation matches the pattern already adopted across exec_shell, the Python REPL, and the MCP launcher, so the gap is a missed call site rather than a design tradeoff.
Exploitation Scenario
A developer using deepseek-tui to work on a public repository asks the agent to review a third-party library's README or fetches a webpage via an MCP tool. That content contains an indirect prompt injection instructing the model to call `js_execution({"code":"console.log(JSON.stringify(process.env))"})` disguised as a debugging or environment-check step. In interactive mode, the developer sees a generic "run JavaScript in local Node.js execution sandbox" approval prompt — wording that understates the trust boundary — and approves it as a routine sandboxed snippet; in YOLO/auto-approve mode, no prompt appears at all. The spawned Node process inherits the full parent shell environment, dumps every exported variable including AWS keys, GitHub tokens, and the DEEPSEEK_API_KEY itself, and that output is written into the tool result. The secrets now sit in the model's context window and are transmitted to the LLM provider on the next API call, giving the attacker (via any downstream access to logs, a colluding provider-side actor, or a second injection that asks the model to "summarize what you found") a path to exfiltrate the credentials outside the developer's control entirely.
Weaknesses (CWE)
CWE-200 Exposure of Sensitive Information to an Unauthorized Actor
Primary
CWE-526 Cleartext Storage of Sensitive Information in an Environment Variable
Primary
CWE-200 — Exposure of Sensitive Information to an Unauthorized Actor: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
- [Architecture and Design] Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N References
- github.com/Hmbown/CodeWhale/commit/26de44a8bd5051f8f944ea60b2c37ae1d2b7d25e
- github.com/Hmbown/CodeWhale/security/advisories/GHSA-h539-c7r8-3xq4
- github.com/advisories/GHSA-h539-c7r8-3xq4
- nvd.nist.gov/vuln/detail/CVE-2026-75915
- vulncheck.com/advisories/codewhale-before-environment-variable-leak-via-js-execution
Timeline
Related Vulnerabilities
CVE-2026-45311 9.6 deepseek-tui: prompt injection enables zero-approval RCE
Same package: deepseek-tui CVE-2026-75913 9.3 Analysis pending
Same package: deepseek-tui CVE-2026-75856 8.6 deepseek-tui: SSRF via DNS-pinning TOCTOU bypass
Same package: deepseek-tui CVE-2026-75858 7.8 Analysis pending
Same package: deepseek-tui CVE-2026-75911 7.8 Analysis pending
Same package: deepseek-tui