@kernlang/review 3.2.3 → 3.3.5
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/dist/cache.js +140 -3
- package/dist/cache.js.map +1 -1
- package/dist/call-graph.d.ts +4 -1
- package/dist/call-graph.js +290 -25
- package/dist/call-graph.js.map +1 -1
- package/dist/concept-rules/contract-drift.d.ts +21 -0
- package/dist/concept-rules/contract-drift.js +66 -0
- package/dist/concept-rules/contract-drift.js.map +1 -0
- package/dist/concept-rules/cross-stack-utils.d.ts +50 -0
- package/dist/concept-rules/cross-stack-utils.js +98 -0
- package/dist/concept-rules/cross-stack-utils.js.map +1 -0
- package/dist/concept-rules/index.js +12 -1
- package/dist/concept-rules/index.js.map +1 -1
- package/dist/concept-rules/tainted-across-wire.d.ts +33 -0
- package/dist/concept-rules/tainted-across-wire.js +98 -0
- package/dist/concept-rules/tainted-across-wire.js.map +1 -0
- package/dist/concept-rules/untyped-api-response.d.ts +30 -0
- package/dist/concept-rules/untyped-api-response.js +71 -0
- package/dist/concept-rules/untyped-api-response.js.map +1 -0
- package/dist/external-tools.d.ts +36 -4
- package/dist/external-tools.js +79 -12
- package/dist/external-tools.js.map +1 -1
- package/dist/graph.js +149 -39
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +29 -4
- package/dist/index.js +329 -47
- package/dist/index.js.map +1 -1
- package/dist/inferrer.d.ts +5 -0
- package/dist/inferrer.js +1 -1
- package/dist/inferrer.js.map +1 -1
- package/dist/llm-bridge.d.ts +26 -1
- package/dist/llm-bridge.js +42 -6
- package/dist/llm-bridge.js.map +1 -1
- package/dist/llm-review.js +29 -11
- package/dist/llm-review.js.map +1 -1
- package/dist/mappers/ts-concepts.js +278 -7
- package/dist/mappers/ts-concepts.js.map +1 -1
- package/dist/public-api.d.ts +73 -0
- package/dist/public-api.js +351 -0
- package/dist/public-api.js.map +1 -0
- package/dist/reporter.d.ts +5 -0
- package/dist/reporter.js +119 -84
- package/dist/reporter.js.map +1 -1
- package/dist/review-health.d.ts +38 -0
- package/dist/review-health.js +60 -0
- package/dist/review-health.js.map +1 -0
- package/dist/rules/async.js +4 -16
- package/dist/rules/async.js.map +1 -1
- package/dist/rules/base.js +112 -87
- package/dist/rules/base.js.map +1 -1
- package/dist/rules/confidence.d.ts +2 -2
- package/dist/rules/confidence.js +32 -15
- package/dist/rules/confidence.js.map +1 -1
- package/dist/rules/dead-code.d.ts +2 -1
- package/dist/rules/dead-code.js +49 -3
- package/dist/rules/dead-code.js.map +1 -1
- package/dist/rules/index.js +131 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/kern-source-cross-file.d.ts +2 -0
- package/dist/rules/kern-source-cross-file.js +102 -0
- package/dist/rules/kern-source-cross-file.js.map +1 -0
- package/dist/rules/kern-source.js +86 -9
- package/dist/rules/kern-source.js.map +1 -1
- package/dist/rules/nextjs-app-router.js +936 -31
- package/dist/rules/nextjs-app-router.js.map +1 -1
- package/dist/rules/nextjs.js +193 -10
- package/dist/rules/nextjs.js.map +1 -1
- package/dist/rules/react-composition.js +442 -61
- package/dist/rules/react-composition.js.map +1 -1
- package/dist/rules/react-hooks.js +51 -2
- package/dist/rules/react-hooks.js.map +1 -1
- package/dist/rules/react.js +265 -49
- package/dist/rules/react.js.map +1 -1
- package/dist/rules/utils.d.ts +37 -2
- package/dist/rules/utils.js +113 -0
- package/dist/rules/utils.js.map +1 -1
- package/dist/semantic-diff.js +1 -1
- package/dist/semantic-diff.js.map +1 -1
- package/dist/taint-ast.js +228 -4
- package/dist/taint-ast.js.map +1 -1
- package/dist/taint-crossfile.d.ts +30 -2
- package/dist/taint-crossfile.js +280 -59
- package/dist/taint-crossfile.js.map +1 -1
- package/dist/taint-types.d.ts +2 -1
- package/dist/taint-types.js +32 -2
- package/dist/taint-types.js.map +1 -1
- package/dist/taint.d.ts +1 -1
- package/dist/taint.js +1 -1
- package/dist/taint.js.map +1 -1
- package/dist/types.d.ts +80 -0
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API resolver — decides whether an exported symbol is part of a package's
|
|
3
|
+
* intentional public API, so dead-export doesn't flag symbols consumed outside the
|
|
4
|
+
* analyzed graph.
|
|
5
|
+
*
|
|
6
|
+
* Sources of truth, in order:
|
|
7
|
+
* 1. package.json `exports` (string / object / conditional)
|
|
8
|
+
* 2. package.json `main` / `module` / `types`
|
|
9
|
+
* 3. package.json `bin`
|
|
10
|
+
* 4. Conservative barrel fallback: `src/index.ts(x)`, `index.ts(x)`
|
|
11
|
+
* 5. kern.config `review.publicApi` — explicit escape hatch
|
|
12
|
+
*
|
|
13
|
+
* A file listed as a package entry has ALL its named exports treated as public.
|
|
14
|
+
* Re-exports resolved through call-graph are already kept live by existing logic;
|
|
15
|
+
* this rule covers exports consumed by EXTERNAL callers who never show up in
|
|
16
|
+
* the graph (library consumers, dynamic loaders, platform entry points).
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync } from 'fs';
|
|
19
|
+
import { dirname, isAbsolute, join, resolve } from 'path';
|
|
20
|
+
import { SyntaxKind } from 'ts-morph';
|
|
21
|
+
const SRC_EXTS = ['.ts', '.tsx'];
|
|
22
|
+
/** Characters that, when present in a pattern, trigger glob expansion. */
|
|
23
|
+
const GLOB_CHARS = /[*?[]/;
|
|
24
|
+
function hasGlobChars(pattern) {
|
|
25
|
+
return GLOB_CHARS.test(pattern);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert a POSIX-style glob pattern to a `RegExp` that matches full paths.
|
|
29
|
+
*
|
|
30
|
+
* Supported syntax:
|
|
31
|
+
* - `*` — any run of non-separator chars
|
|
32
|
+
* - `**` — any path fragment, including separators (zero or more segments)
|
|
33
|
+
* - `?` — a single non-separator char
|
|
34
|
+
* - `[abc]` / `[a-z]` — character class
|
|
35
|
+
* - `[!abc]` — negated character class (POSIX-style; translated to regex `[^abc]`)
|
|
36
|
+
*
|
|
37
|
+
* All other regex metacharacters are escaped. Brace expansion (`{a,b}`) is
|
|
38
|
+
* NOT supported — keep config patterns simple; split into multiple entries.
|
|
39
|
+
*
|
|
40
|
+
* The pattern is expected to be POSIX-separated (forward slashes). The caller
|
|
41
|
+
* normalizes Windows backslashes to `/` before calling this. Consecutive `*`
|
|
42
|
+
* runs are collapsed first to prevent catastrophic backtracking on patterns
|
|
43
|
+
* like `**\/**\/**`.
|
|
44
|
+
*/
|
|
45
|
+
function globToRegex(pattern) {
|
|
46
|
+
const squashed = pattern.replace(/\*{2,}/g, '**').replace(/(?:\*\*\/)+/g, '**/');
|
|
47
|
+
let out = '';
|
|
48
|
+
let i = 0;
|
|
49
|
+
while (i < squashed.length) {
|
|
50
|
+
const c = squashed[i];
|
|
51
|
+
if (c === '*') {
|
|
52
|
+
if (squashed[i + 1] === '*') {
|
|
53
|
+
if (squashed[i + 2] === '/') {
|
|
54
|
+
out += '(?:.*/)?';
|
|
55
|
+
i += 3;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
out += '.*';
|
|
59
|
+
i += 2;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
out += '[^/]*';
|
|
64
|
+
i++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (c === '?') {
|
|
68
|
+
out += '[^/]';
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
else if (c === '[') {
|
|
72
|
+
const end = squashed.indexOf(']', i + 1);
|
|
73
|
+
if (end === -1) {
|
|
74
|
+
out += '\\[';
|
|
75
|
+
i++;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
let inner = squashed.substring(i + 1, end);
|
|
79
|
+
if (inner.startsWith('!'))
|
|
80
|
+
inner = `^${inner.slice(1)}`;
|
|
81
|
+
out += `[${inner}]`;
|
|
82
|
+
i = end + 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if ('.+^$|(){}\\'.includes(c)) {
|
|
86
|
+
out += `\\${c}`;
|
|
87
|
+
i++;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
out += c;
|
|
91
|
+
i++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return new RegExp(`^${out}$`);
|
|
95
|
+
}
|
|
96
|
+
/** Normalize path separators so glob matching works on Windows. */
|
|
97
|
+
function toPosix(p) {
|
|
98
|
+
return p.replace(/\\/g, '/');
|
|
99
|
+
}
|
|
100
|
+
function collectSpecifiers(value) {
|
|
101
|
+
if (typeof value === 'string')
|
|
102
|
+
return [value];
|
|
103
|
+
if (!value || typeof value !== 'object')
|
|
104
|
+
return [];
|
|
105
|
+
if (Array.isArray(value))
|
|
106
|
+
return value.flatMap(collectSpecifiers);
|
|
107
|
+
const out = [];
|
|
108
|
+
for (const v of Object.values(value)) {
|
|
109
|
+
out.push(...collectSpecifiers(v));
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve a package.json specifier (e.g. `./dist/index.js`) to the source file
|
|
115
|
+
* the review operates on (e.g. `./src/index.ts`). Returns undefined if nothing
|
|
116
|
+
* plausible exists on disk.
|
|
117
|
+
*/
|
|
118
|
+
export function resolveSpecifierToSrc(packageRoot, specifier, fileExists = existsSync) {
|
|
119
|
+
if (typeof specifier !== 'string' || !specifier.startsWith('.'))
|
|
120
|
+
return undefined;
|
|
121
|
+
const abs = resolve(packageRoot, specifier);
|
|
122
|
+
const stemMatch = abs.match(/^(.+?)(\.d\.ts|\.js|\.cjs|\.mjs|\.ts|\.tsx)$/);
|
|
123
|
+
const stem = stemMatch ? stemMatch[1] : abs;
|
|
124
|
+
const candidates = [];
|
|
125
|
+
if (abs.endsWith('.ts') || abs.endsWith('.tsx'))
|
|
126
|
+
candidates.push(abs);
|
|
127
|
+
for (const ext of SRC_EXTS)
|
|
128
|
+
candidates.push(`${stem}${ext}`);
|
|
129
|
+
// dist → src swap
|
|
130
|
+
const withSrc = candidates.flatMap((c) => (c.includes('/dist/') ? [c.replace('/dist/', '/src/')] : []));
|
|
131
|
+
candidates.push(...withSrc);
|
|
132
|
+
// Directory entry — try index.{ts,tsx}
|
|
133
|
+
for (const base of [abs, stem]) {
|
|
134
|
+
for (const ext of SRC_EXTS)
|
|
135
|
+
candidates.push(join(base, `index${ext}`));
|
|
136
|
+
}
|
|
137
|
+
for (const c of candidates) {
|
|
138
|
+
if (fileExists(c))
|
|
139
|
+
return c;
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* For a parsed package.json at packageRoot, return all source files that act as
|
|
145
|
+
* public entry points. Missing files are silently dropped.
|
|
146
|
+
*/
|
|
147
|
+
export function resolvePackageEntryFiles(packageRoot, pkg, fileExists = existsSync) {
|
|
148
|
+
const specs = new Set();
|
|
149
|
+
if (pkg.exports !== undefined) {
|
|
150
|
+
for (const s of collectSpecifiers(pkg.exports))
|
|
151
|
+
specs.add(s);
|
|
152
|
+
}
|
|
153
|
+
if (typeof pkg.main === 'string')
|
|
154
|
+
specs.add(pkg.main);
|
|
155
|
+
if (typeof pkg.module === 'string')
|
|
156
|
+
specs.add(pkg.module);
|
|
157
|
+
if (typeof pkg.types === 'string')
|
|
158
|
+
specs.add(pkg.types);
|
|
159
|
+
if (typeof pkg.bin === 'string') {
|
|
160
|
+
specs.add(pkg.bin);
|
|
161
|
+
}
|
|
162
|
+
else if (pkg.bin && typeof pkg.bin === 'object') {
|
|
163
|
+
for (const v of Object.values(pkg.bin)) {
|
|
164
|
+
if (typeof v === 'string')
|
|
165
|
+
specs.add(v);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Conservative barrel fallback — only contributes if the file actually exists.
|
|
169
|
+
for (const ext of SRC_EXTS) {
|
|
170
|
+
specs.add(`./src/index${ext}`);
|
|
171
|
+
specs.add(`./index${ext}`);
|
|
172
|
+
}
|
|
173
|
+
const resolved = new Set();
|
|
174
|
+
for (const s of specs) {
|
|
175
|
+
const p = resolveSpecifierToSrc(packageRoot, s, fileExists);
|
|
176
|
+
if (p)
|
|
177
|
+
resolved.add(p);
|
|
178
|
+
}
|
|
179
|
+
return [...resolved];
|
|
180
|
+
}
|
|
181
|
+
function findPackageRoot(startFile, cache) {
|
|
182
|
+
const startDir = dirname(startFile);
|
|
183
|
+
if (cache.has(startDir))
|
|
184
|
+
return cache.get(startDir) ?? null;
|
|
185
|
+
const visited = [];
|
|
186
|
+
let dir = startDir;
|
|
187
|
+
for (let i = 0; i < 30; i++) {
|
|
188
|
+
if (cache.has(dir)) {
|
|
189
|
+
const hit = cache.get(dir) ?? null;
|
|
190
|
+
for (const v of visited)
|
|
191
|
+
cache.set(v, hit);
|
|
192
|
+
return hit;
|
|
193
|
+
}
|
|
194
|
+
visited.push(dir);
|
|
195
|
+
if (existsSync(join(dir, 'package.json'))) {
|
|
196
|
+
for (const v of visited)
|
|
197
|
+
cache.set(v, dir);
|
|
198
|
+
return dir;
|
|
199
|
+
}
|
|
200
|
+
const parent = dirname(dir);
|
|
201
|
+
if (parent === dir)
|
|
202
|
+
break;
|
|
203
|
+
dir = parent;
|
|
204
|
+
}
|
|
205
|
+
for (const v of visited)
|
|
206
|
+
cache.set(v, null);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Build a public-API map by walking up from each file to its nearest package.json
|
|
211
|
+
* and collecting declared entry points. Applies config overrides on top.
|
|
212
|
+
*/
|
|
213
|
+
export function buildPublicApiMap(filePaths, overrides) {
|
|
214
|
+
const rootCache = new Map();
|
|
215
|
+
const roots = new Set();
|
|
216
|
+
for (const fp of filePaths) {
|
|
217
|
+
const r = findPackageRoot(fp, rootCache);
|
|
218
|
+
if (r)
|
|
219
|
+
roots.add(r);
|
|
220
|
+
}
|
|
221
|
+
const entryFiles = new Set();
|
|
222
|
+
for (const root of roots) {
|
|
223
|
+
const pkgPath = join(root, 'package.json');
|
|
224
|
+
let pkg;
|
|
225
|
+
try {
|
|
226
|
+
pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
for (const f of resolvePackageEntryFiles(root, pkg))
|
|
232
|
+
entryFiles.add(f);
|
|
233
|
+
}
|
|
234
|
+
const explicitSymbols = new Set();
|
|
235
|
+
const projectRoot = overrides?.projectRoot ?? process.cwd();
|
|
236
|
+
if (overrides?.files) {
|
|
237
|
+
for (const pattern of overrides.files) {
|
|
238
|
+
if (typeof pattern !== 'string' || pattern.length === 0)
|
|
239
|
+
continue;
|
|
240
|
+
const abs = isAbsolute(pattern) ? pattern : resolve(projectRoot, pattern);
|
|
241
|
+
// Always add the literal resolved path. This preserves the pre-glob
|
|
242
|
+
// behavior ("files listed here are public even if absent from the
|
|
243
|
+
// reviewed graph") AND gives the right answer for Next.js-style
|
|
244
|
+
// literal brackets like "src/app/[slug]/page.tsx" — the bracketed
|
|
245
|
+
// segment looks like a glob character class but is actually part of
|
|
246
|
+
// the filename. Glob expansion still runs below, so users who meant
|
|
247
|
+
// [...] as a class get that behavior too.
|
|
248
|
+
entryFiles.add(abs);
|
|
249
|
+
if (hasGlobChars(pattern)) {
|
|
250
|
+
const regex = globToRegex(toPosix(abs));
|
|
251
|
+
for (const fp of filePaths) {
|
|
252
|
+
if (regex.test(toPosix(fp)))
|
|
253
|
+
entryFiles.add(fp);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (overrides?.symbols) {
|
|
259
|
+
for (const spec of overrides.symbols) {
|
|
260
|
+
if (typeof spec !== 'string')
|
|
261
|
+
continue;
|
|
262
|
+
const idx = spec.lastIndexOf('#');
|
|
263
|
+
if (idx <= 0 || idx === spec.length - 1)
|
|
264
|
+
continue;
|
|
265
|
+
const rawPath = spec.slice(0, idx);
|
|
266
|
+
const name = spec.slice(idx + 1);
|
|
267
|
+
const abs = isAbsolute(rawPath) ? rawPath : resolve(projectRoot, rawPath);
|
|
268
|
+
explicitSymbols.add(`${abs}#${name}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return { entryFiles, explicitSymbols };
|
|
272
|
+
}
|
|
273
|
+
export const EMPTY_PUBLIC_API = {
|
|
274
|
+
entryFiles: new Set(),
|
|
275
|
+
explicitSymbols: new Set(),
|
|
276
|
+
};
|
|
277
|
+
export function isPublicApi(map, filePath, exportName) {
|
|
278
|
+
if (map.entryFiles.has(filePath))
|
|
279
|
+
return true;
|
|
280
|
+
if (map.explicitSymbols.has(`${filePath}#${exportName}`))
|
|
281
|
+
return true;
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
// Re-export propagation.
|
|
285
|
+
//
|
|
286
|
+
// A curated barrel (`export { foo } from './worker.js'`) makes worker.ts#foo
|
|
287
|
+
// part of the package's public API even if no file in the graph imports
|
|
288
|
+
// worker.ts directly. Without this, dead-export would fire on every symbol
|
|
289
|
+
// a package re-exports but never uses internally: the exact FP that Agon
|
|
290
|
+
// hit before Phase 1, and which the single-file buildPublicApiMap cannot
|
|
291
|
+
// catch on its own.
|
|
292
|
+
function getDeclName(decl, fallback) {
|
|
293
|
+
const maybeNamed = decl;
|
|
294
|
+
if (typeof maybeNamed.getName === 'function') {
|
|
295
|
+
const name = maybeNamed.getName();
|
|
296
|
+
if (name)
|
|
297
|
+
return name;
|
|
298
|
+
}
|
|
299
|
+
if (decl.getKindName() === 'ExportAssignment') {
|
|
300
|
+
const expr = decl.getExpression?.();
|
|
301
|
+
if (expr?.getKind() === SyntaxKind.Identifier)
|
|
302
|
+
return expr.getText();
|
|
303
|
+
}
|
|
304
|
+
return fallback;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Expand `map.entryFiles` through re-export chains: for each public entry,
|
|
308
|
+
* walk `getExportedDeclarations()` and mark every upstream `(file, symbol)`
|
|
309
|
+
* that the entry re-exports as also public. Returns a new map; the input is
|
|
310
|
+
* not mutated.
|
|
311
|
+
*
|
|
312
|
+
* `sourceFileFor(path)` should return the ts-morph SourceFile for an
|
|
313
|
+
* absolute path (or undefined when the file is outside the analyzed graph).
|
|
314
|
+
*/
|
|
315
|
+
export function expandPublicApiThroughReExports(map, sourceFileFor) {
|
|
316
|
+
const entryFiles = new Set(map.entryFiles);
|
|
317
|
+
const explicitSymbols = new Set(map.explicitSymbols);
|
|
318
|
+
// BFS through re-export chains so a barrel-of-barrels propagates too.
|
|
319
|
+
const frontier = [...map.entryFiles];
|
|
320
|
+
const seen = new Set(map.entryFiles);
|
|
321
|
+
while (frontier.length > 0) {
|
|
322
|
+
const entry = frontier.shift();
|
|
323
|
+
const sf = sourceFileFor(entry);
|
|
324
|
+
if (!sf)
|
|
325
|
+
continue;
|
|
326
|
+
let exported;
|
|
327
|
+
try {
|
|
328
|
+
exported = sf.getExportedDeclarations();
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
for (const [exportName, decls] of exported) {
|
|
334
|
+
for (const decl of decls) {
|
|
335
|
+
const declFile = decl.getSourceFile().getFilePath();
|
|
336
|
+
if (declFile === entry)
|
|
337
|
+
continue;
|
|
338
|
+
explicitSymbols.add(`${declFile}#${getDeclName(decl, exportName)}`);
|
|
339
|
+
if (!seen.has(declFile)) {
|
|
340
|
+
seen.add(declFile);
|
|
341
|
+
// An upstream barrel's own re-exports should also be followed;
|
|
342
|
+
// don't mark it as an entry (only declared entries get that), but
|
|
343
|
+
// do walk it so transitive symbols are captured.
|
|
344
|
+
frontier.push(declFile);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return { entryFiles, explicitSymbols };
|
|
350
|
+
}
|
|
351
|
+
//# sourceMappingURL=public-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"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"}
|
package/dist/reporter.d.ts
CHANGED
|
@@ -28,6 +28,11 @@ export declare function formatReportJSON(report: ReviewReport, options?: {
|
|
|
28
28
|
includeLLMPrompt?: boolean;
|
|
29
29
|
}): string;
|
|
30
30
|
export declare function formatSARIF(reports: ReviewReport[]): string;
|
|
31
|
+
export interface SARIFMetadataOptions {
|
|
32
|
+
suppressedFindings?: ReviewFinding[];
|
|
33
|
+
getBaselineStatus?: (report: ReviewReport, finding: ReviewFinding) => 'new' | 'existing' | undefined;
|
|
34
|
+
}
|
|
35
|
+
export declare function formatSARIFWithMetadata(reports: ReviewReport[], options?: SARIFMetadataOptions): string;
|
|
31
36
|
/**
|
|
32
37
|
* Format SARIF with suppression metadata.
|
|
33
38
|
* Suppressed findings appear with a `suppressions` array per SARIF v2.1.0 section 3.35.
|
package/dist/reporter.js
CHANGED
|
@@ -191,6 +191,17 @@ export function formatReport(report, config) {
|
|
|
191
191
|
const lines = [];
|
|
192
192
|
lines.push(` @kernlang/review — analyzing ${report.filePath}`);
|
|
193
193
|
lines.push('');
|
|
194
|
+
// Render health banner BEFORE findings. Users need to know which subsystems skipped/fell
|
|
195
|
+
// back before they interpret "0 findings" as "the file is clean" — a clean report and a
|
|
196
|
+
// report where half the checks didn't run used to look identical in the CLI.
|
|
197
|
+
if (report.health && report.health.entries.length > 0) {
|
|
198
|
+
const label = report.health.status === 'partial' ? 'PARTIAL' : 'DEGRADED';
|
|
199
|
+
lines.push(` [${label}] Review ran in ${report.health.status} mode:`);
|
|
200
|
+
for (const entry of report.health.entries) {
|
|
201
|
+
lines.push(` - ${entry.subsystem} (${entry.kind}): ${entry.message}`);
|
|
202
|
+
}
|
|
203
|
+
lines.push('');
|
|
204
|
+
}
|
|
194
205
|
if (report.inferred.length > 0) {
|
|
195
206
|
lines.push(` KERN-expressible (${report.inferred.length} constructs):`);
|
|
196
207
|
for (const r of report.inferred) {
|
|
@@ -308,6 +319,10 @@ export function formatReportJSON(report, options) {
|
|
|
308
319
|
}
|
|
309
320
|
// ── SARIF Format ─────────────────────────────────────────────────────────
|
|
310
321
|
export function formatSARIF(reports) {
|
|
322
|
+
return formatSARIFWithMetadata(reports);
|
|
323
|
+
}
|
|
324
|
+
export function formatSARIFWithMetadata(reports, options = {}) {
|
|
325
|
+
const { suppressedFindings, getBaselineStatus } = options;
|
|
311
326
|
const sarif = {
|
|
312
327
|
$schema: 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json',
|
|
313
328
|
version: '2.1.0',
|
|
@@ -321,118 +336,138 @@ export function formatSARIF(reports) {
|
|
|
321
336
|
},
|
|
322
337
|
},
|
|
323
338
|
results: [],
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (!rules.has(f.ruleId)) {
|
|
331
|
-
rules.add(f.ruleId);
|
|
332
|
-
sarif.runs[0].tool.driver.rules.push({
|
|
333
|
-
id: f.ruleId,
|
|
334
|
-
shortDescription: { text: f.ruleId },
|
|
335
|
-
helpUri: `https://github.com/kern-lang/kern-lang/blob/main/docs/rules.md#${f.ruleId}`,
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
const sarifLevel = f.severity === 'error' ? 'error' : f.severity === 'warning' ? 'warning' : 'note';
|
|
339
|
-
const result = {
|
|
340
|
-
ruleId: f.ruleId,
|
|
341
|
-
level: sarifLevel,
|
|
342
|
-
message: { text: f.message },
|
|
343
|
-
locations: [
|
|
339
|
+
// SARIF spec 3.20: invocations describe tool-execution events. toolExecutionNotifications
|
|
340
|
+
// carries messages FROM the tool (not findings ABOUT the code), which is exactly where
|
|
341
|
+
// "ESLint skipped" / "call graph failed" belong. Without this, enterprise consumers of
|
|
342
|
+
// SARIF (GitHub code scanning, Azure DevOps) see 0 results and assume "clean" when the
|
|
343
|
+
// truth is "half the analyzers didn't run."
|
|
344
|
+
invocations: [
|
|
344
345
|
{
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
region: {
|
|
348
|
-
startLine: f.primarySpan.startLine,
|
|
349
|
-
startColumn: f.primarySpan.startCol,
|
|
350
|
-
endLine: f.primarySpan.endLine,
|
|
351
|
-
endColumn: f.primarySpan.endCol,
|
|
352
|
-
},
|
|
353
|
-
},
|
|
346
|
+
executionSuccessful: true,
|
|
347
|
+
toolExecutionNotifications: [],
|
|
354
348
|
},
|
|
355
349
|
],
|
|
356
|
-
};
|
|
357
|
-
// SARIF result.rank is 0.0–100.0 per spec; kern/confidence stays 0–1
|
|
358
|
-
if (f.confidence !== undefined) {
|
|
359
|
-
result.rank = f.confidence * 100;
|
|
360
|
-
result.properties = { 'kern/confidence': f.confidence };
|
|
361
|
-
}
|
|
362
|
-
sarif.runs[0].results.push(result);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return JSON.stringify(sarif, null, 2);
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Format SARIF with suppression metadata.
|
|
369
|
-
* Suppressed findings appear with a `suppressions` array per SARIF v2.1.0 section 3.35.
|
|
370
|
-
*/
|
|
371
|
-
export function formatSARIFWithSuppressions(reports, suppressedFindings) {
|
|
372
|
-
const sarif = {
|
|
373
|
-
$schema: 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json',
|
|
374
|
-
version: '2.1.0',
|
|
375
|
-
runs: [
|
|
376
|
-
{
|
|
377
|
-
tool: {
|
|
378
|
-
driver: {
|
|
379
|
-
name: '@kernlang/review',
|
|
380
|
-
version: '2.0.0',
|
|
381
|
-
rules: [],
|
|
382
|
-
},
|
|
383
|
-
},
|
|
384
|
-
results: [],
|
|
385
350
|
},
|
|
386
351
|
],
|
|
387
352
|
};
|
|
353
|
+
// Aggregate + dedupe health entries across every report. The in-process ReviewHealthBuilder
|
|
354
|
+
// already dedupes within one review, but reports from different reviewFile calls can each
|
|
355
|
+
// carry their own builders — dedupe again here by (subsystem, kind) so SARIF output emits one
|
|
356
|
+
// notification per distinct failure mode, not one per report.
|
|
357
|
+
const healthSeen = new Set();
|
|
358
|
+
let anyError = false;
|
|
359
|
+
for (const report of reports) {
|
|
360
|
+
for (const entry of report.health?.entries ?? []) {
|
|
361
|
+
const key = `${entry.subsystem}:${entry.kind}`;
|
|
362
|
+
if (healthSeen.has(key))
|
|
363
|
+
continue;
|
|
364
|
+
healthSeen.add(key);
|
|
365
|
+
// SARIF notification levels map to health kinds:
|
|
366
|
+
// skipped -> note (optional subsystem absent — nothing wrong)
|
|
367
|
+
// fallback -> warning (analysis degraded but still ran)
|
|
368
|
+
// error -> error (subsystem failed outright; findings may be missing)
|
|
369
|
+
const level = entry.kind === 'error' ? 'error' : entry.kind === 'fallback' ? 'warning' : 'note';
|
|
370
|
+
if (entry.kind === 'error')
|
|
371
|
+
anyError = true;
|
|
372
|
+
sarif.runs[0].invocations[0].toolExecutionNotifications.push({
|
|
373
|
+
descriptor: { id: `kern/health/${entry.subsystem}` },
|
|
374
|
+
level,
|
|
375
|
+
message: { text: entry.message },
|
|
376
|
+
properties: {
|
|
377
|
+
'kern/subsystem': entry.subsystem,
|
|
378
|
+
'kern/kind': entry.kind,
|
|
379
|
+
...(entry.detail !== undefined ? { 'kern/detail': entry.detail } : {}),
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Spec 3.20.6: executionSuccessful=false means the tool raised any error-level notification.
|
|
385
|
+
// Setting this correctly lets CI systems distinguish "tool ran cleanly, zero findings" from
|
|
386
|
+
// "tool errored on a subsystem, zero findings from that subsystem don't mean safe code."
|
|
387
|
+
if (anyError) {
|
|
388
|
+
sarif.runs[0].invocations[0].executionSuccessful = false;
|
|
389
|
+
}
|
|
388
390
|
const rules = new Set();
|
|
389
|
-
// Include file path in suppression key to avoid cross-file fingerprint collisions
|
|
390
391
|
const suppressedSet = new Set(suppressedFindings?.map((f) => `${f.primarySpan.file}:${f.fingerprint}`) ?? []);
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
rules.add(f.ruleId);
|
|
392
|
+
function pushResult(finding, report, overrides = {}) {
|
|
393
|
+
if (!rules.has(finding.ruleId)) {
|
|
394
|
+
rules.add(finding.ruleId);
|
|
395
395
|
sarif.runs[0].tool.driver.rules.push({
|
|
396
|
-
id:
|
|
397
|
-
shortDescription: { text:
|
|
398
|
-
helpUri: `https://github.com/kern-lang/kern-lang/blob/main/docs/rules.md#${
|
|
396
|
+
id: finding.ruleId,
|
|
397
|
+
shortDescription: { text: finding.ruleId },
|
|
398
|
+
helpUri: `https://github.com/kern-lang/kern-lang/blob/main/docs/rules.md#${finding.ruleId}`,
|
|
399
399
|
});
|
|
400
400
|
}
|
|
401
|
-
const sarifLevel =
|
|
401
|
+
const sarifLevel = finding.severity === 'error' ? 'error' : finding.severity === 'warning' ? 'warning' : 'note';
|
|
402
|
+
const baselineStatus = report ? getBaselineStatus?.(report, finding) : undefined;
|
|
403
|
+
const properties = {};
|
|
402
404
|
const result = {
|
|
403
|
-
ruleId:
|
|
405
|
+
ruleId: finding.ruleId,
|
|
404
406
|
level: sarifLevel,
|
|
405
|
-
message: { text:
|
|
407
|
+
message: { text: finding.message },
|
|
406
408
|
locations: [
|
|
407
409
|
{
|
|
408
410
|
physicalLocation: {
|
|
409
|
-
artifactLocation: { uri:
|
|
411
|
+
artifactLocation: { uri: finding.primarySpan.file },
|
|
410
412
|
region: {
|
|
411
|
-
startLine:
|
|
412
|
-
startColumn:
|
|
413
|
-
endLine:
|
|
414
|
-
endColumn:
|
|
413
|
+
startLine: finding.primarySpan.startLine,
|
|
414
|
+
startColumn: finding.primarySpan.startCol,
|
|
415
|
+
endLine: finding.primarySpan.endLine,
|
|
416
|
+
endColumn: finding.primarySpan.endCol,
|
|
415
417
|
},
|
|
416
418
|
},
|
|
417
419
|
},
|
|
418
420
|
],
|
|
419
421
|
};
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
result.
|
|
422
|
+
// SARIF result.rank is 0.0–100.0 per spec; kern/confidence stays 0–1
|
|
423
|
+
if (finding.confidence !== undefined) {
|
|
424
|
+
result.rank = finding.confidence * 100;
|
|
425
|
+
properties['kern/confidence'] = finding.confidence;
|
|
423
426
|
}
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
427
|
+
if (baselineStatus) {
|
|
428
|
+
properties['kern/baselineStatus'] = baselineStatus;
|
|
429
|
+
}
|
|
430
|
+
if (Object.keys(properties).length > 0) {
|
|
431
|
+
result.properties = properties;
|
|
432
|
+
}
|
|
433
|
+
const suppressions = [];
|
|
434
|
+
if (overrides.isSuppressedInSource || suppressedSet.has(`${finding.primarySpan.file}:${finding.fingerprint}`)) {
|
|
435
|
+
suppressions.push({
|
|
436
|
+
kind: 'inSource',
|
|
437
|
+
justification: 'kern-ignore directive',
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
if (baselineStatus === 'existing') {
|
|
441
|
+
suppressions.push({
|
|
442
|
+
kind: 'external',
|
|
443
|
+
justification: 'Present in review baseline',
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
if (suppressions.length > 0) {
|
|
447
|
+
result.suppressions = suppressions;
|
|
431
448
|
}
|
|
432
449
|
sarif.runs[0].results.push(result);
|
|
433
450
|
}
|
|
451
|
+
for (const report of reports) {
|
|
452
|
+
for (const finding of report.findings) {
|
|
453
|
+
pushResult(finding, report);
|
|
454
|
+
}
|
|
455
|
+
for (const finding of report.suppressedFindings ?? []) {
|
|
456
|
+
pushResult(finding, report, { isSuppressedInSource: true });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
for (const finding of suppressedFindings ?? []) {
|
|
460
|
+
pushResult(finding, undefined, { isSuppressedInSource: true });
|
|
461
|
+
}
|
|
434
462
|
return JSON.stringify(sarif, null, 2);
|
|
435
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Format SARIF with suppression metadata.
|
|
466
|
+
* Suppressed findings appear with a `suppressions` array per SARIF v2.1.0 section 3.35.
|
|
467
|
+
*/
|
|
468
|
+
export function formatSARIFWithSuppressions(reports, suppressedFindings) {
|
|
469
|
+
return formatSARIFWithMetadata(reports, { suppressedFindings });
|
|
470
|
+
}
|
|
436
471
|
// ── Multi-file Summary ───────────────────────────────────────────────────
|
|
437
472
|
export function formatSummary(reports) {
|
|
438
473
|
const lines = [];
|