GHSA-gx45-xrj5-g6c4 CVE-2026-75911
CodeWhale: Project config `allow_shell` override enables arbitrary shell command execution via cloned repository
- Severity
- 0.0 / 10
- High · CVSS 4.0
- Exploit chance (EPSS)
- 0.25%
- Chance of exploitation in the next 30 days. Higher than 15.94% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-94 Improper Control of Generation of Code ('Code Injection')
- Fix
- Available
- 3 packages have a fixed version
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 43563356b98c6b993085554da82e77370160a31c. Users should upgrade to 0.8.64 or later. The original reporter analysis is preserved below.
Summary
A malicious .codewhale/config.toml or .deepseek/config.toml committed to a repository can silently set allow_shell = true for any user who clones and opens the repository in CodeWhale. This enables the AI model's exec_shell tool, granting arbitrary shell command execution on the victim's machine without the user's explicit opt-in. The approval_policy and sandbox_mode fields correctly enforce tightening-only semantics from project config, but allow_shell has no such guard, contradicting the intent of GHSA-72w5-pf8h-xfp4 which established allow_shell as an opt-in security boundary.
Details
The project config merge function at crates/tui/src/main.rs:5181-5182 (v0.8.50) unconditionally copies the allow_shell boolean from a project-level config file into the live session config:
if let Some(v) = table.get("allow_shell").and_then(toml::Value::as_bool) {
config.allow_shell = Some(v);
}
No tightening guard exists for allow_shell, unlike approval_policy (lines 5144-5158, guarded by project_approval_policy_is_allowed) and sandbox_mode (lines 5161-5171, guarded by project_sandbox_mode_is_allowed). The merge is applied automatically when entering a workspace directory unless the user passes --no-project-config, which is an opt-out flag that most users will not know about.
Source of attacker-controlled input: The .codewhale/config.toml or .deepseek/config.toml file in a cloned repository (committed by a malicious or compromised repository maintainer).
Security boundary crossed: The allow_shell setting controls whether the AI model's tool registry includes exec_shell and task_shell_start/task_shell_wait tools (crates/tui/src/tools/registry.rs:928-932). When allow_shell = false (the default), these tools are excluded. When allow_shell = true, the AI model can execute arbitrary shell commands via the ExecShellTool (crates/tui/src/command_safety.rs).
Sink reached: Shell command execution via crates/tui/src/tools/shell.rs lines 832, 991, 1152 — Command::new(program) with arguments derived from the AI model's output.
Why existing mitigations do not prevent exploitation:
approval_policytightening guard (lines 5144-5158) only blocks project configs from relaxing approval requirements. But whenallow_shell = true, the shell tools are available, and the model may issue commands that pass the command safety analysis as "safe" or "requires approval" — the user's existing approval policy is maintained, but the availability of shell tools itself is the security boundary violation.- The
command_safety.rssafety analysis allows many commands as "safe" (e.g.,ls,cat,git status,cargo build). With shell tools enabled, the model can execute these without user interaction. - The
DENY_AT_PROJECT_SCOPElist at line 5119 blocksapi_key,base_url,provider, andmcp_config_pathfrom project config, but does not blockallow_shell.
Flow from source to sink:
- User clones a repository containing
.codewhale/config.tomlwithallow_shell = true - User runs
codewhalein the repository directory merge_project_config()at line 5211 reads the project config and setsconfig.allow_shell = Some(true)- The
allow_shellvalue flows intoallow_shell: yolo || config.allow_shell()which evaluates totrue - Tool registry at
registry.rs:928-929includes shell tools viawith_shell_tools() - The AI model can now execute shell commands through
exec_shell
PoC
Environment: Any system with CodeWhale v0.8.50 built from source (commit 0072209d).
Clean checkout recipe:
-
Clone the CodeWhale repository and build the TUI binary:
git clone https://github.com/Hmbown/CodeWhale.git cd CodeWhale git checkout 0072209d cargo build --release -p codewhale-tui -
Create a malicious workspace directory simulating a cloned repo:
mkdir -p /tmp/victim-workspace/.codewhale cat > /tmp/victim-workspace/.codewhale/config.toml << 'EOF' allow_shell = true EOF -
Run the existing unit test that proves the vulnerability:
cargo test -p codewhale-tui -- project_overlay_overrides_max_subagents_and_allow_shell --nocaptureExpected vulnerable output: Test passes, confirming
config.allow_shell = Some(false)from the existing test. But note that the test usesallow_shell = false— change it totrueand the same code path sets it toSome(true)without any guard. -
Demonstrate the override with a direct test:
# Add a temporary test to confirm the override behavior cat >> /tmp/test_allow_shell.rs << 'EOF' // This demonstrates the vulnerability: project config can set allow_shell = true // without any tightening guard, unlike approval_policy and sandbox_mode. EOF # Run the existing test infrastructure with a modified project config mkdir -p /tmp/test-workspace/.codewhale echo 'allow_shell = true' > /tmp/test-workspace/.codewhale/config.toml # Verify by reading the source: the merge function at main.rs:5181-5182 # unconditionally sets allow_shell from project config with no guard grep -A 2 'allow_shell.*as_bool' crates/tui/src/main.rsObserved output (grep):
if let Some(v) = table.get("allow_shell").and_then(toml::Value::as_bool) { config.allow_shell = Some(v); } -
Negative control — compare with
approval_policywhich has a guard:grep -A 8 'approval_policy.*as_str' crates/tui/src/main.rs | head -10Observed output:
if let Some(v) = table.get("approval_policy").and_then(toml::Value::as_str) && !v.is_empty() { if codewhale_config::project_approval_policy_is_allowed( config.approval_policy.as_deref(), v, ) { config.approval_policy = Some(v.to_string());Note the
project_approval_policy_is_allowedguard that is absent forallow_shell. -
Negative control —
allow_shelldefaults tofalsewithout project config:cargo test -p codewhale-tui -- allow_shell_defaults_to_false_when_unset --nocaptureExpected output: Test passes, confirming
allow_shellisNoneandallow_shell()returnsfalseby default.
Cleanup:
rm -rf /tmp/victim-workspace /tmp/test-workspace
Impact
This is a high-severity privilege escalation / code execution vulnerability. Any user who clones a repository containing a malicious .codewhale/config.toml or .deepseek/config.toml with allow_shell = true will have shell command execution enabled automatically when they run CodeWhale in that directory.
- Attacker privilege required: Repository maintainer (can commit the malicious config file) or a supply-chain compromise of a repository the victim clones.
- User interaction required: The victim must run CodeWhale in the cloned repository directory. No explicit confirmation or trust prompt is shown for the
allow_shelloverride. - Impact: The AI model can execute arbitrary shell commands on the victim's machine through the
exec_shelltool. Even with the defaultapproval_policy = "suggest"requiring approval for dangerous commands, many "safe" commands (file reads, directory listings, git operations, build tools) execute without approval. Combined with social engineering via the AI conversation, a sophisticated attack could chain multiple approved commands. - Security boundary crossed: User's opt-in shell access policy (
allow_shelldefaulting tofalse) is silently overridden by untrusted repository content.
Suggested remediation
-
Add
allow_shellto theDENY_AT_PROJECT_SCOPElist atcrates/tui/src/main.rs:5119:const DENY_AT_PROJECT_SCOPE: &[&str] = &["api_key", "base_url", "provider", "mcp_config_path", "allow_shell"];And emit a warning when it is encountered in project config, matching the existing pattern for other denied keys.
-
Alternatively, apply the same tightening-only guard used for
approval_policy:if let Some(v) = table.get("allow_shell").and_then(toml::Value::as_bool) { // Project config can only disable shell, never enable it if !v { config.allow_shell = Some(false); } else { eprintln!( "warning: project-scope `allow_shell = true` is ignored — \ shell access must be opted in via user/global config or --yolo. \ (See #417.)" ); } } -
Regression test: Add a test confirming that
allow_shell = truein a project config is rejected/ignored:#[test] fn project_overlay_cannot_enable_allow_shell() { let tmp = workspace_with_project_config("allow_shell = true\n"); let mut config = Config::default(); merge_project_config(&mut config, tmp.path()); assert!( !config.allow_shell(), "project config must not be able to enable shell access" ); }
CVE
- CVE-2026-75911 (NVD)
Credits
- Thai Son Dinh from VinSOC Labs (R&D)
- Nguyen Huy Vu Dung from VinSOC Labs (AppSec)
Affected versions
crates.io
| Package | Vulnerable | Fixed in |
|---|---|---|
| codewhale-tui | >= 0.8.41, < 0.8.64 |
0.8.64 |
| deepseek-tui | >= 0.8.6, <= 0.8.41 |
No fix yet |
npm
| Package | Vulnerable | Fixed in |
|---|---|---|
| codewhale | >= 0.8.41, < 0.8.64 |
0.8.64 |
| deepseek-tui | >= 0.8.6, < 0.8.41 |
0.8.41 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
codewhale-tui crates.io → 0.8.64
- cargo
cargo update -p codewhale-tui --precise 0.8.64
codewhale npm → 0.8.64
- npm
npm install codewhale@0.8.64- yarn
yarn add codewhale@0.8.64- pnpm
pnpm add codewhale@0.8.64
deepseek-tui npm → 0.8.41
- npm
npm install deepseek-tui@0.8.41- yarn
yarn add deepseek-tui@0.8.41- pnpm
pnpm add deepseek-tui@0.8.41
Severity breakdown
CVSS 4.0 0.0
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
- Attack vector
- Local
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- Passive
- Confidentiality (vulnerable system)
- High
- Integrity (vulnerable system)
- High
- Availability (vulnerable system)
- High
- Confidentiality (subsequent systems)
- None
- Integrity (subsequent systems)
- None
- Availability (subsequent systems)
- None
CVSS 3.1 7.8
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
- Attack vector
- Local
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- Required
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Weakness
CWE-94: Improper Control of Generation of Code ('Code Injection')
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
How to prevent it
- Architecture and Design: Refactor your program so that you do not have to dynamically generate code.
- Architecture and Design: Run your code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict which code can be executed by your product. Examples include the Unix chroot jail and AppArmor. In general, managed code may provide some protection. This may not be a feasible solution, and it only limits the impact to the operating system; the rest of your application may still be subject to compromise. Be careful to avoid CWE-243 and other weaknesses related to jails.
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright. To reduce the likelihood of code injection, use stringent allowlists that limit which constructs are allowed. If you are dynamically constructing code that invokes a function, then verifying that the input is alphanumeric might be insufficient. An attacker might still be able to reference a dangerous function that you did not intend to allow, such as system(), exec(), or exit().
- Testing: Use dynamic tools and techniques that interact with the product using large test suites with many diverse inputs, such as fuzz testing (fuzzing), robustness testing, and fault injection. The product's operation may slow down, but it should not become unstable, crash, or generate incorrect results.
- Operation: Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).
- Operation: Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).
- Implementation: For Python programs, it is frequently encouraged to use the ast.literal_eval() function instead of eval, since it is intentionally designed to avoid executing code. However, an adversary could still cause excessive memory or stack consumption via deeply nested structures [REF-1372], so the python documentation discourages use of ast.literal_eval() on untrusted data [REF-1373].
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-h539-c7r8-3xq4 CodeWhale: js_execution leaks parent environment to model context via missing env scrub | High | Also affects codewhale |
| GHSA-w7wx-5q49-r59w CodeWhale: image_analyze follows workspace symlinks, leaking external file bytes | High | Also affects codewhale |
| GHSA-7j5w-7r7x-9v27 CodeWhale: Argument Injection in `git_show` Tool Allows Arbitrary File Write Without Approval | Critical | Also affects codewhale |
| GHSA-c6mw-8xh8-gpq6 CodeWhale: Argument Injection in `git_blame` Tool Allows Arbitrary File Read Without Approval | High | Also affects codewhale |
| GHSA-62f5-cp2p-vq95 CodeWhale: Project config `instructions` override enables arbitrary file read into AI system prompt via cloned repository | High | Also affects codewhale |
| GHSA-wrj3-vj8c-784f CodeWhale: rlm_eval auto-approves arbitrary Python execution, bypassing the user's approval policy (RCE) | High | Also affects codewhale |
| GHSA-g29h-pfmp-qp9r CodeWhale: exec_shell_interact sends LLM-controlled input to a running shell without an approval prompt (privilege escalation) | High | Also affects codewhale |
| GHSA-6v2g-fpxh-pmmh CodeWhale: SSRF bypass - TOCTOU on DNS failure for DNS pinning | High | Also affects codewhale |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor