@lhi/tdd-audit 1.16.0 → 1.20.0
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 +214 -93
- package/SKILL.md +6 -0
- package/docs/ai-remediation.md +114 -42
- package/docs/configuration.md +236 -0
- package/docs/rest-api.md +144 -131
- package/docs/scanner.md +5 -3
- package/docs/vulnerability-patterns.md +241 -1
- package/index.js +37 -26
- package/lib/auditor.js +880 -0
- package/lib/badge.js +34 -7
- package/lib/config.js +50 -1
- package/lib/github.js +1 -1
- package/lib/plugin.js +118 -23
- package/lib/reporter.js +23 -5
- package/lib/scanner.js +29 -0
- package/package.json +1 -1
- package/prompts/ai-security.md +329 -0
- package/prompts/auto-audit.md +462 -17
- package/prompts/node-advanced-security.md +394 -0
- package/prompts/security-test-patterns.md +522 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Configuration Reference
|
|
2
|
+
|
|
3
|
+
All `tdd-audit` behaviour is controlled by `.tdd-audit.json` at your repo root. CLI flags override file config; file config overrides built-in defaults.
|
|
4
|
+
|
|
5
|
+
Generate a starter file:
|
|
6
|
+
```bash
|
|
7
|
+
npx @lhi/tdd-audit@latest init
|
|
8
|
+
npx @lhi/tdd-audit@latest init --provider anthropic
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Full schema
|
|
14
|
+
|
|
15
|
+
```jsonc
|
|
16
|
+
{
|
|
17
|
+
// ── Core ──────────────────────────────────────────────────────────────────
|
|
18
|
+
"output": "text", // "text" | "json" | "sarif" | "report"
|
|
19
|
+
"severityThreshold": "LOW", // minimum severity to include: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
|
|
20
|
+
"ignore": [], // path prefixes to skip: ["node_modules", "dist"]
|
|
21
|
+
"port": 3000, // port for `tdd-audit serve`
|
|
22
|
+
"serverApiKey": null, // required on REST API calls; falls back to TDD_AUDIT_API_KEY env var
|
|
23
|
+
|
|
24
|
+
// ── AI provider ───────────────────────────────────────────────────────────
|
|
25
|
+
"provider": "anthropic", // "anthropic" | "openai" | "gemini" | "ollama"
|
|
26
|
+
"model": "claude-opus-4-6",
|
|
27
|
+
"apiKeyEnv": "ANTHROPIC_API_KEY", // env var to read the key from
|
|
28
|
+
"baseUrl": null, // override for OpenAI-compatible providers
|
|
29
|
+
|
|
30
|
+
// ── Branding ──────────────────────────────────────────────────────────────
|
|
31
|
+
// For wrapper/rebranded distributions. See docs/extensibility.md.
|
|
32
|
+
"org": "Daily Caller",
|
|
33
|
+
"project": "my-project",
|
|
34
|
+
"badge_label": "dc-audit", // replaces "tdd-audit" in the shields.io badge
|
|
35
|
+
"tdd_site": "https://security.example.com", // replaces npm link in badge + SARIF
|
|
36
|
+
"security_name": "Alice Smith", // name of the security contact — stamped into SECURITY.md, compliance reports, and webhook payloads
|
|
37
|
+
"security_email": "security@example.com", // email for the vulnerability reporting address in SECURITY.md and payloads
|
|
38
|
+
|
|
39
|
+
// ── Policy as code ────────────────────────────────────────────────────────
|
|
40
|
+
// Override the default severity for any named pattern.
|
|
41
|
+
// Useful when a pattern's default severity doesn't match your org's risk model.
|
|
42
|
+
"severity_overrides": {
|
|
43
|
+
"CORS Wildcard": "CRITICAL", // default: MEDIUM
|
|
44
|
+
"Sensitive Log": "HIGH", // default: MEDIUM
|
|
45
|
+
"Cleartext Traffic": "HIGH" // default: MEDIUM
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// ── Notifications ─────────────────────────────────────────────────────────
|
|
49
|
+
// Fired when a scan completes. Both can be set simultaneously.
|
|
50
|
+
"webhook_url": "https://hooks.example.com/tdd-audit",
|
|
51
|
+
// POST body: { project, timestamp, summary: { critical, high, medium, low }, findings: [...] }
|
|
52
|
+
|
|
53
|
+
"slack_webhook": "https://hooks.slack.com/services/...",
|
|
54
|
+
"slack_channel": "#security", // optional override; uses webhook default if absent
|
|
55
|
+
|
|
56
|
+
// ── Workflow integration ───────────────────────────────────────────────────
|
|
57
|
+
"open_pr": true, // open a GitHub PR per finding instead of committing directly
|
|
58
|
+
"github_token": null, // falls back to GITHUB_TOKEN env var
|
|
59
|
+
"github_repo": null, // "owner/repo" — auto-detected from git remote if null
|
|
60
|
+
|
|
61
|
+
// ── CI / scheduled modes ──────────────────────────────────────────────────
|
|
62
|
+
"pr_mode": false, // lightweight static scan only — fast, designed for PR gates
|
|
63
|
+
"org_scan": null, // "my-github-org" — scan all repos in the org
|
|
64
|
+
"schedule": null, // cron expression for external schedulers: "0 2 * * *"
|
|
65
|
+
|
|
66
|
+
// ── Output additions ──────────────────────────────────────────────────────
|
|
67
|
+
"sbom": false, // generate CycloneDX SBOM alongside the audit
|
|
68
|
+
"report": false, // generate human-readable compliance report (markdown)
|
|
69
|
+
"watch": false, // re-scan affected files on save (watch mode)
|
|
70
|
+
|
|
71
|
+
// ── Secret rotation ───────────────────────────────────────────────────────
|
|
72
|
+
"rotate_secrets": false, // when a hardcoded key is detected, prompt to rotate via provider API
|
|
73
|
+
|
|
74
|
+
// ── Extensibility ─────────────────────────────────────────────────────────
|
|
75
|
+
// See docs/extensibility.md for full documentation.
|
|
76
|
+
"pattern_repos": [],
|
|
77
|
+
"extra_skill_dirs": [],
|
|
78
|
+
"extra_repos": [],
|
|
79
|
+
"mcp_services": [],
|
|
80
|
+
"extra_domains": []
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## CLI flag equivalents
|
|
87
|
+
|
|
88
|
+
| Config key | CLI flag |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `output: "json"` | `--json` or `--format json` |
|
|
91
|
+
| `output: "sarif"` | `--format sarif` |
|
|
92
|
+
| `output: "report"` | `--format report` |
|
|
93
|
+
| `pr_mode: true` | `--pr` |
|
|
94
|
+
| `org_scan: "myorg"` | `--org myorg` |
|
|
95
|
+
| `open_pr: true` | `--open-pr` |
|
|
96
|
+
| `sbom: true` | `--sbom` |
|
|
97
|
+
| `watch: true` | `--watch` |
|
|
98
|
+
| `report: true` | `--report` |
|
|
99
|
+
| `rotate_secrets: true` | `--rotate-secrets` |
|
|
100
|
+
| `provider: "openai"` | `--provider openai` |
|
|
101
|
+
| `model: "gpt-4o"` | `--model gpt-4o` |
|
|
102
|
+
| `severityThreshold: "HIGH"` | `--threshold HIGH` |
|
|
103
|
+
|
|
104
|
+
CLI flags always win over file config.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Policy as code
|
|
109
|
+
|
|
110
|
+
`severity_overrides` lets you redefine what severity means for your org without forking the scanner. The key is the exact pattern name from the [vulnerability patterns reference](./vulnerability-patterns.md).
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
"severity_overrides": {
|
|
114
|
+
"CORS Wildcard": "CRITICAL"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This is applied before findings are reported, before notifications fire, and before PR gates are evaluated. A pattern overridden to `CRITICAL` will block a PR merge just like any built-in CRITICAL.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Notifications
|
|
123
|
+
|
|
124
|
+
Both `webhook_url` and `slack_webhook` fire after every scan (CLI, `--ai`, and `POST /scan`).
|
|
125
|
+
|
|
126
|
+
**Webhook payload:**
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"project": "my-project",
|
|
130
|
+
"org": "My Org",
|
|
131
|
+
"timestamp": "2026-03-26T12:00:00Z",
|
|
132
|
+
"duration_ms": 4200,
|
|
133
|
+
"summary": { "critical": 1, "high": 3, "medium": 2, "low": 0 },
|
|
134
|
+
"findings": [ ... ]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Slack message format:**
|
|
139
|
+
```
|
|
140
|
+
🔴 tdd-audit — my-project
|
|
141
|
+
1 critical · 3 high · 2 medium
|
|
142
|
+
Run /caller-audit to remediate.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## PR mode (`--pr`)
|
|
148
|
+
|
|
149
|
+
Designed for CI PR gates. Runs only the static scanner — no AI agents, no RAG, no fixes. Completes in under a second.
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
- run: npx @lhi/tdd-audit@latest --pr --threshold HIGH
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Exits non-zero if any finding meets or exceeds `severityThreshold`. Wire into your branch protection rules to block merges automatically.
|
|
156
|
+
|
|
157
|
+
Use `severity_overrides` to tune what counts as a blocker for your stack.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Auto-fix PR (`--open-pr`)
|
|
162
|
+
|
|
163
|
+
Instead of committing fixes directly to the working branch, opens a GitHub PR per finding. Each PR contains:
|
|
164
|
+
- The exploit test (Red)
|
|
165
|
+
- The patch (Green)
|
|
166
|
+
- A description linking to the vulnerability pattern
|
|
167
|
+
|
|
168
|
+
Requires `GITHUB_TOKEN` in the environment or `github_token` in config.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Watch mode (`--watch`)
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npx @lhi/tdd-audit@latest --watch
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Re-runs the static scanner on any modified file on save. Reports new findings immediately in the terminal. Does not run agents or apply fixes — use `/caller-audit` for that.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## SBOM (`--sbom`)
|
|
183
|
+
|
|
184
|
+
Generates a [CycloneDX](https://cyclonedx.org/) Software Bill of Materials in JSON format alongside the audit report. Output to `sbom.json` in the project root.
|
|
185
|
+
|
|
186
|
+
Required for US federal contracts (EO 14028), increasingly required in enterprise vendor questionnaires.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Compliance report (`--format report` / `--report`)
|
|
191
|
+
|
|
192
|
+
Generates a markdown report suitable for attaching to a SOC 2 audit, ISO 27001 evidence package, or vendor security questionnaire. Includes:
|
|
193
|
+
|
|
194
|
+
- Findings summary table (severity, location, status)
|
|
195
|
+
- Fix evidence (exploit test name, patch commit, suite result)
|
|
196
|
+
- Coverage gate result
|
|
197
|
+
- Hardening controls applied
|
|
198
|
+
- SBOM reference (if generated)
|
|
199
|
+
- Timestamp and auditor (org/project from config)
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Org scan (`--org`)
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
npx @lhi/tdd-audit@latest --org my-github-org --format report
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Discovers all repos in a GitHub org, runs `--pr` mode on each, and produces a cross-org summary:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
my-github-org security posture — 2026-03-26
|
|
213
|
+
|
|
214
|
+
✅ repo-a 0 critical · 0 high
|
|
215
|
+
⚠️ repo-b 0 critical · 2 high
|
|
216
|
+
🔴 repo-c 1 critical · 4 high
|
|
217
|
+
|
|
218
|
+
3 repos scanned · 1 critical · 6 high total
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Fires `webhook_url` and `slack_webhook` with the aggregate payload if configured.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Secret rotation (`--rotate-secrets`)
|
|
226
|
+
|
|
227
|
+
When a hardcoded API key is detected, prompts to rotate it via the provider's API. Supported providers:
|
|
228
|
+
|
|
229
|
+
| Secret type | Rotation method |
|
|
230
|
+
|---|---|
|
|
231
|
+
| OpenAI key (`sk-...`) | OpenAI API — revoke + generate new key |
|
|
232
|
+
| Anthropic key (`sk-ant-...`) | Anthropic console link + clipboard |
|
|
233
|
+
| GitHub token | GitHub API — revoke token |
|
|
234
|
+
| Supabase service key | Supabase API — regenerate |
|
|
235
|
+
|
|
236
|
+
After rotation, updates the relevant `.env` file and removes the hardcoded value from source. The old key is invalidated whether you commit the change or not.
|
package/docs/rest-api.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# REST API
|
|
2
2
|
|
|
3
|
-
`tdd-audit serve`
|
|
3
|
+
`tdd-audit serve` exposes an authenticated HTTP API built on **Fastify**. Use it to integrate AI-powered security audits into dashboards, CI pipelines, bots, or any tooling that speaks JSON.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -59,7 +59,7 @@ By default the rate limiter keys on the **socket IP**, not `X-Forwarded-For`, to
|
|
|
59
59
|
|
|
60
60
|
### Path validation
|
|
61
61
|
|
|
62
|
-
`POST /
|
|
62
|
+
`POST /audit` and `POST /audit/ai` validate that the requested path is inside the server's working directory. The check is normalised with a trailing path separator to prevent sibling-directory prefix bypasses (e.g. `/app-evil` cannot escape via `/app`). Paths outside cwd return `400`.
|
|
63
63
|
|
|
64
64
|
### Security headers
|
|
65
65
|
|
|
@@ -80,60 +80,117 @@ X-Frame-Options: DENY
|
|
|
80
80
|
No auth required. Returns server status and version.
|
|
81
81
|
|
|
82
82
|
```json
|
|
83
|
-
{ "status": "ok", "version": "1.
|
|
83
|
+
{ "status": "ok", "version": "1.17.0" }
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
88
|
-
### `POST /
|
|
88
|
+
### `POST /audit/ai`
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
**The primary endpoint.** Runs a full agentic LLM audit using tool calls (`read_file`, `list_files`, `search_in_files`, `write_file`). Returns immediately with a `jobId`; poll `GET /jobs/:id` or stream `GET /jobs/:id/stream` for results.
|
|
91
|
+
|
|
92
|
+
Provider and API key can be supplied in the request body or pre-configured in `.tdd-audit.json`.
|
|
91
93
|
|
|
92
94
|
**Request**
|
|
93
95
|
```json
|
|
94
96
|
{
|
|
95
|
-
"path":
|
|
96
|
-
"
|
|
97
|
+
"path": ".",
|
|
98
|
+
"provider": "anthropic",
|
|
99
|
+
"apiKey": "sk-ant-...",
|
|
100
|
+
"model": "claude-opus-4-6",
|
|
101
|
+
"baseUrl": null,
|
|
102
|
+
"depth": "tier-2"
|
|
97
103
|
}
|
|
98
104
|
```
|
|
99
105
|
|
|
100
|
-
| Field |
|
|
106
|
+
| Field | Required | Default | Description |
|
|
101
107
|
|---|---|---|---|
|
|
102
|
-
| `path` |
|
|
103
|
-
| `
|
|
108
|
+
| `path` | no | cwd | Path to audit. Must be inside server cwd. |
|
|
109
|
+
| `provider` | yes* | cfg | `anthropic` \| `openai` \| `gemini` \| `ollama` |
|
|
110
|
+
| `apiKey` | yes* | cfg | Provider API key |
|
|
111
|
+
| `model` | no | provider default | Model override |
|
|
112
|
+
| `baseUrl` | no | — | Base URL for OpenAI-compatible services |
|
|
113
|
+
| `depth` | no | `tier-1` | Output depth tier (see below) |
|
|
114
|
+
| `scanOnly` | no | — | Override depth-derived scan mode |
|
|
115
|
+
| `allowWrites` | no | `false` | Override depth-derived write permission |
|
|
116
|
+
| `findings` | no | — | Pre-identified findings array (triggers targeted-apply mode when `depth=tier-4`) |
|
|
117
|
+
|
|
118
|
+
*Required unless configured in `.tdd-audit.json`.
|
|
119
|
+
|
|
120
|
+
**Response — 202 Accepted**
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
HTTP/1.1 202 Accepted
|
|
124
|
+
Location: /jobs/job_1_...
|
|
125
|
+
Retry-After: 5
|
|
126
|
+
```
|
|
127
|
+
```json
|
|
128
|
+
{ "jobId": "job_1_1711363200000" }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Job result envelope** (available via `GET /jobs/:id` after completion)
|
|
104
132
|
|
|
105
|
-
**Response — JSON**
|
|
106
133
|
```json
|
|
107
134
|
{
|
|
108
|
-
"version":
|
|
109
|
-
"
|
|
110
|
-
"
|
|
135
|
+
"version": "1.16.0",
|
|
136
|
+
"provider": "anthropic",
|
|
137
|
+
"model": "claude-opus-4-6",
|
|
138
|
+
"depth": "tier-2",
|
|
139
|
+
"mode": "scan-only",
|
|
140
|
+
"stack": "Node.js / Express",
|
|
141
|
+
"summary": { "CRITICAL": 1, "HIGH": 2, "MEDIUM": 0, "LOW": 1 },
|
|
142
|
+
"patchesApplied": 0,
|
|
143
|
+
"findings": [ ... ],
|
|
111
144
|
"likelyFalsePositives": [ ... ],
|
|
112
|
-
"
|
|
113
|
-
"scannedAt":
|
|
114
|
-
"duration": 42
|
|
145
|
+
"remediation": [],
|
|
146
|
+
"scannedAt": "2026-03-26T12:00:00.000Z"
|
|
115
147
|
}
|
|
116
148
|
```
|
|
117
149
|
|
|
118
|
-
|
|
150
|
+
`patchesApplied` is the billable unit for `tier-4`: the count of `remediation` entries where `status === "fixed"`.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### Depth tiers
|
|
155
|
+
|
|
156
|
+
| Tier | Mode | Finding fields added | `allowWrites` |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| `tier-1` | scan-only | `name`, `severity`, `file`, `line`, `snippet` | no |
|
|
159
|
+
| `tier-2` | scan-only | + `risk`, `effort`, `cwe`, `owasp`, `references` | no |
|
|
160
|
+
| `tier-3` | full audit | + `patch` (copy-ready), `testSnippet` | no |
|
|
161
|
+
| `tier-4` | full audit | LLM calls `write_file`; `remediation` array tracks each patch | yes |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### Targeted apply (tier-4 + findings)
|
|
119
166
|
|
|
120
|
-
|
|
167
|
+
When `depth=tier-4` and a `findings` array is supplied, the LLM skips the scan phase entirely and applies only the patches you specify. Use this to apply a single finding from a previous tier-3 report:
|
|
121
168
|
|
|
122
|
-
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"provider": "anthropic",
|
|
172
|
+
"apiKey": "sk-ant-...",
|
|
173
|
+
"depth": "tier-4",
|
|
174
|
+
"findings": [
|
|
175
|
+
{
|
|
176
|
+
"name": "SQL Injection",
|
|
177
|
+
"file": "src/db.js",
|
|
178
|
+
"line": 42,
|
|
179
|
+
"patch": "const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\nstmt.run(id);"
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
123
184
|
|
|
124
|
-
|
|
125
|
-
|---|---|
|
|
126
|
-
| 400 | Path traversal attempt, oversized body (> 512 KB), or invalid JSON |
|
|
127
|
-
| 401 | Missing or invalid API key |
|
|
128
|
-
| 429 | Rate limit exceeded |
|
|
185
|
+
The job mode will be reported as `targeted-apply/tier-4(1)`. Only the listed findings are touched — no full re-scan.
|
|
129
186
|
|
|
130
187
|
---
|
|
131
188
|
|
|
132
189
|
### `POST /remediate`
|
|
133
190
|
|
|
134
|
-
Queue an AI-powered remediation job for a **provided findings list**. Returns immediately with a `jobId
|
|
191
|
+
Queue an AI-powered remediation job for a **provided findings list**. Returns immediately with a `jobId`.
|
|
135
192
|
|
|
136
|
-
Use `POST /audit`
|
|
193
|
+
Use `POST /audit/ai` for a fully agentic audit instead.
|
|
137
194
|
|
|
138
195
|
**Request**
|
|
139
196
|
```json
|
|
@@ -142,34 +199,28 @@ Use `POST /audit` instead if you want the server to run the scan itself.
|
|
|
142
199
|
"provider": "anthropic",
|
|
143
200
|
"apiKey": "sk-ant-...",
|
|
144
201
|
"model": "claude-opus-4-6",
|
|
145
|
-
"baseUrl": null
|
|
146
|
-
"severity": "HIGH"
|
|
202
|
+
"baseUrl": null
|
|
147
203
|
}
|
|
148
204
|
```
|
|
149
205
|
|
|
150
206
|
| Field | Required | Description |
|
|
151
207
|
|---|---|---|
|
|
152
|
-
| `findings` | yes | Array of finding objects
|
|
208
|
+
| `findings` | yes | Array of finding objects |
|
|
153
209
|
| `provider` | yes | `anthropic` \| `openai` \| `gemini` \| `ollama` |
|
|
154
210
|
| `apiKey` | yes | Provider API key |
|
|
155
|
-
| `model` | no | Defaults per provider
|
|
156
|
-
| `baseUrl` | no | Override base URL for
|
|
157
|
-
| `severity` | no | Minimum severity to fix. Default: `LOW` (fix all) |
|
|
211
|
+
| `model` | no | Defaults per provider |
|
|
212
|
+
| `baseUrl` | no | Override base URL for OpenAI-compatible services |
|
|
158
213
|
|
|
159
214
|
**Response — 202 Accepted**
|
|
160
215
|
```json
|
|
161
216
|
{ "jobId": "job_1_1711363200000" }
|
|
162
217
|
```
|
|
163
218
|
|
|
164
|
-
Job lifecycle: `pending → running → done | error`
|
|
165
|
-
|
|
166
219
|
---
|
|
167
220
|
|
|
168
221
|
### `POST /audit`
|
|
169
222
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
If no `provider`/`apiKey` are supplied, the server runs the scan only (no remediation) and the job transitions to `done` with just the `findings` array.
|
|
223
|
+
Static scan + AI remediation pipeline in one shot. Runs `quickScan` then passes findings to the remediator. If no `provider`/`apiKey` are supplied, runs the scan only.
|
|
173
224
|
|
|
174
225
|
**Request**
|
|
175
226
|
```json
|
|
@@ -185,7 +236,7 @@ If no `provider`/`apiKey` are supplied, the server runs the scan only (no remedi
|
|
|
185
236
|
|
|
186
237
|
| Field | Required | Description |
|
|
187
238
|
|---|---|---|
|
|
188
|
-
| `path` | no | Path to scan. Defaults to cwd.
|
|
239
|
+
| `path` | no | Path to scan. Defaults to cwd. |
|
|
189
240
|
| `provider` | no | If supplied with `apiKey`, AI remediation runs after the scan |
|
|
190
241
|
| `apiKey` | no | Provider API key |
|
|
191
242
|
| `model` | no | Defaults per provider |
|
|
@@ -196,69 +247,21 @@ If no `provider`/`apiKey` are supplied, the server runs the scan only (no remedi
|
|
|
196
247
|
|
|
197
248
|
```
|
|
198
249
|
HTTP/1.1 202 Accepted
|
|
199
|
-
Location: /jobs/
|
|
250
|
+
Location: /jobs/job_1_...
|
|
200
251
|
Retry-After: 2
|
|
201
252
|
```
|
|
202
|
-
```json
|
|
203
|
-
{ "jobId": "job_1_1711363200000" }
|
|
204
|
-
```
|
|
205
253
|
|
|
206
254
|
Job lifecycle: `pending → scanning → scanned → remediating → done | error`
|
|
207
255
|
|
|
208
|
-
Poll `GET /jobs/:id` or stream `GET /jobs/:id/stream` for progress.
|
|
209
|
-
|
|
210
|
-
**Job object during remediation**
|
|
211
|
-
```json
|
|
212
|
-
{
|
|
213
|
-
"id": "job_1_...",
|
|
214
|
-
"status": "remediating",
|
|
215
|
-
"total": 8,
|
|
216
|
-
"completed": 3,
|
|
217
|
-
"current": "SQL Injection"
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
**Job object when done**
|
|
222
|
-
```json
|
|
223
|
-
{
|
|
224
|
-
"id": "job_1_...",
|
|
225
|
-
"status": "done",
|
|
226
|
-
"createdAt": "...",
|
|
227
|
-
"startedAt": "...",
|
|
228
|
-
"completedAt": "...",
|
|
229
|
-
"findings": [ ... ],
|
|
230
|
-
"results": [
|
|
231
|
-
{
|
|
232
|
-
"finding": { ... },
|
|
233
|
-
"status": "remediated",
|
|
234
|
-
"exploitTest": { "filename": "__tests__/security/xss.test.js", "content": "..." },
|
|
235
|
-
"patch": { "filename": "src/app.js", "diff": "..." },
|
|
236
|
-
"refactorChecks": ["npm test", "npm run test:security"]
|
|
237
|
-
}
|
|
238
|
-
]
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
256
|
---
|
|
243
257
|
|
|
244
258
|
### `GET /jobs/:id`
|
|
245
259
|
|
|
246
|
-
Poll for job status. Works for jobs created by
|
|
260
|
+
Poll for job status. Works for jobs created by `POST /audit/ai`, `POST /remediate`, and `POST /audit`.
|
|
247
261
|
|
|
248
|
-
**Response — pending /
|
|
262
|
+
**Response — pending / running**
|
|
249
263
|
```json
|
|
250
|
-
{ "id": "job_1_...", "status": "
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
**Response — remediating (with progress)**
|
|
254
|
-
```json
|
|
255
|
-
{
|
|
256
|
-
"id": "job_1_...",
|
|
257
|
-
"status": "remediating",
|
|
258
|
-
"total": 8,
|
|
259
|
-
"completed": 3,
|
|
260
|
-
"current": "SQL Injection"
|
|
261
|
-
}
|
|
264
|
+
{ "id": "job_1_...", "status": "running", "depth": "tier-2", "createdAt": "..." }
|
|
262
265
|
```
|
|
263
266
|
|
|
264
267
|
**Response — done**
|
|
@@ -269,7 +272,7 @@ Poll for job status. Works for jobs created by both `POST /remediate` and `POST
|
|
|
269
272
|
"createdAt": "...",
|
|
270
273
|
"startedAt": "...",
|
|
271
274
|
"completedAt": "...",
|
|
272
|
-
"
|
|
275
|
+
"result": { ... }
|
|
273
276
|
}
|
|
274
277
|
```
|
|
275
278
|
|
|
@@ -293,18 +296,14 @@ curl -N http://localhost:3000/jobs/job_1_.../stream \
|
|
|
293
296
|
|
|
294
297
|
**Event format**
|
|
295
298
|
```
|
|
296
|
-
data: {"id":"job_1_...","status":"
|
|
297
|
-
|
|
298
|
-
data: {"id":"job_1_...","status":"scanned","findings":[...]}
|
|
299
|
-
|
|
300
|
-
data: {"id":"job_1_...","status":"remediating","total":8,"completed":1,"current":"SQL Injection"}
|
|
299
|
+
data: {"id":"job_1_...","status":"running","depth":"tier-2","log":"🔍 Exploring..."}
|
|
301
300
|
|
|
302
|
-
data: {"id":"job_1_...","status":"done","completedAt":"...","
|
|
301
|
+
data: {"id":"job_1_...","status":"done","completedAt":"...","result":{...}}
|
|
303
302
|
```
|
|
304
303
|
|
|
305
|
-
The connection
|
|
304
|
+
The connection closes automatically after the terminal state. Connecting to an already-completed job pushes the current state and closes immediately.
|
|
306
305
|
|
|
307
|
-
**Node.js example
|
|
306
|
+
**Node.js example**
|
|
308
307
|
```javascript
|
|
309
308
|
const es = new EventSource(
|
|
310
309
|
'http://localhost:3000/jobs/job_1_.../stream',
|
|
@@ -312,7 +311,7 @@ const es = new EventSource(
|
|
|
312
311
|
);
|
|
313
312
|
es.onmessage = (e) => {
|
|
314
313
|
const job = JSON.parse(e.data);
|
|
315
|
-
if (job.status === 'done') { console.log(job.
|
|
314
|
+
if (job.status === 'done') { console.log(job.result); es.close(); }
|
|
316
315
|
if (job.status === 'error') { console.error(job.error); es.close(); }
|
|
317
316
|
};
|
|
318
317
|
```
|
|
@@ -321,28 +320,19 @@ es.onmessage = (e) => {
|
|
|
321
320
|
|
|
322
321
|
## Full workflow examples
|
|
323
322
|
|
|
324
|
-
### curl —
|
|
323
|
+
### curl — tier-2 audit with polling
|
|
325
324
|
|
|
326
325
|
```bash
|
|
327
326
|
npx @lhi/tdd-audit serve --port 3000 --api-key mysecret &
|
|
328
327
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
-H "Content-Type: application/json" \
|
|
332
|
-
-d '{"path": "."}' | jq '.summary'
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
### curl — full pipeline with polling
|
|
336
|
-
|
|
337
|
-
```bash
|
|
338
|
-
# Kick off audit
|
|
339
|
-
JOB=$(curl -s -X POST http://localhost:3000/audit \
|
|
328
|
+
# Start the audit
|
|
329
|
+
JOB=$(curl -s -X POST http://localhost:3000/audit/ai \
|
|
340
330
|
-H "Authorization: Bearer mysecret" \
|
|
341
331
|
-H "Content-Type: application/json" \
|
|
342
332
|
-d '{
|
|
343
|
-
"path": ".",
|
|
344
333
|
"provider": "anthropic",
|
|
345
|
-
"apiKey": "sk-ant-..."
|
|
334
|
+
"apiKey": "sk-ant-...",
|
|
335
|
+
"depth": "tier-2"
|
|
346
336
|
}' | jq -r '.jobId')
|
|
347
337
|
|
|
348
338
|
# Poll until done
|
|
@@ -351,30 +341,53 @@ while true; do
|
|
|
351
341
|
-H "Authorization: Bearer mysecret" | jq -r '.status')
|
|
352
342
|
echo "Status: $STATUS"
|
|
353
343
|
[ "$STATUS" = "done" ] || [ "$STATUS" = "error" ] && break
|
|
354
|
-
sleep
|
|
344
|
+
sleep 3
|
|
355
345
|
done
|
|
346
|
+
|
|
347
|
+
# Print findings summary
|
|
348
|
+
curl -s http://localhost:3000/jobs/$JOB \
|
|
349
|
+
-H "Authorization: Bearer mysecret" | jq '.result.summary'
|
|
356
350
|
```
|
|
357
351
|
|
|
358
|
-
### curl —
|
|
352
|
+
### curl — tier-3 report then targeted apply
|
|
359
353
|
|
|
360
354
|
```bash
|
|
361
|
-
|
|
355
|
+
# Step 1: get copy-ready patches (no writes)
|
|
356
|
+
JOB=$(curl -s -X POST http://localhost:3000/audit/ai \
|
|
362
357
|
-H "Authorization: Bearer mysecret" \
|
|
363
358
|
-H "Content-Type: application/json" \
|
|
364
|
-
-d '{"
|
|
359
|
+
-d '{"provider":"anthropic","apiKey":"sk-ant-...","depth":"tier-3"}' \
|
|
360
|
+
| jq -r '.jobId')
|
|
361
|
+
|
|
362
|
+
# (wait for done)
|
|
363
|
+
|
|
364
|
+
# Step 2: apply one specific patch
|
|
365
|
+
FINDING=$(curl -s http://localhost:3000/jobs/$JOB \
|
|
366
|
+
-H "Authorization: Bearer mysecret" \
|
|
367
|
+
| jq '.result.findings[0]')
|
|
368
|
+
|
|
369
|
+
curl -s -X POST http://localhost:3000/audit/ai \
|
|
370
|
+
-H "Authorization: Bearer mysecret" \
|
|
371
|
+
-H "Content-Type: application/json" \
|
|
372
|
+
-d "{
|
|
373
|
+
\"provider\": \"anthropic\",
|
|
374
|
+
\"apiKey\": \"sk-ant-...\",
|
|
375
|
+
\"depth\": \"tier-4\",
|
|
376
|
+
\"findings\": [$FINDING]
|
|
377
|
+
}" | jq '.jobId'
|
|
365
378
|
```
|
|
366
379
|
|
|
367
|
-
###
|
|
380
|
+
### curl — SARIF output for GitHub code scanning
|
|
368
381
|
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
382
|
+
```bash
|
|
383
|
+
JOB=$(curl -s -X POST http://localhost:3000/audit/ai \
|
|
384
|
+
-H "Authorization: Bearer mysecret" \
|
|
385
|
+
-H "Content-Type: application/json" \
|
|
386
|
+
-d '{"provider":"anthropic","apiKey":"sk-ant-...","depth":"tier-2","format":"sarif"}' \
|
|
387
|
+
| jq -r '.jobId')
|
|
388
|
+
|
|
389
|
+
# (wait for done)
|
|
390
|
+
|
|
391
|
+
curl -s http://localhost:3000/jobs/$JOB \
|
|
392
|
+
-H "Authorization: Bearer mysecret" | jq '.result' > results.sarif
|
|
380
393
|
```
|