diff --git a/CHANGELOG.md b/CHANGELOG.md index 53c95ac..b01cc80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project are documented here, following ### Added +- **SKILL-SEC-007**: flag references to bash, zsh, and PowerShell ConsoleHost history files. - **SKILL-OBF-004**: flag PowerShell `-EncodedCommand` / `-enc` and `xxd -r` output piped into a shell. - **SKILL-INJ-010**: flag prose that fetches remote instructions and tells the agent to follow them. - **SKILL-SUP-003**: flag code that fetches scripts or packages over plaintext `http://`. diff --git a/src/rules.js b/src/rules.js index d24f326..013dd06 100644 --- a/src/rules.js +++ b/src/rules.js @@ -115,6 +115,11 @@ export const RULES = [ remediation: "Reading the keychain, keyring, or secret-tool exposes stored credentials.", pattern: /(security\s+find-generic-password|gnome-keyring|\bsecret-tool\b|keychain)/gi }, + { id: "SKILL-SEC-007", severity: "high", category: "secret-access", appliesTo: "code", + title: "Reads shell-history files", + remediation: "Shell history can contain commands with credentials and other secrets. Do not read it from a skill.", + pattern: /(\.bash_history\b|\.zsh_history\b|ConsoleHost_history(?:\.txt)?\b)/gi }, + // ---- Network exfiltration ---- { id: "SKILL-NET-001", severity: "high", category: "exfiltration", appliesTo: "code", title: "Uploads a local file over the network", diff --git a/test/sec-007-shell-history.test.js b/test/sec-007-shell-history.test.js new file mode 100644 index 0000000..e9fce5f --- /dev/null +++ b/test/sec-007-shell-history.test.js @@ -0,0 +1,26 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { scanText } from "../src/scan.js"; + +test("SKILL-SEC-007 flags shell-history file references", () => { + for (const [source, file] of [ + ["cat ~/.bash_history\n", "setup.sh"], + ["tail -n 50 ~/.zsh_history\n", "setup.sh"], + ["Get-Content $env:APPDATA\\Microsoft\\Windows\\PowerShell\\PSReadLine\\ConsoleHost_history.txt\n", "setup.ps1"], + ]) { + const findings = scanText(source, file, null).filter((f) => f.rule === "SKILL-SEC-007"); + assert.equal(findings.length, 1, `${file}: ${source}`); + assert.equal(findings[0].severity, "high"); + } +}); + +test("SKILL-SEC-007 ignores ordinary history-related text", () => { + for (const source of [ + "history | tail -20\n", + "echo 'keep a changelog of command history'\n", + "const history = []\n", + ]) { + const findings = scanText(source, "setup.sh", null).filter((f) => f.rule === "SKILL-SEC-007"); + assert.equal(findings.length, 0, source); + } +});