T09 · Insecure Skill Coding Practices
- Location
hooks/agent-changelog-commit/handler.ts:82- Finding
Automatic Hook Command Injection Through Tracked Path Configuration
- Content
View full analysis
Vulnerability Details
File Location:
hooks/agent-changelog-commit/handler.ts:19-25, 82-85
Vulnerability Type: Shell command injection through workspace-controlled configuration
Risk Level: HighVulnerable Code
ts function getTracked(workspace: string): string[] { const cfgPath = join(workspace, ".agent-changelog.json"); try { const cfg = JSON.parse(readFileSync(cfgPath, "utf-8")); if (Array.isArray(cfg.tracked) && cfg.tracked.length > 0) return cfg.tracked; } catch {} return []; }ts // Stage tracked files for (const f of getTracked(workspace)) { run(`git add "${f}" 2>/dev/null || true`, workspace); }The constructed command is passed to a shell by:
ts function run(cmd: string, cwd: string): string { try { return execSync(cmd, { cwd, encoding: "utf-8", timeout: 15_000 }).trim(); } catch { return ""; } }Technical Analysis
The
trackedarray is read from the workspace-controlled.agent-changelog.jsonfile without validation. Each value is interpolated into a command string passed toexecSync, which invokes a shell.Wrapping the value in double quotes does not prevent shell evaluation. Command substitutions using
$()or backticks are still evaluated inside double-quoted shell strings. Shell metacharacters capable of terminating the quoted argument may also become dangerous if a value contains a quote.This operation occurs in the
message:senthook, so exploitation does not require a user to invoke the affected command manually after the malicious configuration has been introduced.Attack Path
- An attacker, compromised tool, or untrusted workspace content modifies
.agent-changelog.json. - The attacker adds a malicious
trackedvalue containing shell syntax, such as a path with a command substitution. - OpenClaw emits a
message:sentevent. - The automatic commit hook read ...[truncated 895 chars]
- An attacker, compromised tool, or untrusted workspace content modifies
- Remediation
View remediation
Remediation Suggestions
Do not construct shell command strings from configuration values.
-
Replace
execSyncwithspawnSyncorexecFileSyncand pass arguments separately:ts import { spawnSync } from "node:child_process"; for (const f of getTracked(workspace)) { const result = spawnSync("git", ["add", "--", f], { cwd: workspace, encoding: "utf-8", timeout: 15_000, shell: false, }); if (result.error || result.status !== 0) { console.error(`[agent-changelog-commit] Failed to stage tracked path`); } } -
Validate every configured path before use:
- Require a nonempty string.
- Reject NUL bytes and control characters.
- Reject absolute paths.
- Resolve the path and ensure it remains inside the workspace.
- Consider rejecting traversal components such as
...
-
Treat
.agent-changelog.jsonas security-sensitive configuration and restrict its write permissions where practical. -
Add tests covering filenames containing quotes,
$(), backticks, leading hyphens, whitespace, and newline characters.
-
