oauthlint 0.4.0 → 0.5.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 CHANGED
@@ -2,15 +2,16 @@
2
2
 
3
3
  # oauthlint
4
4
 
5
- **Catch the OAuth / OIDC / JWT anti-patterns AI coding tools systematically produce.**
5
+ **Catch the OAuth / OIDC / JWT / session / CORS anti-patterns AI coding tools systematically produce.**
6
6
 
7
- A curated, multi-language Semgrep rule pack · JS/TS · Python · Go · Java · Rust (and growing) · CLI + GitHub Action + VS Code · free & MIT
7
+ A curated, multi-language Semgrep rule pack · JS/TS · Python · Go · Java · Rust · CLI + GitHub Action + VS Code · free & MIT
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/oauthlint.svg)](https://www.npmjs.com/package/oauthlint)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/oauthlint.svg)](https://www.npmjs.com/package/oauthlint)
11
11
  [![CI](https://github.com/Auspeo/oauthlint/actions/workflows/ci.yml/badge.svg)](https://github.com/Auspeo/oauthlint/actions/workflows/ci.yml)
12
12
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
13
13
  [![docs](https://img.shields.io/badge/docs-oauthlint.dev-2f6feb.svg)](https://oauthlint.dev)
14
+ [![powered by Semgrep](https://img.shields.io/badge/powered%20by-Semgrep-0a7d6b.svg)](https://semgrep.dev)
14
15
 
15
16
  </div>
16
17
 
@@ -20,59 +21,10 @@ npx oauthlint scan ./src
20
21
 
21
22
  > Requires [Semgrep](https://semgrep.dev/docs/getting-started/) on the machine running the scan (`pipx install semgrep` or `brew install semgrep`). The CLI invokes it under the hood and normalises the output for humans and CI.
22
23
 
23
- 📖 **Full documentation & rule catalogue → [oauthlint.dev](https://oauthlint.dev)**
24
+ 📖 **Full docs & rule catalogue → [oauthlint.dev/docs](https://oauthlint.dev/docs)** · 🔬 **the research behind it → [oauthlint.dev/research](https://oauthlint.dev/research)**
24
25
 
25
26
  ---
26
27
 
27
- ## The problem
28
-
29
- LLM coding assistants — Cursor, Claude, GitHub Copilot, Gemini — ship the *same*
30
- OAuth/JWT mistakes across every project they touch:
31
-
32
- - a JWT verified with `alg: none` accepted
33
- - `client_secret` hard-coded in source
34
- - an OAuth flow with no `state` and no PKCE
35
- - a token written to `localStorage`, readable by any XSS
36
- - `redirect_uri` allow-listed with a `*` wildcard
37
- - a `/login` POST with no rate limiting
38
- - a password persisted in plaintext
39
- - `Math.random()` used for a CSRF token
40
-
41
- oauthlint is the layer between generic SAST and an enterprise IAM program:
42
- **free, focused, developer-first.** Every finding names the rule, the exact
43
- `file:line`, *why* it's dangerous, and *how* to fix it — with CWE/OWASP mappings.
44
-
45
- <div align="center">
46
-
47
- ![oauthlint scanning a project and flagging JWT auth issues](https://raw.githubusercontent.com/Auspeo/oauthlint/main/docs/public/demo.gif)
48
-
49
- </div>
50
-
51
- ## Why oauthlint, and not just Semgrep?
52
-
53
- Honest answer: nothing stops you from writing these rules yourself. Semgrep is
54
- open source — it's the engine we run — and a capable engineer could reproduce a
55
- lot of this. There's no technical moat and we won't pretend otherwise. What
56
- oauthlint gives you is the work most people never do:
57
-
58
- - **Low false positives, validated against real auth libraries.** The rules are
59
- run against `jose`, NextAuth, PyJWT, Authlib, `golang/oauth2`, `oauth2-rs`,
60
- Spring Security and more. Anything that fires on mature library source goes to
61
- a triage queue, not to you — validated across thousands of files of real
62
- auth-library source, with **zero false positives** on the clean libraries
63
- ([validation report](https://oauthlint.dev/VALIDATION)).
64
- - **One coherent product across every language it covers.** Same concepts, same ID scheme,
65
- same docs — not a patchwork of community rules with mismatched styles.
66
- - **Every finding teaches.** The 100+ rules — across JS/TS · Python · Go · Java
67
- · Rust — each link to a fix page with CWE and OWASP mappings. It's a lesson,
68
- not a grep hit.
69
- - **The angle the registry doesn't have:** oauthlint specifically targets the
70
- auth bugs AI coding tools ship on repeat, encoded in each rule's
71
- `llm-prevalence` metadata and measured by a [reproducible benchmark](https://github.com/Auspeo/oauthlint/tree/main/benchmark).
72
-
73
- Use oauthlint when you'd rather not write and maintain an auth rule pack
74
- yourself. That's the whole pitch.
75
-
76
28
  ## Quick start
77
29
 
78
30
  ```bash
@@ -82,236 +34,49 @@ npx oauthlint scan ./src
82
34
  # fail CI on HIGH severity and above
83
35
  npx oauthlint scan ./src --fail-on HIGH
84
36
 
85
- # machine-readable output
86
- npx oauthlint scan ./src --json
87
-
88
- # GitHub Code Scanning
37
+ # GitHub Code Scanning (SARIF) or a shareable HTML audit report
89
38
  npx oauthlint scan ./src --format sarif > oauthlint.sarif
90
-
91
- # auto-apply safe fixes (e.g. cookie flags)
92
- npx oauthlint scan ./src --fix
93
-
94
- # scan several explicit paths (e.g. a list of changed files)
95
- npx oauthlint scan src/auth.ts src/session.ts
96
-
97
- # scan only what changed vs the default branch (fast, incremental)
98
- npx oauthlint scan --diff
99
-
100
- # scan only git-staged files (ideal in a pre-commit hook)
101
- npx oauthlint scan --staged
102
-
103
- # adopt on a large existing codebase: snapshot today's findings…
104
- npx oauthlint baseline ./src
105
- # …then only get alerted on NEW findings from here on
106
- npx oauthlint scan ./src --baseline --fail-on HIGH
107
- ```
108
-
109
- ## Adopting on an existing codebase (baseline)
110
-
111
- Turning a linter on for the first time on a large repo usually drowns you in
112
- pre-existing findings. A **baseline** snapshots the findings that exist *today*
113
- so `scan` only alerts on **new** ones — letting you adopt OAuthLint without
114
- fixing everything up front.
115
-
116
- ```bash
117
- # 1. capture the current findings into .oauthlint-baseline.json (commit this file)
118
- oauthlint baseline ./src
119
-
120
- # 2. from now on, scan reports only findings NOT in the baseline
121
- oauthlint scan ./src --baseline --fail-on HIGH
122
- ```
123
-
124
- - **`oauthlint baseline [paths...]`** scans and writes
125
- `.oauthlint-baseline.json` (override with `-o, --output <file>`). It prints how
126
- many findings were baselined. Commit the file so the whole team shares it.
127
- - **`oauthlint scan --baseline [file]`** runs a normal scan, then suppresses any
128
- finding already in the baseline and reports only the rest. `--fail-on` and the
129
- exit code consider **only the new findings**. With no value it defaults to
130
- `.oauthlint-baseline.json`; a missing baseline file is a **clear error**
131
- (exit `2`), never silently treated as empty.
132
-
133
- **How a finding is fingerprinted.** Each finding gets a stable fingerprint —
134
- `sha256(oauthlintRuleId + repo-relative path + a whitespace-normalised snapshot
135
- of the matched code)`. Crucially the **raw line number is not part of the hash**,
136
- so moving a finding up or down (adding imports, reformatting) keeps it
137
- baselined, while **changing the flagged code** surfaces it as new. The same
138
- fingerprint occurring multiple times in one file is disambiguated by a
139
- deterministic occurrence index. Paths are normalised to repo/cwd-relative
140
- (POSIX separators) so a baseline is portable across machines, and unreadable
141
- files (deleted/binary) degrade gracefully instead of crashing. The baseline
142
- JSON is small, sorted and human-diffable (a `version`, a `generatedAt`, and the
143
- fingerprint list).
144
-
145
- ## Incremental scanning
146
-
147
- For pre-commit hooks and editor integrations, scanning the whole tree on every
148
- keystroke is wasteful. OAuthLint can scan **only the files that changed**:
149
-
150
- ```bash
151
- # files changed vs the merge-base with the repo's default branch
152
- oauthlint scan --diff
153
-
154
- # files changed vs an explicit ref (branch, tag, or commit)
155
- oauthlint scan --diff origin/main
156
- oauthlint scan --diff HEAD~5
157
-
158
- # only git-staged files — drop this in a pre-commit hook
159
- oauthlint scan --staged --fail-on HIGH
160
- ```
161
-
162
- How the change set is resolved:
163
-
164
- - **`--diff [ref]`** — when no `ref` is given, the base is the merge-base with the
165
- repo's default branch (`origin/HEAD` → `origin/main` → `origin/master`, falling
166
- back to `HEAD` when there's no remote). The change set is everything Added /
167
- Copied / Modified / Renamed since that base, plus uncommitted work-tree and
168
- staged changes, plus new untracked files. Deleted files are skipped.
169
- - **`--staged`** — only the staged set (`git diff --cached`), the same files a
170
- commit is about to capture.
171
- - Explicit path args (`oauthlint scan a.ts b.ts`) scan exactly those paths.
172
-
173
- In every mode, files in a language the rule pack doesn't cover (Markdown,
174
- lockfiles, images, …) are filtered out automatically, and an **empty change set
175
- exits `0`** with a `No changed files to scan.` note — it never fails your commit.
176
- Outside a git repository, `--diff` / `--staged` print a clear error and exit `2`
177
- instead of crashing. Git is always invoked with arguments passed as an array
178
- (never a shell string), so a ref name can't inject a shell command.
179
-
180
- Example pre-commit hook (`.git/hooks/pre-commit`):
181
-
182
- ```bash
183
- #!/bin/sh
184
- npx oauthlint scan --staged --fail-on HIGH
185
- ```
186
-
187
- ## Commands
188
-
39
+ npx oauthlint scan ./src --format html > report.html
189
40
  ```
190
- oauthlint scan [paths...] Scan files/directories (default: current dir)
191
- oauthlint scan a.ts b.ts Scan an explicit list of paths
192
- oauthlint scan --diff [ref] Scan only files changed vs a git ref
193
- oauthlint scan --staged Scan only git-staged files (pre-commit)
194
- oauthlint scan --json Emit machine-readable JSON
195
- oauthlint scan --format sarif Emit SARIF for GitHub Code Scanning
196
- oauthlint scan --severity HIGH Only show findings ≥ HIGH
197
- oauthlint scan --fail-on off Never fail the build (CI dry-run)
198
- oauthlint scan --fix Auto-apply safe fixes
199
- oauthlint scan --baseline Report only findings NOT in the baseline
200
- oauthlint baseline [paths...] Snapshot current findings to a baseline file
201
- oauthlint baseline -o <file> Write the baseline to a custom path
202
- oauthlint list List every shipped rule
203
- oauthlint list --json Same, as JSON
204
- oauthlint init Generate .oauthlintrc.yml at cwd
205
- oauthlint init --force Overwrite an existing config
206
- oauthlint doctor Check your setup (Semgrep, config, …)
207
- oauthlint --no-update-check Skip the "update available" check (any command)
208
- ```
209
-
210
- ## Use it in CI
211
41
 
212
- Run the CLI in a workflow and upload SARIF to **GitHub Code Scanning** so
213
- findings appear inline on the PR:
214
-
215
- ```yaml
216
- # .github/workflows/oauthlint.yml
217
- jobs:
218
- oauthlint:
219
- runs-on: ubuntu-latest
220
- steps:
221
- - uses: actions/checkout@v4
222
- - run: pipx install semgrep # the engine oauthlint runs
223
- - run: npx oauthlint scan ./src --format sarif > oauthlint.sarif
224
- - uses: github/codeql-action/upload-sarif@v3
225
- with:
226
- sarif_file: oauthlint.sarif
227
- ```
228
-
229
- Or just fail the build on HIGH findings: `npx oauthlint scan ./src --fail-on HIGH`.
230
-
231
- ### GitHub Action
232
-
233
- Prefer not to wire up the CLI by hand? The **[GitHub Action](https://github.com/Auspeo/oauthlint/tree/main/action)** wraps it for you — it's Docker-based, so it runs in any repo regardless of language:
234
-
235
- ```yaml
236
- - uses: Auspeo/oauthlint/action@v1
237
- with:
238
- severity: HIGH
239
- fail-on: HIGH
240
- ```
241
-
242
- ### VS Code extension
243
-
244
- Catch findings as you type. Install **[oauthlint](https://marketplace.visualstudio.com/items?itemName=auspeo.oauthlint-vscode)** (`auspeo.oauthlint-vscode`) from the VS Code Marketplace for inline diagnostics on save, plus Quick Fix suppressions.
42
+ Scan only what changed for fast pre-commit hooks and editors with `--diff` / `--staged`, or adopt on a large repo with a [baseline](https://oauthlint.dev/docs/baseline) (`oauthlint baseline ./src` then `scan --baseline`) so you're alerted on **new** findings only. Other commands: `list`, `init`, `doctor`. Run `oauthlint --help` or see the [full CLI reference](https://oauthlint.dev/docs/cli).
245
43
 
246
44
  ## What it catches
247
45
 
248
- Rules across OAuth 2.0, OIDC, JWT, cookies, CORS, secrets and session hygiene
249
- each mapped to CWE & OWASP, each with a documentation page. Languages covered
250
- today (more on the way):
251
-
252
- | Language | Libraries |
253
- |----------|-----------|
254
- | JavaScript / TypeScript | jose, jsonwebtoken, NextAuth, express, … |
255
- | Python | PyJWT, Authlib, requests, Flask, Django |
256
- | Java | Spring Security, jjwt, nimbus-jose-jwt |
257
- | Go | golang-jwt, crypto/tls, net/http |
258
- | Rust | jsonwebtoken, reqwest, actix/tower |
46
+ LLM coding assistants — Cursor, Claude, Copilot, Gemini — ship the *same* auth bugs across every project: a JWT accepted with `alg: none`, a hard-coded `client_secret`, an OAuth flow with no `state`/PKCE, a token in `localStorage`, a `*` wildcard `redirect_uri`, an unrate-limited `/login`, a plaintext password, `Math.random()` for a CSRF token.
259
47
 
260
- 👉 **Browse the full, always-current catalogue at [oauthlint.dev/rules](https://oauthlint.dev/rules/).**
48
+ - **100+ rules** across **JS/TS · Python · Go · Java · Rust**, each mapped to CWE/OWASP with a fix page — a lesson, not a grep hit.
49
+ - **Dataflow (taint) analysis** — beyond pattern-matching, the pack traces untrusted input through to dangerous sinks to catch **open-redirect** and **SSRF**.
50
+ - **HTML report** — `scan --format html` renders a self-contained, offline, no-JavaScript audit you can email or attach to a PR.
51
+ - Plus **SARIF** for Code Scanning, `--fix` for safe auto-fixes, incremental `--diff`/`--staged`, and a [baseline](https://oauthlint.dev/docs/baseline) for existing codebases.
261
52
 
262
- ## Configuration
53
+ 👉 **Browse the always-current catalogue at [oauthlint.dev/rules](https://oauthlint.dev/rules/).**
263
54
 
264
- Generate a `.oauthlintrc.yml` at your repo root with `oauthlint init`:
265
-
266
- ```yaml
267
- version: 1
268
- include:
269
- - "src/**/*.{ts,tsx,js,jsx}"
270
- exclude:
271
- - "**/*.test.ts"
272
- rules:
273
- auth.cookie.no-samesite: warn
274
- auth.session.id-in-url: off
275
- failOn: HIGH
276
- ```
55
+ ## Use directly with Semgrep no install
277
56
 
278
- ### Inline suppression
57
+ Already have [Semgrep](https://semgrep.dev)? Run the full pack with one command, no config file:
279
58
 
280
- ```ts
281
- // oauthlint-disable-next-line auth.jwt.alg-none -- legacy code, replaced in Q2
282
- return jwt.verify(token, key, { algorithms: ['RS256', 'none'] });
59
+ ```bash
60
+ semgrep --config https://oauthlint.dev/r/oauthlint.yaml ./src
283
61
  ```
284
62
 
285
- Wholesale silencing (`oauthlint-disable-file *`) is intentionally unsupported
286
- the next reviewer needs to see exactly which lines opted out, and why.
287
-
288
- ## Exit codes
63
+ Per-language bundles exist too (`oauthlint-python.yaml`, `oauthlint-go.yaml`, ). That URL is always the latest pack; for a pinned ruleset in CI, use this CLI (`npx oauthlint@<version> scan`) or vendor [`oauthlint-rules`](https://www.npmjs.com/package/oauthlint-rules). See [the Semgrep docs](https://oauthlint.dev/docs/semgrep).
289
64
 
290
- | Code | When |
291
- |:----:|------|
292
- | `0` | No finding at or above the `--fail-on` threshold |
293
- | `1` | At least one **HIGH** finding |
294
- | `2` | At least one **CRITICAL** finding, a scan whose output could not be parsed (it never silently exits clean), or a missing/malformed `--baseline` file |
295
- | `127` | Semgrep is not installed |
65
+ ## Why oauthlint, and not just Semgrep?
296
66
 
297
- > With `--baseline`, the `--fail-on` gate and these exit codes consider only the
298
- > **new** (non-baselined) findings.
67
+ Honest answer: nothing stops you writing these rules yourself — Semgrep is open source and it's the engine we run. There's no technical moat. What oauthlint gives you is the work most people never do:
299
68
 
300
- ## Stay up to date
69
+ - **Low false positives, validated against real auth libraries** — `jose`, NextAuth, PyJWT, Authlib, `golang/oauth2`, `oauth2-rs`, Spring Security and more. Anything that fires on mature library source goes to a triage queue, not to you ([validation report](https://oauthlint.dev/VALIDATION)).
70
+ - **One coherent product across every language** — same concepts, same ID scheme, same docs; not a patchwork of community rules.
71
+ - **The angle the registry doesn't have** — it targets the auth bugs AI tools ship on repeat, encoded in each rule's `llm-prevalence` metadata and measured by a reproducible benchmark ([the research](https://oauthlint.dev/research)).
301
72
 
302
- oauthlint occasionally prints a short *"Update available"* notice to **stderr**
303
- when a newer version is on npm. It's designed to stay out of your way:
73
+ Use oauthlint when you'd rather not write and maintain an auth rule pack yourself. That's the whole pitch.
304
74
 
305
- - It's **non-blocking** and checks npm at most **once a day** (the result is
306
- cached), so it never slows a scan down.
307
- - It writes to **stderr only** and is **silent** under `--json` / `--format
308
- sarif`, when output is piped, in CI, and offline — so it can never corrupt a
309
- machine-readable report or nag automated runs.
75
+ ## Also available
310
76
 
311
- To turn it off entirely, pass `--no-update-check` or set the standard
312
- `NO_UPDATE_NOTIFIER=1` environment variable.
77
+ - **GitHub Action** `Auspeo/oauthlint/action@v1`, Docker-based (any language), with inline PR annotations + a job summary. [Docs](https://github.com/Auspeo/oauthlint/tree/main/action).
78
+ - **VS Code / Cursor / Windsurf** — [oauthlint](https://marketplace.visualstudio.com/items?itemName=auspeo.oauthlint-vscode) on the VS Code Marketplace and [OpenVSX](https://open-vsx.org/extension/auspeo/oauthlint-vscode): inline diagnostics on save, a status-bar finding count, and Quick Fix suppressions.
313
79
 
314
80
  ## License
315
81
 
316
- MIT — see [LICENSE](https://github.com/Auspeo/oauthlint/blob/main/LICENSE).
317
- Built and maintained by [Auspeo](https://github.com/Auspeo).
82
+ MIT — see [LICENSE](https://github.com/Auspeo/oauthlint/blob/main/LICENSE). Built and maintained by [Auspeo](https://github.com/Auspeo).
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyEpC,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CAwHrD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyEpC,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CA2HrD"}
package/dist/cli.js CHANGED
@@ -80,7 +80,7 @@ export async function buildProgram() {
80
80
  .argument('[paths...]', 'One or more files/directories to scan', ['.'])
81
81
  .description('Scan files or directories for auth misconfigurations')
82
82
  .option('--json', 'Emit JSON (shortcut for --format json)')
83
- .option('--format <fmt>', 'Output format: pretty | json | sarif', parseFormat)
83
+ .option('--format <fmt>', 'Output format: pretty | json | sarif | html', parseFormat)
84
84
  .option('--severity <level>', 'Only emit findings ≥ this severity', parseSeverity)
85
85
  .option('--fail-on <level>', 'Process exits non-zero if any finding ≥ this severity', parseFailOn)
86
86
  .option('--diff [ref]', 'Scan only files changed vs a git ref (default: merge-base with the default branch)')
@@ -101,7 +101,10 @@ export async function buildProgram() {
101
101
  fix: opts.fix,
102
102
  baseline: opts.baseline,
103
103
  });
104
- const machineReadable = opts.json === true || opts.format === 'json' || opts.format === 'sarif';
104
+ const machineReadable = opts.json === true ||
105
+ opts.format === 'json' ||
106
+ opts.format === 'sarif' ||
107
+ opts.format === 'html';
105
108
  await finishWithNotice(code, { version, machineReadable, updateCheck: updateCheckEnabled() });
106
109
  });
107
110
  program
@@ -174,7 +177,7 @@ function parseFailOn(v) {
174
177
  return 'off';
175
178
  return parseSeverity(v);
176
179
  }
177
- const FORMATS = ['pretty', 'json', 'sarif'];
180
+ const FORMATS = ['pretty', 'json', 'sarif', 'html'];
178
181
  function parseFormat(v) {
179
182
  const lower = v.toLowerCase();
180
183
  if (!FORMATS.includes(lower)) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAmB,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAqB,MAAM,YAAY,CAAC;AAM3D;;;;;;;;GAQG;AACH,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,CAAC,CAAqB,EAAiB,EAAE,CACrD,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QAClB,IAAI,CAAC,CAAC,cAAc,KAAK,CAAC,EAAE,CAAC;YAC3B,GAAG,EAAE,CAAC;YACN,OAAO;QACT,CAAC;QACD,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IACL,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,gBAAgB,CAC7B,IAAY,EACZ,GAAwE;IAExE,MAAM,iBAAiB,CAAC;QACtB,cAAc,EAAE,GAAG,CAAC,OAAO;QAC3B,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,QAAQ,EAAE,CAAC,GAAG,CAAC,WAAW;KAC3B,CAAC,CAAC;IACH,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,iDAAiD;IACjD,8DAA8D;IAC9D,KAAK,MAAM,SAAS,IAAI;QACtB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;QACnC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;KAC1C,EAAE,CAAC;QACF,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAY,CAAC;YACrE,IAAI,GAAG,CAAC,OAAO;gBAAE,OAAO,GAAG,CAAC,OAAO,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAE3C,OAAO;SACJ,IAAI,CAAC,WAAW,CAAC;SACjB,WAAW,CACV,4FAA4F,CAC7F;SACA,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC;SACjC,MAAM,CAAC,mBAAmB,EAAE,gDAAgD,CAAC,CAAC;IAEjF,+EAA+E;IAC/E,yEAAyE;IACzE,MAAM,kBAAkB,GAAG,GAAY,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,KAAK,KAAK,CAAC;IAE/E,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,YAAY,EAAE,uCAAuC,EAAE,CAAC,GAAG,CAAC,CAAC;SACtE,WAAW,CAAC,sDAAsD,CAAC;SACnE,MAAM,CAAC,QAAQ,EAAE,wCAAwC,CAAC;SAC1D,MAAM,CAAC,gBAAgB,EAAE,6CAA6C,EAAE,WAAW,CAAC;SACpF,MAAM,CAAC,oBAAoB,EAAE,oCAAoC,EAAE,aAAa,CAAC;SACjF,MAAM,CACL,mBAAmB,EACnB,uDAAuD,EACvD,WAAW,CACZ;SACA,MAAM,CACL,cAAc,EACd,oFAAoF,CACrF;SACA,MAAM,CAAC,UAAU,EAAE,0DAA0D,CAAC;SAC9E,MAAM,CAAC,oBAAoB,EAAE,sCAAsC,CAAC;SACpE,MAAM,CAAC,OAAO,EAAE,4DAA4D,CAAC;SAC7E,MAAM,CACL,mBAAmB,EACnB,4GAA4G,CAC7G;SACA,MAAM,CAAC,KAAK,EAAE,KAAe,EAAE,IAAoB,EAAE,EAAE;QACtD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC;YACzB,KAAK;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;QACH,MAAM,eAAe,GACnB,IAAI,CAAC,IAAI,KAAK,IAAI;YAClB,IAAI,CAAC,MAAM,KAAK,MAAM;YACtB,IAAI,CAAC,MAAM,KAAK,OAAO;YACvB,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC;QACzB,MAAM,gBAAgB,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,QAAQ,CAAC,YAAY,EAAE,uCAAuC,EAAE,CAAC,GAAG,CAAC,CAAC;SACtE,WAAW,CACV,sFAAsF,CACvF;SACA,MAAM,CAAC,qBAAqB,EAAE,kCAAkC,EAAE,0BAA0B,CAAC;SAC7F,MAAM,CAAC,oBAAoB,EAAE,sCAAsC,CAAC;SACpE,MAAM,CAAC,KAAK,EAAE,KAAe,EAAE,IAAwB,EAAE,EAAE;QAC1D,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC;YAC7B,KAAK;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;QACH,iEAAiE;QACjE,MAAM,gBAAgB,CAAC,IAAI,EAAE;YAC3B,OAAO;YACP,eAAe,EAAE,KAAK;YACtB,WAAW,EAAE,kBAAkB,EAAE;SAClC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,gDAAgD,CAAC;SAC7D,MAAM,CAAC,QAAQ,EAAE,oCAAoC,CAAC;SACtD,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,EAAE;QACzC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,gBAAgB,CAAC,IAAI,EAAE;YAC3B,OAAO;YACP,eAAe,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI;YACnC,WAAW,EAAE,kBAAkB,EAAE;SAClC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,sDAAsD,CAAC;SACnE,MAAM,CAAC,aAAa,EAAE,mCAAmC,CAAC;SAC1D,MAAM,CAAC,KAAK,EAAE,IAAyB,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACtE,MAAM,gBAAgB,CAAC,IAAI,EAAE;YAC3B,OAAO;YACP,eAAe,EAAE,KAAK;YACtB,WAAW,EAAE,kBAAkB,EAAE;SAClC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,4DAA4D,CAAC;SACzE,MAAM,CAAC,QAAQ,EAAE,oCAAoC,CAAC;SACtD,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,EAAE;QACzC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,gBAAgB,CAAC,IAAI,EAAE;YAC3B,OAAO;YACP,eAAe,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI;YACnC,WAAW,EAAE,kBAAkB,EAAE;SAClC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC,CAAC;IACtF,OAAO,OAAO,CAAC;AACjB,CAAC;AAsBD,SAAS,aAAa,CAAC,CAAS;IAC9B,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,EAAkB,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,uBAAuB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC5B,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC9B,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAC7D,SAAS,WAAW,CAAC,CAAS;IAC5B,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,EAAgB,CAAC;IAC5C,IAAI,CAAE,OAA6B,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,uBAAuB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { SemgrepAdapter } from '../adapters/semgrep.js';
2
2
  import type { SeverityName } from '../types.js';
3
- export type ScanFormat = 'pretty' | 'json' | 'sarif';
3
+ export type ScanFormat = 'pretty' | 'json' | 'sarif' | 'html';
4
4
  export interface ScanCommandOptions {
5
5
  /**
6
6
  * Path(s) to scan. Accepts one or many — e.g. a pre-commit hook or editor
@@ -1 +1 @@
1
- {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EAGf,MAAM,wBAAwB,CAAC;AAchC,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,GAAG,KAAK,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuJvE"}
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EAGf,MAAM,wBAAwB,CAAC;AAehC,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,GAAG,KAAK,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkLvE"}
@@ -8,6 +8,7 @@ import { Reporter } from '../core/reporter.js';
8
8
  import { toSarif } from '../core/sarif.js';
9
9
  import { exitCodeFor, highestSeverity, meetsThreshold } from '../core/severity.js';
10
10
  import { applySuppressions } from '../core/suppress.js';
11
+ import { renderHtmlReport } from '../formatters/html.js';
11
12
  export async function runScan(opts) {
12
13
  const cwd = opts.cwd ?? process.cwd();
13
14
  const config = await loadConfig(cwd);
@@ -80,6 +81,11 @@ export async function runScan(opts) {
80
81
  });
81
82
  stream.write(`${JSON.stringify(empty, null, 2)}\n`);
82
83
  }
84
+ else if (format === 'html') {
85
+ const html = await renderHtmlReport({ findings: [], scannedFiles: 0, durationMs: 0, semgrepVersion: null, errors: [] }, { target: 'changed files (none)' });
86
+ stream.write(html);
87
+ errStream.write('No changed files to scan.\n');
88
+ }
83
89
  else {
84
90
  stream.write('No changed files to scan.\n');
85
91
  }
@@ -128,6 +134,19 @@ export async function runScan(opts) {
128
134
  const sarif = await toSarif({ ...result, findings });
129
135
  stream.write(`${JSON.stringify(sarif, null, 2)}\n`);
130
136
  }
137
+ else if (format === 'html') {
138
+ // HTML is the report artifact → stdout only. Any human chatter (e.g. the
139
+ // update notice, suppression counts) stays on stderr so the document is
140
+ // never corrupted and `> report.html` is always valid.
141
+ const html = await renderHtmlReport({ ...result, findings }, { target: label });
142
+ stream.write(html);
143
+ if (suppressed.length > 0) {
144
+ errStream.write(`ℹ ${suppressed.length} finding${suppressed.length === 1 ? '' : 's'} suppressed via inline directives.\n`);
145
+ }
146
+ if (baselinedCount > 0) {
147
+ errStream.write(`ℹ ${baselinedCount} finding${baselinedCount === 1 ? '' : 's'} already in the baseline (reporting NEW findings only).\n`);
148
+ }
149
+ }
131
150
  else {
132
151
  reporter.reportResult({ ...result, findings });
133
152
  if (opts.fix && format === 'pretty') {
@@ -1 +1 @@
1
- {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { ScanResult } from '../types.js';
2
+ /**
3
+ * Render a scan result as a single, self-contained HTML document.
4
+ *
5
+ * Design goals:
6
+ * - **Self-contained**: all CSS is inlined; no external requests, no JavaScript.
7
+ * The file works opened straight from disk and prints cleanly.
8
+ * - **Shareable audit artifact**: dark header, severity-coloured badges, a
9
+ * per-severity summary, and findings grouped by severity (worst first).
10
+ * - **Safe**: EVERY interpolated value (paths, messages, rule ids, code
11
+ * snippets, scan target) is HTML-escaped via {@link escapeHtml}, so hostile
12
+ * source under scan — even a literal `</script>` or `<img onerror>` — is
13
+ * rendered as inert text and can never execute in the report.
14
+ *
15
+ * Output is deterministic given its inputs: the timestamp is injected (see
16
+ * {@link HtmlReportOptions.generatedAt}) rather than read from the clock, so
17
+ * the same scan always produces byte-identical HTML — which keeps tests stable.
18
+ */
19
+ export interface HtmlReportOptions {
20
+ /** The scan target(s) shown in the header (e.g. `./src` or a file list). */
21
+ target: string;
22
+ /**
23
+ * Timestamp shown in the header. Injected for determinism; defaults to now.
24
+ * Accepts a `Date` or a pre-formatted string.
25
+ */
26
+ generatedAt?: Date | string;
27
+ /** Tool version, shown in the footer. */
28
+ version?: string;
29
+ /**
30
+ * Best-effort source-snippet lookup. When omitted, snippets are read from
31
+ * disk by {@link Finding.filePath}. Injectable so tests need no real files
32
+ * and so the caller can supply already-resolved snippets. Return
33
+ * `null`/`undefined` for "no snippet available".
34
+ */
35
+ readSnippet?: (filePath: string, startLine: number) => string | null | undefined;
36
+ }
37
+ /**
38
+ * HTML-escape a value for safe interpolation in element text OR attribute
39
+ * context. Covers `&`, `<`, `>`, `"`, and `'` — the full set needed so that
40
+ * neither markup nor an attribute-quote breakout is possible. `&` is replaced
41
+ * first so the other entities aren't double-encoded.
42
+ */
43
+ export declare function escapeHtml(value: unknown): string;
44
+ /**
45
+ * Build the full HTML report document for a scan result. The only side effect
46
+ * is best-effort disk reads for code snippets (skippable via
47
+ * {@link HtmlReportOptions.readSnippet}). Returns the complete document string.
48
+ */
49
+ export declare function renderHtmlReport(result: ScanResult, options: HtmlReportOptions): Promise<string>;
50
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/formatters/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,UAAU,EAAgB,MAAM,aAAa,CAAC;AAErE;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC5B,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAClF;AAID;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOjD;AA+PD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,CAgDjB"}
@@ -0,0 +1,308 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ const SEVERITY_ORDER = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'];
3
+ /**
4
+ * HTML-escape a value for safe interpolation in element text OR attribute
5
+ * context. Covers `&`, `<`, `>`, `"`, and `'` — the full set needed so that
6
+ * neither markup nor an attribute-quote breakout is possible. `&` is replaced
7
+ * first so the other entities aren't double-encoded.
8
+ */
9
+ export function escapeHtml(value) {
10
+ return String(value)
11
+ .replace(/&/g, '&amp;')
12
+ .replace(/</g, '&lt;')
13
+ .replace(/>/g, '&gt;')
14
+ .replace(/"/g, '&quot;')
15
+ .replace(/'/g, '&#39;');
16
+ }
17
+ function severityCounts(findings) {
18
+ const counts = {
19
+ CRITICAL: 0,
20
+ HIGH: 0,
21
+ MEDIUM: 0,
22
+ LOW: 0,
23
+ INFO: 0,
24
+ };
25
+ for (const f of findings)
26
+ counts[f.severity]++;
27
+ return counts;
28
+ }
29
+ /** Read the flagged source line from disk, best-effort. Never throws. */
30
+ async function defaultReadSnippet(filePath, startLine) {
31
+ if (!filePath || startLine < 1)
32
+ return null;
33
+ try {
34
+ const text = await readFile(filePath, 'utf8');
35
+ const lines = text.split(/\r?\n/);
36
+ return lines[startLine - 1] ?? null;
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ /**
43
+ * Resolve a snippet for each finding up front (async disk reads), keyed by the
44
+ * finding object, so the synchronous render pass below stays simple. The
45
+ * injectable {@link HtmlReportOptions.readSnippet} short-circuits disk access.
46
+ */
47
+ async function resolveSnippets(findings, read) {
48
+ const out = new Map();
49
+ await Promise.all(findings.map(async (f) => {
50
+ let snippet;
51
+ if (read) {
52
+ snippet = read(f.filePath, f.startLine);
53
+ }
54
+ else {
55
+ snippet = await defaultReadSnippet(f.filePath, f.startLine);
56
+ }
57
+ out.set(f, snippet ?? null);
58
+ }));
59
+ return out;
60
+ }
61
+ const STYLES = `
62
+ :root { color-scheme: light; }
63
+ * { box-sizing: border-box; }
64
+ body {
65
+ margin: 0;
66
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
67
+ color: #1b2330;
68
+ background: #f4f6fa;
69
+ line-height: 1.5;
70
+ }
71
+ .wrap { max-width: 960px; margin: 0 auto; padding: 0 24px 64px; }
72
+ header.report {
73
+ background: #0f1729;
74
+ color: #f4f6fa;
75
+ padding: 32px 0;
76
+ border-bottom: 4px solid #2f6feb;
77
+ }
78
+ header.report .wrap { padding-bottom: 0; }
79
+ header.report h1 { margin: 0 0 4px; font-size: 24px; letter-spacing: -0.01em; }
80
+ header.report h1 .mark { color: #6ea8ff; }
81
+ header.report .meta { margin: 12px 0 0; font-size: 13px; color: #9fb0c9; }
82
+ header.report .meta code {
83
+ background: rgba(255,255,255,0.08);
84
+ padding: 1px 6px;
85
+ border-radius: 4px;
86
+ font-size: 12px;
87
+ color: #d6e2f5;
88
+ }
89
+ .summary {
90
+ display: flex;
91
+ flex-wrap: wrap;
92
+ gap: 10px;
93
+ margin: 24px 0 8px;
94
+ }
95
+ .pill {
96
+ display: inline-flex;
97
+ align-items: baseline;
98
+ gap: 6px;
99
+ padding: 6px 12px;
100
+ border-radius: 999px;
101
+ font-size: 13px;
102
+ font-weight: 600;
103
+ border: 1px solid transparent;
104
+ }
105
+ .pill .n { font-size: 15px; }
106
+ .pill.total { background: #fff; border-color: #d7deea; color: #1b2330; }
107
+ .badge, .pill {
108
+ --crit-bg: #fde7e9; --crit-fg: #8a1020; --crit-bd: #f3b7c0;
109
+ --high-bg: #fdecea; --high-fg: #a01b12; --high-bd: #f4bcb6;
110
+ --med-bg: #fff4e0; --med-fg: #8a5a00; --med-bd: #f3d79a;
111
+ --low-bg: #e8eefc; --low-fg: #244a9c; --low-bd: #c4d3f4;
112
+ --info-bg: #eef1f5; --info-fg: #4a5568; --info-bd: #d4dae3;
113
+ }
114
+ .pill.CRITICAL { background: var(--crit-bg); color: var(--crit-fg); border-color: var(--crit-bd); }
115
+ .pill.HIGH { background: var(--high-bg); color: var(--high-fg); border-color: var(--high-bd); }
116
+ .pill.MEDIUM { background: var(--med-bg); color: var(--med-fg); border-color: var(--med-bd); }
117
+ .pill.LOW { background: var(--low-bg); color: var(--low-fg); border-color: var(--low-bd); }
118
+ .pill.INFO { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
119
+ .group { margin-top: 32px; }
120
+ .group > h2 {
121
+ font-size: 13px;
122
+ text-transform: uppercase;
123
+ letter-spacing: 0.06em;
124
+ color: #5b6b85;
125
+ border-bottom: 1px solid #dde3ed;
126
+ padding-bottom: 8px;
127
+ margin: 0 0 16px;
128
+ }
129
+ .finding {
130
+ background: #fff;
131
+ border: 1px solid #e2e7f0;
132
+ border-left: 4px solid #c4d3f4;
133
+ border-radius: 8px;
134
+ padding: 16px 18px;
135
+ margin-bottom: 14px;
136
+ }
137
+ .finding.CRITICAL { border-left-color: #c0182e; }
138
+ .finding.HIGH { border-left-color: #d63a2e; }
139
+ .finding.MEDIUM { border-left-color: #e0921a; }
140
+ .finding.LOW { border-left-color: #3a6fd8; }
141
+ .finding.INFO { border-left-color: #8e9aad; }
142
+ .finding .head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
143
+ .badge {
144
+ display: inline-block;
145
+ padding: 2px 9px;
146
+ border-radius: 5px;
147
+ font-size: 11px;
148
+ font-weight: 700;
149
+ letter-spacing: 0.04em;
150
+ border: 1px solid transparent;
151
+ }
152
+ .badge.CRITICAL { background: var(--crit-bg); color: var(--crit-fg); border-color: var(--crit-bd); }
153
+ .badge.HIGH { background: var(--high-bg); color: var(--high-fg); border-color: var(--high-bd); }
154
+ .badge.MEDIUM { background: var(--med-bg); color: var(--med-fg); border-color: var(--med-bd); }
155
+ .badge.LOW { background: var(--low-bg); color: var(--low-fg); border-color: var(--low-bd); }
156
+ .badge.INFO { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
157
+ .finding .rule { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; font-weight: 600; }
158
+ .finding .oid { color: #5b6b85; font-size: 12px; font-weight: 500; }
159
+ .finding .loc {
160
+ margin: 8px 0 0;
161
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
162
+ font-size: 12px;
163
+ color: #5b6b85;
164
+ }
165
+ .finding .msg { margin: 8px 0 0; }
166
+ .finding pre {
167
+ margin: 12px 0 0;
168
+ background: #0f1729;
169
+ color: #e4ebf5;
170
+ border-radius: 6px;
171
+ padding: 12px 14px;
172
+ overflow-x: auto;
173
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
174
+ font-size: 12.5px;
175
+ line-height: 1.45;
176
+ }
177
+ .finding pre .ln { color: #5b6b85; user-select: none; margin-right: 14px; }
178
+ .finding .doc { margin: 10px 0 0; font-size: 13px; }
179
+ .finding .doc a { color: #2f6feb; text-decoration: none; }
180
+ .finding .doc a:hover { text-decoration: underline; }
181
+ .empty {
182
+ margin-top: 40px;
183
+ text-align: center;
184
+ background: #fff;
185
+ border: 1px solid #e2e7f0;
186
+ border-radius: 10px;
187
+ padding: 48px 24px;
188
+ }
189
+ .empty .check { font-size: 40px; color: #1f9d55; }
190
+ .empty h2 { margin: 12px 0 4px; font-size: 20px; }
191
+ .empty p { margin: 0; color: #5b6b85; }
192
+ footer.report { margin-top: 40px; color: #8090a8; font-size: 12px; text-align: center; }
193
+ footer.report a { color: #5b6b85; }
194
+ @media print {
195
+ body { background: #fff; }
196
+ .finding, .empty { break-inside: avoid; }
197
+ }
198
+ `;
199
+ const SEVERITY_LABEL = {
200
+ CRITICAL: 'Critical',
201
+ HIGH: 'High',
202
+ MEDIUM: 'Medium',
203
+ LOW: 'Low',
204
+ INFO: 'Info',
205
+ };
206
+ function renderSummary(counts, total) {
207
+ const pills = SEVERITY_ORDER.filter((s) => counts[s] > 0)
208
+ .map((s) => `<span class="pill ${s}"><span class="n">${counts[s]}</span> ${escapeHtml(SEVERITY_LABEL[s])}</span>`)
209
+ .join('\n ');
210
+ const totalPill = `<span class="pill total"><span class="n">${total}</span> total</span>`;
211
+ return `<div class="summary">\n ${totalPill}${pills ? `\n ${pills}` : ''}\n </div>`;
212
+ }
213
+ function renderSnippet(snippet, startLine) {
214
+ if (snippet === null)
215
+ return '';
216
+ // A blank flagged line is worth nothing to show; skip it.
217
+ if (snippet.trim() === '')
218
+ return '';
219
+ return `\n <pre><span class="ln">${escapeHtml(startLine)}</span>${escapeHtml(snippet)}</pre>`;
220
+ }
221
+ function renderFinding(f, snippet) {
222
+ const oid = f.oauthlintRuleId
223
+ ? ` <span class="oid">(${escapeHtml(f.oauthlintRuleId)})</span>`
224
+ : '';
225
+ const message = escapeHtml(f.message).replace(/\r?\n/g, '<br>');
226
+ const doc = f.docUrl
227
+ ? `\n <p class="doc">📖 <a href="${escapeHtml(f.docUrl)}" rel="noreferrer noopener">${escapeHtml(f.docUrl)}</a></p>`
228
+ : '';
229
+ return ` <article class="finding ${f.severity}">
230
+ <div class="head">
231
+ <span class="badge ${f.severity}">${escapeHtml(f.severity)}</span>
232
+ <span class="rule">${escapeHtml(f.ruleId)}</span>${oid}
233
+ </div>
234
+ <p class="loc">${escapeHtml(f.filePath)}:${escapeHtml(f.startLine)}</p>
235
+ <p class="msg">${message}</p>${renderSnippet(snippet, f.startLine)}${doc}
236
+ </article>`;
237
+ }
238
+ function renderGroups(findings, snippets) {
239
+ const groups = [];
240
+ for (const sev of SEVERITY_ORDER) {
241
+ const inGroup = findings.filter((f) => f.severity === sev);
242
+ if (inGroup.length === 0)
243
+ continue;
244
+ const body = inGroup.map((f) => renderFinding(f, snippets.get(f) ?? null)).join('\n');
245
+ groups.push(` <section class="group">
246
+ <h2>${escapeHtml(SEVERITY_LABEL[sev])} · ${inGroup.length}</h2>
247
+ ${body}
248
+ </section>`);
249
+ }
250
+ return groups.join('\n');
251
+ }
252
+ function formatTimestamp(generatedAt) {
253
+ if (typeof generatedAt === 'string')
254
+ return generatedAt;
255
+ const d = generatedAt ?? new Date();
256
+ return d.toISOString();
257
+ }
258
+ /**
259
+ * Build the full HTML report document for a scan result. The only side effect
260
+ * is best-effort disk reads for code snippets (skippable via
261
+ * {@link HtmlReportOptions.readSnippet}). Returns the complete document string.
262
+ */
263
+ export async function renderHtmlReport(result, options) {
264
+ const { findings } = result;
265
+ const counts = severityCounts(findings);
266
+ const total = findings.length;
267
+ const timestamp = formatTimestamp(options.generatedAt);
268
+ const snippets = await resolveSnippets(findings, options.readSnippet);
269
+ const body = total === 0
270
+ ? ` <div class="empty">
271
+ <div class="check">✓</div>
272
+ <h2>No issues found</h2>
273
+ <p>OAuthLint scanned the target${result.scannedFiles ? ` (${result.scannedFiles} file${result.scannedFiles === 1 ? '' : 's'})` : ''} and found no auth misconfigurations.</p>
274
+ </div>`
275
+ : `${renderSummary(counts, total)}
276
+ ${renderGroups(findings, snippets)}`;
277
+ const footerBits = [
278
+ options.version ? `OAuthLint v${escapeHtml(options.version)}` : 'OAuthLint',
279
+ result.semgrepVersion ? `semgrep ${escapeHtml(result.semgrepVersion)}` : null,
280
+ result.scannedFiles ? `${result.scannedFiles} file(s) scanned` : null,
281
+ ].filter((x) => x !== null);
282
+ return `<!DOCTYPE html>
283
+ <html lang="en">
284
+ <head>
285
+ <meta charset="utf-8">
286
+ <meta name="viewport" content="width=device-width, initial-scale=1">
287
+ <title>OAuthLint Report</title>
288
+ <style>${STYLES}</style>
289
+ </head>
290
+ <body>
291
+ <header class="report">
292
+ <div class="wrap">
293
+ <h1><span class="mark">OAuth</span>Lint — Security Audit Report</h1>
294
+ <p class="meta">
295
+ Target: <code>${escapeHtml(options.target)}</code><br>
296
+ Generated: <code>${escapeHtml(timestamp)}</code>
297
+ </p>
298
+ </div>
299
+ </header>
300
+ <main class="wrap">
301
+ ${body}
302
+ <footer class="report">${escapeHtml(footerBits.join(' · '))}</footer>
303
+ </main>
304
+ </body>
305
+ </html>
306
+ `;
307
+ }
308
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/formatters/html.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauthlint",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Catch the OAuth/OIDC/JWT anti-patterns AI coding tools systematically produce. CLI wrapper around the oauthlint-rules Semgrep rule pack.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,7 +47,7 @@
47
47
  "picocolors": "1.1.1",
48
48
  "yaml": "2.9.0",
49
49
  "zod": "3.24.1",
50
- "oauthlint-rules": "0.2.4"
50
+ "oauthlint-rules": "0.2.5"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "22.10.5",