oauthlint 0.4.0 → 0.6.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 +32 -260
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +20 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/explain.d.ts +58 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +245 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/scan.d.ts +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +19 -0
- package/dist/commands/scan.js.map +1 -1
- package/dist/core/reporter.d.ts.map +1 -1
- package/dist/core/reporter.js +4 -0
- package/dist/core/reporter.js.map +1 -1
- 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 +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -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
|
@@ -5,6 +5,7 @@ export { BASELINE_VERSION, DEFAULT_BASELINE_FILE, type Baseline, type BaselineEn
|
|
|
5
5
|
export { runList, type ListOptions } from './commands/list.js';
|
|
6
6
|
export { runInit, type InitOptions } from './commands/init.js';
|
|
7
7
|
export { runDoctor, type DoctorOptions } from './commands/doctor.js';
|
|
8
|
+
export { runExplain, type ExplainOptions, type ExplainedRule, } from './commands/explain.js';
|
|
8
9
|
export { applySuppressions, isSuppressed, loadSuppressionMap, parseSuppressionsFromSource, } from './core/suppress.js';
|
|
9
10
|
export { SemgrepAdapter, SemgrepNotInstalledError } from './adapters/semgrep.js';
|
|
10
11
|
export { Reporter, type ReporterOptions } from './core/reporter.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,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"}
|
|
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,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC;AAC/B,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"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { BASELINE_VERSION, DEFAULT_BASELINE_FILE, BaselineNotFoundError, Baselin
|
|
|
5
5
|
export { runList } from './commands/list.js';
|
|
6
6
|
export { runInit } from './commands/init.js';
|
|
7
7
|
export { runDoctor } from './commands/doctor.js';
|
|
8
|
+
export { runExplain, } from './commands/explain.js';
|
|
8
9
|
export { applySuppressions, isSuppressed, loadSuppressionMap, parseSuppressionsFromSource, } from './core/suppress.js';
|
|
9
10
|
export { SemgrepAdapter, SemgrepNotInstalledError } from './adapters/semgrep.js';
|
|
10
11
|
export { Reporter } from './core/reporter.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAA2B,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAA+B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EAIrB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAsB,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,EAAwB,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;AAO5B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAA2B,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAA+B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EAIrB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAsB,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,UAAU,GAGX,MAAM,uBAAuB,CAAC;AAC/B,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,EAAwB,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;AAO5B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oauthlint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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.
|
|
50
|
+
"oauthlint-rules": "0.2.6"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "22.10.5",
|