What happened?
Findings
[VULN-001] Git Flag / Argument Injection in getChangedFiles (High)
- Location: understand-anything-plugin/packages/core/src/staleness.ts:370
- Confidence: High
- Issue: getChangedFiles() executes git diff ${lastCommitHash}..HEAD --name-only via execFileSync without revision verification (git rev-parse) or --end-of-options separation.
- Impact: When analyzing a project containing malicious graph metadata (e.g. knowledge-graph.json or meta.json where project.gitCommitHash is set to --output=path/to/target), git diff interprets the
prefix as command options. Specifically, --output= causes Git to write arbitrary diff output to an attacker-controlled destination, resulting in arbitrary file creation or file overwrite.
- Evidence:
// understand-anything-plugin/packages/core/src/staleness.ts:365-378
export function getChangedFiles(
projectDir: string,
lastCommitHash: string,
): string[] {
try {
const output = execFileSync("git", ["diff", ${lastCommitHash}..HEAD, "--name-only"], {
cwd: projectDir,
encoding: "utf-8",
});
return parseChangedFiles(output);
} catch {
return [];
}
}
(Note: In contrast, evaluateGraphFreshness at lines 223–229 in the same file safely validates the revision using ["rev-parse", "--verify", "--end-of-options", ${requestedGraphCommitHash}^{commit}]
before diffing).
- Fix: Verify and resolve lastCommitHash to a valid commit hash using rev-parse --verify --end-of-options and pass --end-of-options before the revisions:
```typescript
export function getChangedFiles(
projectDir: string,
lastCommitHash: string,
): string[] {
try {
const commit = execFileSync(
"git",
["rev-parse", "--verify", "--end-of-options", `${lastCommitHash}^{commit}`],
{ cwd: projectDir, encoding: "utf-8" },
).trim();
const output = execFileSync(
"git",
["diff", "--end-of-options", `${commit}..HEAD`, "--name-only"],
{ cwd: projectDir, encoding: "utf-8" },
);
return parseChangedFiles(output);
} catch {
return [];
}
}
────────────────────────────────────────────────────────────────────────────────
[VULN-002] Unsanitized Commit Hash in /understand Skill Diff Commands (High)
- Location: understand-anything-plugin/skills/understand/SKILL.md:196, understand-anything-plugin/skills/understand/SKILL.md:365, understand-anything-plugin/hooks/auto-update-prompt.md:28
- Confidence: High
- Issue: /understand instructions in SKILL.md and the auto-update hook prompt instruct the LLM / shell to directly interpolate into git diff ..HEAD --name-only
without first resolving and sanitizing the commit hash with git rev-parse.
- Impact: If a repository contains an attacker-controlled .ua/meta.json or .understand-anything/meta.json with a malicious gitCommitHash, executing the instructed command leads to Git flag injection
or unexpected argument parsing during incremental analysis.
- Evidence:
skills/understand/SKILL.md:196 & 365
git diff ..HEAD --name-only
git diff "..HEAD" --name-only > "$UA_DIR/tmp/changed-files.txt"
hooks/auto-update-prompt.md:28
git diff "..HEAD" --name-only
(Note: Other skills in the repository like understand-chat, understand-diff, understand-explain, understand-onboard, and understand-domain already follow the secure pattern: GRAPH_COMMIT=$(git
rev-parse --verify --end-of-options "${GRAPH_COMMIT_RAW}^{commit}" 2>/dev/null)).
- Fix: Update skills/understand/SKILL.md and hooks/auto-update-prompt.md to resolve the commit hash before executing git diff:
```bash
GRAPH_COMMIT=$(git rev-parse --verify --end-of-options "${LAST_COMMIT_HASH}^{commit}" 2>/dev/null)
if [ -n "$GRAPH_COMMIT" ]; then
git diff --name-only "$GRAPH_COMMIT" HEAD -- .
fi
### Minimal reproduction
_No response_
### Plugin version
2.9.0
### Platform / client
Claude Code (CLI)
### OS + Node version
macos 26.5.2
### Primary language of the analyzed project
_No response_
### Approximate file count of the analyzed project
_No response_
### Relevant logs
```shell
What happened?
Findings
[VULN-001] Git Flag / Argument Injection in getChangedFiles (High)
prefix as command options. Specifically, --output= causes Git to write arbitrary diff output to an attacker-controlled destination, resulting in arbitrary file creation or file overwrite.
// understand-anything-plugin/packages/core/src/staleness.ts:365-378
export function getChangedFiles(
projectDir: string,
lastCommitHash: string,
): string[] {
try {
const output = execFileSync("git", ["diff",
${lastCommitHash}..HEAD, "--name-only"], {cwd: projectDir,
encoding: "utf-8",
});
return parseChangedFiles(output);
} catch {
return [];
}
}
────────────────────────────────────────────────────────────────────────────────
[VULN-002] Unsanitized Commit Hash in /understand Skill Diff Commands (High)
without first resolving and sanitizing the commit hash with git rev-parse.
or unexpected argument parsing during incremental analysis.
skills/understand/SKILL.md:196 & 365
git diff ..HEAD --name-only
git diff "..HEAD" --name-only > "$UA_DIR/tmp/changed-files.txt"
hooks/auto-update-prompt.md:28
git diff "..HEAD" --name-only