Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🦀
🦀 crates.io📦 npm
Not in CISA KEV
HIGH severity

GHSA-h539-c7r8-3xq4 deepseek-tui

HIGHFix: Hmbown/CodeWhale@26de44a

GHSA-h539-c7r8-3xq4 is a high-severity (CVSS 7.5) Information Exposure vulnerability in deepseek-tui. A fix is available for deepseek-tui — see the affected versions and patch details below.

CodeWhale: js_execution leaks parent environment to model context via missing env scrub

Also known asCVE-2026-75915
Published
Sep 4, 2026
Updated
Sep 4, 2026
Affected
4 pkgs
Patched
3 / 4
Exploits
None indexed
Exploitation data as of Sep 20, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.

Exploitation and automatability from CISA’s SSVC triage for GHSA-h539-c7r8-3xq4.

EPSS Exploitation Probability

via FIRST.org ↗
0.6%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs49th percentile — riskier than 49% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-h539-c7r8-3xq4 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,333 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

4 pkgs affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, a proxy for how much of the ecosystem is exposed.

deepseek-tuicrates.io
514downloads / week

Description

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):

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:

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:

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:

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:

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.

Affected Packages

4 total 3 fixed
EcosystemPackageVulnerable rangeFix
🦀crates.iodeepseek-tui0.8.32No fix
🦀crates.iocodewhale-tui0.8.41&&< 0.8.640.8.64cargo update -p codewhale-tui --precise 0.8.64
📦npmdeepseek-tui0.8.32&&< 0.8.410.8.41npm install deepseek-tui@0.8.41
📦npmcodewhale0.8.41&&< 0.8.640.8.64npm install codewhale@0.8.64

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for deepseek-tui, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    No patched version of deepseek-tui has shipped for GHSA-h539-c7r8-3xq4 yet. Where your build allows, override or pin the dependency away from the vulnerable range, and apply any maintainer-recommended mitigation.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-h539-c7r8-3xq4 can be triaged on real exposure rather than presence alone.

Tailored to GHSA-h539-c7r8-3xq4. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### 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 Jav
O3 Security · Impact-Aware SCA

Is GHSA-h539-c7r8-3xq4 in your dependencies?

O3 Security finds GHSA-h539-c7r8-3xq4 across crates.io, npm dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-h539-c7r8-3xq4: deepseek-tui | O3 Security