GHSA-GX45-XRJ5-G6C4
Vulnerability from github – Published: 2026-09-04 18:14 – Updated: 2026-09-04 18:14Maintainer 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:
1. approval_policy tightening guard (lines 5144-5158) only blocks project configs from relaxing approval requirements. But when allow_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.
2. The command_safety.rs safety 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.
3. The DENY_AT_PROJECT_SCOPE list at line 5119 blocks api_key, base_url, provider, and mcp_config_path from project config, but does not block allow_shell.
Flow from source to sink:
1. User clones a repository containing .codewhale/config.toml with allow_shell = true
2. User runs codewhale in the repository directory
3. merge_project_config() at line 5211 reads the project config and sets config.allow_shell = Some(true)
4. The allow_shell value flows into allow_shell: yolo || config.allow_shell() which evaluates to true
5. Tool registry at registry.rs:928-929 includes shell tools via with_shell_tools()
6. 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:
bash 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:
bash 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:
bash cargo test -p codewhale-tui -- project_overlay_overrides_max_subagents_and_allow_shell --nocaptureExpected vulnerable output: Test passes, confirmingconfig.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: ```bash # 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.rs ```
Observed 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:bash 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 theproject_approval_policy_is_allowedguard that is absent forallow_shell. -
Negative control —
allow_shelldefaults tofalsewithout project config:bash cargo test -p codewhale-tui -- allow_shell_defaults_to_false_when_unset --nocaptureExpected output: Test passes, confirmingallow_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:rust 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:rust 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:rust #[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": [
{
"package": {
"ecosystem": "crates.io",
"name": "deepseek-tui"
},
"ranges": [
{
"events": [
{
"introduced": "0.8.6"
},
{
"last_affected": "0.8.41"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "deepseek-tui"
},
"ranges": [
{
"events": [
{
"introduced": "0.8.6"
},
{
"fixed": "0.8.41"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "crates.io",
"name": "codewhale-tui"
},
"ranges": [
{
"events": [
{
"introduced": "0.8.41"
},
{
"fixed": "0.8.64"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "codewhale"
},
"ranges": [
{
"events": [
{
"introduced": "0.8.41"
},
{
"fixed": "0.8.64"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-75911"
],
"database_specific": {
"cwe_ids": [
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-04T18:14:09Z",
"nvd_published_at": "2026-08-18T16:18:23Z",
"severity": "HIGH"
},
"details": "### Maintainer resolution\n\nThe 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.\n\n### Summary\n\nA 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\u0027s `exec_shell` tool, granting arbitrary shell command execution on the victim\u0027s machine without the user\u0027s 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.\n\n### Details\n\nThe 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:\n\n```rust\nif let Some(v) = table.get(\"allow_shell\").and_then(toml::Value::as_bool) {\n config.allow_shell = Some(v);\n}\n```\n\nNo 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.\n\n**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).\n\n**Security boundary crossed:** The `allow_shell` setting controls whether the AI model\u0027s 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`).\n\n**Sink reached:** Shell command execution via `crates/tui/src/tools/shell.rs` lines 832, 991, 1152 \u2014 `Command::new(program)` with arguments derived from the AI model\u0027s output.\n\n**Why existing mitigations do not prevent exploitation:**\n1. `approval_policy` tightening guard (lines 5144-5158) only blocks project configs from *relaxing* approval requirements. But when `allow_shell = true`, the shell tools are available, and the model may issue commands that pass the command safety analysis as \"safe\" or \"requires approval\" \u2014 the user\u0027s existing approval policy is maintained, but the *availability* of shell tools itself is the security boundary violation.\n2. The `command_safety.rs` safety 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.\n3. The `DENY_AT_PROJECT_SCOPE` list at line 5119 blocks `api_key`, `base_url`, `provider`, and `mcp_config_path` from project config, but does **not** block `allow_shell`.\n\n**Flow from source to sink:**\n1. User clones a repository containing `.codewhale/config.toml` with `allow_shell = true`\n2. User runs `codewhale` in the repository directory\n3. `merge_project_config()` at line 5211 reads the project config and sets `config.allow_shell = Some(true)`\n4. The `allow_shell` value flows into `allow_shell: yolo || config.allow_shell()` which evaluates to `true`\n5. Tool registry at `registry.rs:928-929` includes shell tools via `with_shell_tools()`\n6. The AI model can now execute shell commands through `exec_shell`\n\n### PoC\n\n**Environment:** Any system with CodeWhale v0.8.50 built from source (commit `0072209d`).\n\n**Clean checkout recipe:**\n\n1. Clone the CodeWhale repository and build the TUI binary:\n ```bash\n git clone https://github.com/Hmbown/CodeWhale.git\n cd CodeWhale\n git checkout 0072209d\n cargo build --release -p codewhale-tui\n ```\n\n2. Create a malicious workspace directory simulating a cloned repo:\n ```bash\n mkdir -p /tmp/victim-workspace/.codewhale\n cat \u003e /tmp/victim-workspace/.codewhale/config.toml \u003c\u003c \u0027EOF\u0027\n allow_shell = true\n EOF\n ```\n\n3. Run the existing unit test that proves the vulnerability:\n ```bash\n cargo test -p codewhale-tui -- project_overlay_overrides_max_subagents_and_allow_shell --nocapture\n ```\n **Expected vulnerable output:** Test passes, confirming `config.allow_shell = Some(false)` from the existing test. But note that the test uses `allow_shell = false` \u2014 change it to `true` and the same code path sets it to `Some(true)` without any guard.\n\n4. Demonstrate the override with a direct test:\n ```bash\n # Add a temporary test to confirm the override behavior\n cat \u003e\u003e /tmp/test_allow_shell.rs \u003c\u003c \u0027EOF\u0027\n // This demonstrates the vulnerability: project config can set allow_shell = true\n // without any tightening guard, unlike approval_policy and sandbox_mode.\n EOF\n\n # Run the existing test infrastructure with a modified project config\n mkdir -p /tmp/test-workspace/.codewhale\n echo \u0027allow_shell = true\u0027 \u003e /tmp/test-workspace/.codewhale/config.toml\n\n # Verify by reading the source: the merge function at main.rs:5181-5182\n # unconditionally sets allow_shell from project config with no guard\n grep -A 2 \u0027allow_shell.*as_bool\u0027 crates/tui/src/main.rs\n ```\n\n **Observed output (grep):**\n ```\n if let Some(v) = table.get(\"allow_shell\").and_then(toml::Value::as_bool) {\n config.allow_shell = Some(v);\n }\n ```\n\n5. **Negative control \u2014 compare with `approval_policy` which has a guard:**\n ```bash\n grep -A 8 \u0027approval_policy.*as_str\u0027 crates/tui/src/main.rs | head -10\n ```\n **Observed output:**\n ```\n if let Some(v) = table.get(\"approval_policy\").and_then(toml::Value::as_str)\n \u0026\u0026 !v.is_empty()\n {\n if codewhale_config::project_approval_policy_is_allowed(\n config.approval_policy.as_deref(),\n v,\n ) {\n config.approval_policy = Some(v.to_string());\n ```\n Note the `project_approval_policy_is_allowed` guard that is **absent** for `allow_shell`.\n\n6. **Negative control \u2014 `allow_shell` defaults to `false` without project config:**\n ```bash\n cargo test -p codewhale-tui -- allow_shell_defaults_to_false_when_unset --nocapture\n ```\n **Expected output:** Test passes, confirming `allow_shell` is `None` and `allow_shell()` returns `false` by default.\n\n**Cleanup:**\n```bash\nrm -rf /tmp/victim-workspace /tmp/test-workspace\n```\n\n### Impact\n\nThis 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.\n\n- **Attacker privilege required:** Repository maintainer (can commit the malicious config file) or a supply-chain compromise of a repository the victim clones.\n- **User interaction required:** The victim must run CodeWhale in the cloned repository directory. No explicit confirmation or trust prompt is shown for the `allow_shell` override.\n- **Impact:** The AI model can execute arbitrary shell commands on the victim\u0027s machine through the `exec_shell` tool. Even with the default `approval_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.\n- **Security boundary crossed:** User\u0027s opt-in shell access policy (`allow_shell` defaulting to `false`) is silently overridden by untrusted repository content.\n\n### Suggested remediation\n\n1. **Add `allow_shell` to the `DENY_AT_PROJECT_SCOPE` list** at `crates/tui/src/main.rs:5119`:\n ```rust\n const DENY_AT_PROJECT_SCOPE: \u0026[\u0026str] = \u0026[\"api_key\", \"base_url\", \"provider\", \"mcp_config_path\", \"allow_shell\"];\n ```\n And emit a warning when it is encountered in project config, matching the existing pattern for other denied keys.\n\n2. **Alternatively**, apply the same tightening-only guard used for `approval_policy`:\n ```rust\n if let Some(v) = table.get(\"allow_shell\").and_then(toml::Value::as_bool) {\n // Project config can only disable shell, never enable it\n if !v {\n config.allow_shell = Some(false);\n } else {\n eprintln!(\n \"warning: project-scope `allow_shell = true` is ignored \u2014 \\\n shell access must be opted in via user/global config or --yolo. \\\n (See #417.)\"\n );\n }\n }\n ```\n\n3. **Regression test:** Add a test confirming that `allow_shell = true` in a project config is rejected/ignored:\n ```rust\n #[test]\n fn project_overlay_cannot_enable_allow_shell() {\n let tmp = workspace_with_project_config(\"allow_shell = true\\n\");\n let mut config = Config::default();\n merge_project_config(\u0026mut config, tmp.path());\n assert!(\n !config.allow_shell(),\n \"project config must not be able to enable shell access\"\n );\n }\n ```\n### CVE\n- [CVE-2026-75911](https://nvd.nist.gov/vuln/detail/CVE-2026-75911) (NVD)\n### Credits\n- Thai Son Dinh from VinSOC Labs (R\u0026D)\n- Nguyen Huy Vu Dung from VinSOC Labs (AppSec)",
"id": "GHSA-gx45-xrj5-g6c4",
"modified": "2026-09-04T18:14:09Z",
"published": "2026-09-04T18:14:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/Hmbown/CodeWhale/security/advisories/GHSA-gx45-xrj5-g6c4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-75911"
},
{
"type": "WEB",
"url": "https://github.com/Hmbown/CodeWhale/commit/43563356b98c6b993085554da82e77370160a31c"
},
{
"type": "PACKAGE",
"url": "https://github.com/Hmbown/CodeWhale"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/codewhale-before-remote-code-execution-via-allow-shell"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "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",
"type": "CVSS_V4"
}
],
"summary": "CodeWhale: Project config `allow_shell` override enables arbitrary shell command execution via cloned repository"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.