GHSA-c6mw-8xh8-gpq6: Infoleak

Published Sep 4, 2026
·
Updated

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 9a34b5034d29f05d1f28fa61b04719ca6a741020. Users should upgrade to 0.8.64 or later. The original reporter analysis is preserved below.

Argument Injection in gitblame Tool Allows Arbitrary File Read Without Approval

Overview

The gitblame tool in DeepSeek-TUI passes the model-supplied rev parameter unvalidated into the argv of git blame. git blame accepts --contents=<file>, which causes it to use the file's contents in place of the working tree and echo each line verbatim in the blame output. A rev value of --contents=/path/to/secret therefore exfiltrates the targeted file's contents into the tool result, which is returned to the model and displayed in the chat transcript.

The tool is registered with ApprovalRequirement::Auto and declares ToolCapability::ReadOnly. The read is in-scope for the capability label, but the target of the read is not the user expects gitblame to read files inside the workspace, not arbitrary paths on the host.

This is a sibling of the gitshow argument-injection vulnerability filed separately, sharing the same root cause (missing --end-of-options sentinel and unvalidated rev).

Impact

Arbitrary file read at the privilege of the user running DeepSeek-TUI, via malicious repository content combined with prompt injection (the threat model already documented in CVE-2026-45311).

Reachable as the invoking user:

- ~/.ssh/idrsa, ~/.ssh/ided25519, and other private keys - ~/.aws/credentials, ~/.config/gh/hosts.yml, ~/.netrc - .env files anywhere in the filesystem - Any project file outside the workspace the tool would normally restrict to

The leaked contents land in the model's context. The same model that obeyed the prompt-injection in step one can be instructed to forward the leak via fetchurl (network-policy permitting), summarize it in chat, or write it into a tool output the attacker can later retrieve.

Technical Details

Root Cause

crates/tui/src/tools/githistory.rs:

rust // L314-316 fn approvalrequirement(&self) -> ApprovalRequirement { ApprovalRequirement::Auto }

// L322-358 (excerpt) async fn execute(&self, input: Value, context: &ToolContext) -> Result<ToolResult, ToolError> { let pathstr = requiredstr(&input, "path")?; let resolvedpath = context.resolvepath(pathstr)?; // path is bounded to workspace ... let rev = optionalstr(&input, "rev").unwrapor("HEAD"); // rev is NOT bounded ... let mut args = vec![ "blame".tostring(), "--date=iso".tostring(), format!("-L{startline},{endline}"), ]; if porcelain { args.push("--line-porcelain".tostring()); } args.push(rev.tostring()); // unvalidated, no sentinel args.push("--".tostring()); args.push(pathspec.display().tostring()); ... }

path correctly flows through context.resolvepath, which enforces workspace containment (spec.rs:342). rev does not and the -- separator after rev only ends pathspec parsing, it does not stop option parsing of rev itself.

The JSON schema for rev (L283-285) is {"type": "string"} with no constraints.

Why --contents Works

git blame --contents=<file> -- <pathspec> blames the working tree path as if its contents were the supplied file. Each line of the supplied file appears verbatim in the porcelain or human-readable output, prefixed with the attribution marker 00000000 (External file (--contents) <date> N). The full line content is preserved.

Two secondary primitives in the same parser also leak data, with smaller yield:

- --ignore-revs-file=<file> : surfaces parse errors that disclose partial content when the file is not a valid revs list. - -S <file>, --reverse <rev1>..<rev2> : not directly exploitable for read but expand the option surface that argv injection can reach.

Proof of Concept

Argv assembled by the tool with input {"path": "a.txt", "rev": "--contents=/home/a/.ssh/idrsa"}:

git blame --date=iso -L1,200 --contents=/home/a/.ssh/idrsa -- a.txt

Reproduced against system git as a non-root user:

$ id uid=1001(a) gid=1001(a) groups=1001(a)

$ echo "PRIVATEKEYDATA" > /home/a/.ssh/idrsa $ chmod 600 /home/a/.ssh/idrsa

$ git blame --date=iso -L1,5 "--contents=/home/a/.ssh/idrsa" -- a.txt 00000000 (External file (--contents) 2026-05-19 07:05:51 -0400 1) PRIVATEKEYDATA

A non-readable target (/etc/shadow, owned by root with mode 0640) returns Permission denied, confirming the read is bounded by uid as expected; this is not a privilege boundary bypass, it is the desktop user's own filesystem view being exposed past the workspace boundary the tool's path argument otherwise enforces.

End-to-end exploitation is identical to the gitshow companion: malicious repo → AGENTS.md injection → model calls gitblame with the crafted rev → auto-approval → leaked content returned in tool output and consumed by the model.

Remediation

Same shape as the gitshow fix:

rust args.push("--end-of-options".tostring()); args.push(rev.tostring()); args.push("--".tostring()); args.push(pathspec.display().tostring());

Plus a leading-hyphen rejection on rev. A regression test should pin both rev = "--contents=/etc/passwd" and rev = "--ignore-revs-file=/etc/passwd" as rejected inputs.

Affected Software

4 affected componentsFixes available
npm/codewhale>=0.8.41<0.8.64
0.8.64
rust/codewhale-tui>=0.8.41<0.8.64
0.8.64
npm/deepseek-tui>=0.3.27<0.8.41
0.8.41
rust/deepseek-tui>=0.3.27<=0.8.41

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/codewhale to a version that resolves this vulnerability.

    Fixed in 0.8.64
  2. Upgrade

    Upgrade rust/codewhale-tui to a version that resolves this vulnerability.

    Fixed in 0.8.64
  3. Upgrade

    Upgrade npm/deepseek-tui to a version that resolves this vulnerability.

    Fixed in 0.8.41
  4. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 0.8.64Patch 9a34b5034d29f05d1f28fa61b04719ca6a741020
  5. Configuration

    In the git_blame tool, reject malicious `rev` inputs (including those with a leading hyphen, e.g., `--contents=/home/a/.ssh/id_rsa`) so `rev` cannot be used to pass arbitrary `git blame` options like `--contents=<file>`.

    DeepSeek-TUI (git_blame tool) rev input validation = reject any rev string that starts with '-'
  6. Compensating control

    Ensure external path access for file-blame operations is constrained to workspace containment (as enforced by `context.resolve_path` for the `path` argument), and prevent `rev` from being able to reference arbitrary host filesystem paths via `--contents=<file>`.

Event History

Sep 4, 2026
Advisory Published
via GitHub·06:11 PM
Data Sourced
via GitHub·06:11 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What conditions are required for exploitation?

An attacker needs to cause the model to supply a crafted rev value to the git_blame tool, such as --contents=/path/to/secret. No attacker privileges are required, but user interaction is required according to the advisory's CVSS vector.

2

Why can this expose files without a user approval prompt?

The git_blame tool is registered with ApprovalRequirement::Auto and is labeled ReadOnly. Although reading is within that capability label, the unvalidated rev parameter can redirect the read to arbitrary host paths rather than workspace files.

3

What information could be disclosed?

The contents of a targeted readable file can be included verbatim in git blame output. That output is returned to the model and displayed in the chat transcript.

4

Which version contains the fix?

Version 0.8.64 contains the fix. Upgrade to version 0.8.64 or later.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203