oauthlint 0.3.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 +28 -188
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +68 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/baseline.d.ts +21 -0
- package/dist/commands/baseline.d.ts.map +1 -0
- package/dist/commands/baseline.js +50 -0
- package/dist/commands/baseline.js.map +1 -0
- package/dist/commands/scan.d.ts +8 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +47 -0
- package/dist/commands/scan.js.map +1 -1
- package/dist/core/baseline.d.ts +102 -0
- package/dist/core/baseline.d.ts.map +1 -0
- package/dist/core/baseline.js +223 -0
- package/dist/core/baseline.js.map +1 -0
- package/dist/core/update-notifier.d.ts +61 -0
- package/dist/core/update-notifier.d.ts.map +1 -0
- package/dist/core/update-notifier.js +267 -0
- package/dist/core/update-notifier.js.map +1 -0
- package/dist/formatters/html.d.ts +50 -0
- package/dist/formatters/html.d.ts.map +1 -0
- package/dist/formatters/html.js +308 -0
- package/dist/formatters/html.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { request as httpsRequest } from 'node:https';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
/**
|
|
7
|
+
* A minimal, dependency-free "newer version available" notifier — the same
|
|
8
|
+
* idea as npm/eslint, kept deliberately small so it adds no transitive
|
|
9
|
+
* supply-chain surface (we don't pull in the `update-notifier` tree).
|
|
10
|
+
*
|
|
11
|
+
* Hard guarantees:
|
|
12
|
+
* - NEVER blocks or delays the command. The check is fire-and-forget against a
|
|
13
|
+
* ~24h cache; the network call has a short timeout and ALL errors are
|
|
14
|
+
* swallowed (offline is completely silent).
|
|
15
|
+
* - Writes ONLY to stderr, never stdout — so `--json` / `--format sarif`
|
|
16
|
+
* piping is never corrupted.
|
|
17
|
+
* - Suppressed whenever output is machine-readable, stderr isn't a TTY, a CI
|
|
18
|
+
* env is detected, `NO_UPDATE_NOTIFIER` is set, or the user opted out.
|
|
19
|
+
*/
|
|
20
|
+
/** Package name on the npm registry. */
|
|
21
|
+
const PACKAGE_NAME = 'oauthlint';
|
|
22
|
+
/** How long a cached registry answer is trusted before we re-check. */
|
|
23
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h
|
|
24
|
+
/** Network timeout — short, so a slow/blocked registry never stalls the CLI. */
|
|
25
|
+
const NETWORK_TIMEOUT_MS = 2500;
|
|
26
|
+
/**
|
|
27
|
+
* Decide whether a notice may be shown at all, independent of versions.
|
|
28
|
+
* Cheap, synchronous, and the single source of truth for suppression.
|
|
29
|
+
*/
|
|
30
|
+
export function shouldCheckForUpdate(opts) {
|
|
31
|
+
const env = opts.env ?? process.env;
|
|
32
|
+
if (opts.disabled)
|
|
33
|
+
return false;
|
|
34
|
+
if (opts.machineReadable)
|
|
35
|
+
return false;
|
|
36
|
+
// Explicit kill-switch honoured by the whole ecosystem.
|
|
37
|
+
if (env.NO_UPDATE_NOTIFIER)
|
|
38
|
+
return false;
|
|
39
|
+
// Any common CI signal — never nag automated runs.
|
|
40
|
+
if (isCI(env))
|
|
41
|
+
return false;
|
|
42
|
+
// Piped / redirected stderr: the notice would land in a log or get mangled.
|
|
43
|
+
const stderr = opts.stderr ?? process.stderr;
|
|
44
|
+
if (!stderr.isTTY)
|
|
45
|
+
return false;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
function isCI(env) {
|
|
49
|
+
return Boolean(env.CI ||
|
|
50
|
+
env.CONTINUOUS_INTEGRATION ||
|
|
51
|
+
env.BUILD_NUMBER ||
|
|
52
|
+
env.GITHUB_ACTIONS ||
|
|
53
|
+
env.GITLAB_CI ||
|
|
54
|
+
env.CIRCLECI ||
|
|
55
|
+
env.TRAVIS ||
|
|
56
|
+
env.JENKINS_URL ||
|
|
57
|
+
env.TEAMCITY_VERSION ||
|
|
58
|
+
env.BITBUCKET_BUILD_NUMBER);
|
|
59
|
+
}
|
|
60
|
+
/** Default cache location: XDG_CACHE_HOME, else ~/.cache. */
|
|
61
|
+
export function defaultCachePath(env = process.env) {
|
|
62
|
+
const base = env.XDG_CACHE_HOME?.trim() || join(homedir(), '.cache');
|
|
63
|
+
return join(base, 'oauthlint', 'update-check.json');
|
|
64
|
+
}
|
|
65
|
+
async function readCache(path) {
|
|
66
|
+
try {
|
|
67
|
+
const raw = await readFile(path, 'utf8');
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
if (parsed &&
|
|
70
|
+
typeof parsed === 'object' &&
|
|
71
|
+
typeof parsed.lastCheck === 'number') {
|
|
72
|
+
const c = parsed;
|
|
73
|
+
const v = c.latestVersion;
|
|
74
|
+
// Validate the cached version: only accept null or a sane semver-ish
|
|
75
|
+
// string. Never trust arbitrary content (defends against a tampered file).
|
|
76
|
+
if (v === null || (typeof v === 'string' && isValidVersion(v))) {
|
|
77
|
+
return { lastCheck: c.lastCheck, latestVersion: v };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
/* missing/corrupt cache → treat as no cache */
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
async function writeCache(path, cache) {
|
|
87
|
+
try {
|
|
88
|
+
await mkdir(dirname(path), { recursive: true });
|
|
89
|
+
await writeFile(path, JSON.stringify(cache), 'utf8');
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* best-effort: a read-only home must never break the command */
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Fetch the latest published version from the npm registry.
|
|
97
|
+
* Hits the lightweight dist-tags endpoint, validates the response, and returns
|
|
98
|
+
* null on any error/timeout. Nothing from the response is ever executed.
|
|
99
|
+
*/
|
|
100
|
+
export function fetchLatestVersion(name, timeoutMs = NETWORK_TIMEOUT_MS) {
|
|
101
|
+
return new Promise((resolve) => {
|
|
102
|
+
let settled = false;
|
|
103
|
+
const done = (v) => {
|
|
104
|
+
if (settled)
|
|
105
|
+
return;
|
|
106
|
+
settled = true;
|
|
107
|
+
resolve(v);
|
|
108
|
+
};
|
|
109
|
+
const req = httpsRequest({
|
|
110
|
+
method: 'GET',
|
|
111
|
+
hostname: 'registry.npmjs.org',
|
|
112
|
+
path: `/-/package/${encodeURIComponent(name)}/dist-tags`,
|
|
113
|
+
headers: { accept: 'application/json' },
|
|
114
|
+
timeout: timeoutMs,
|
|
115
|
+
}, (res) => {
|
|
116
|
+
if (res.statusCode !== 200) {
|
|
117
|
+
res.resume();
|
|
118
|
+
done(null);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let body = '';
|
|
122
|
+
let size = 0;
|
|
123
|
+
res.setEncoding('utf8');
|
|
124
|
+
res.on('data', (chunk) => {
|
|
125
|
+
size += chunk.length;
|
|
126
|
+
// Cap the response we'll buffer — defensive against a hostile/huge body.
|
|
127
|
+
if (size > 64 * 1024) {
|
|
128
|
+
req.destroy();
|
|
129
|
+
done(null);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
body += chunk;
|
|
133
|
+
});
|
|
134
|
+
res.on('end', () => {
|
|
135
|
+
try {
|
|
136
|
+
const json = JSON.parse(body);
|
|
137
|
+
const latest = json.latest;
|
|
138
|
+
done(typeof latest === 'string' && isValidVersion(latest) ? latest : null);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
done(null);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
req.on('timeout', () => {
|
|
146
|
+
req.destroy();
|
|
147
|
+
done(null);
|
|
148
|
+
});
|
|
149
|
+
req.on('error', () => done(null));
|
|
150
|
+
req.end();
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Run the update check and, when appropriate, print a single notice to stderr.
|
|
155
|
+
* Designed to be awaited right before the process exits so the notice appears
|
|
156
|
+
* AFTER the command's normal output and never interleaves.
|
|
157
|
+
*
|
|
158
|
+
* It resolves to the notice string it printed (or null), which is handy for
|
|
159
|
+
* tests; production callers can ignore the return value.
|
|
160
|
+
*/
|
|
161
|
+
export async function maybeNotifyUpdate(opts) {
|
|
162
|
+
const env = opts.env ?? process.env;
|
|
163
|
+
const stderr = opts.stderr ?? process.stderr;
|
|
164
|
+
if (!shouldCheckForUpdate({
|
|
165
|
+
machineReadable: opts.machineReadable,
|
|
166
|
+
disabled: opts.disabled,
|
|
167
|
+
stderr,
|
|
168
|
+
env,
|
|
169
|
+
})) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
// A bad/dev version (e.g. '0.0.0') can't be meaningfully compared.
|
|
173
|
+
if (!isValidVersion(opts.currentVersion))
|
|
174
|
+
return null;
|
|
175
|
+
const now = opts.now ?? Date.now;
|
|
176
|
+
const cachePath = opts.cachePath ?? defaultCachePath(env);
|
|
177
|
+
const fetchLatest = opts.fetchLatest ?? fetchLatestVersion;
|
|
178
|
+
try {
|
|
179
|
+
const cache = await readCache(cachePath);
|
|
180
|
+
let latest = cache?.latestVersion ?? null;
|
|
181
|
+
const fresh = cache !== null && now() - cache.lastCheck < CHECK_INTERVAL_MS;
|
|
182
|
+
if (!fresh) {
|
|
183
|
+
// Cache is stale or absent → check the registry, then persist the answer
|
|
184
|
+
// (and the timestamp) so we don't hit the network again for ~24h.
|
|
185
|
+
latest = await fetchLatest(PACKAGE_NAME, NETWORK_TIMEOUT_MS);
|
|
186
|
+
await writeCache(cachePath, { lastCheck: now(), latestVersion: latest });
|
|
187
|
+
}
|
|
188
|
+
if (!latest || !isValidVersion(latest))
|
|
189
|
+
return null;
|
|
190
|
+
if (compareSemver(latest, opts.currentVersion) <= 0)
|
|
191
|
+
return null;
|
|
192
|
+
const notice = formatNotice(opts.currentVersion, latest);
|
|
193
|
+
stderr.write(`${notice}\n`);
|
|
194
|
+
return notice;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// Belt-and-braces: nothing here may ever surface to the user.
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function formatNotice(current, latest) {
|
|
202
|
+
const headline = `${pc.dim('Update available')} ${pc.dim(current)} ${pc.dim('→')} ${pc.green(latest)}`;
|
|
203
|
+
const upgrade = `Run ${pc.cyan('npm i -g oauthlint')} ${pc.dim('(or')} ${pc.cyan('npx oauthlint@latest')}${pc.dim(')')}`;
|
|
204
|
+
const optOut = pc.dim('Disable: --no-update-check or NO_UPDATE_NOTIFIER=1');
|
|
205
|
+
return `\n${headline}\n${upgrade}\n${optOut}`;
|
|
206
|
+
}
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Semver: a tiny, self-contained comparator. Enough for "is latest > current",
|
|
209
|
+
// including basic prerelease ordering, without a dependency.
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
const VERSION_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
212
|
+
export function isValidVersion(v) {
|
|
213
|
+
return VERSION_RE.test(v.trim());
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Compare two semver strings. Returns >0 if a>b, <0 if a<b, 0 if equal.
|
|
217
|
+
* Honours prerelease precedence (1.0.0-rc < 1.0.0) per the semver spec's
|
|
218
|
+
* common cases. Invalid input sorts as "not greater" so we never nag wrongly.
|
|
219
|
+
*/
|
|
220
|
+
export function compareSemver(a, b) {
|
|
221
|
+
const pa = parseVersion(a);
|
|
222
|
+
const pb = parseVersion(b);
|
|
223
|
+
if (!pa || !pb)
|
|
224
|
+
return 0;
|
|
225
|
+
for (let i = 0; i < 3; i++) {
|
|
226
|
+
if (pa.main[i] !== pb.main[i])
|
|
227
|
+
return pa.main[i] - pb.main[i];
|
|
228
|
+
}
|
|
229
|
+
// Equal core. A version WITH a prerelease is lower than one without.
|
|
230
|
+
if (pa.pre.length === 0 && pb.pre.length === 0)
|
|
231
|
+
return 0;
|
|
232
|
+
if (pa.pre.length === 0)
|
|
233
|
+
return 1; // a is a release, b is a prerelease
|
|
234
|
+
if (pb.pre.length === 0)
|
|
235
|
+
return -1; // a is a prerelease, b is a release
|
|
236
|
+
const len = Math.max(pa.pre.length, pb.pre.length);
|
|
237
|
+
for (let i = 0; i < len; i++) {
|
|
238
|
+
const x = pa.pre[i];
|
|
239
|
+
const y = pb.pre[i];
|
|
240
|
+
if (x === undefined)
|
|
241
|
+
return -1; // shorter prerelease set has lower precedence
|
|
242
|
+
if (y === undefined)
|
|
243
|
+
return 1;
|
|
244
|
+
if (x === y)
|
|
245
|
+
continue;
|
|
246
|
+
const xn = /^\d+$/.test(x);
|
|
247
|
+
const yn = /^\d+$/.test(y);
|
|
248
|
+
if (xn && yn)
|
|
249
|
+
return Number(x) - Number(y);
|
|
250
|
+
if (xn)
|
|
251
|
+
return -1; // numeric identifiers are lower than alphanumeric
|
|
252
|
+
if (yn)
|
|
253
|
+
return 1;
|
|
254
|
+
return x < y ? -1 : 1; // lexical ASCII order
|
|
255
|
+
}
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
function parseVersion(v) {
|
|
259
|
+
const m = VERSION_RE.exec(v.trim());
|
|
260
|
+
if (!m)
|
|
261
|
+
return null;
|
|
262
|
+
return {
|
|
263
|
+
main: [Number(m[1]), Number(m[2]), Number(m[3])],
|
|
264
|
+
pre: m[4] ? m[4].split('.') : [],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=update-notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-notifier.js","sourceRoot":"","sources":["../../src/core/update-notifier.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, '&')
|
|
12
|
+
.replace(/</g, '<')
|
|
13
|
+
.replace(/>/g, '>')
|
|
14
|
+
.replace(/"/g, '"')
|
|
15
|
+
.replace(/'/g, ''');
|
|
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/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { buildProgram } from './cli.js';
|
|
2
2
|
export { runScan, type ScanCommandOptions } from './commands/scan.js';
|
|
3
|
+
export { runBaseline, type BaselineCommandOptions } from './commands/baseline.js';
|
|
4
|
+
export { BASELINE_VERSION, DEFAULT_BASELINE_FILE, type Baseline, type BaselineEntry, type BaselineFile, BaselineNotFoundError, BaselineParseError, buildBaseline, fingerprintFindings, loadBaseline, partitionByBaseline, serialiseBaseline, } from './core/baseline.js';
|
|
3
5
|
export { runList, type ListOptions } from './commands/list.js';
|
|
4
6
|
export { runInit, type InitOptions } from './commands/init.js';
|
|
5
7
|
export { runDoctor, type DoctorOptions } from './commands/doctor.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,eAAe,EACf,OAAO,EACP,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,eAAe,EACf,OAAO,EACP,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|