@kernlang/review 3.3.9 → 3.4.1
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/call-graph.d.ts +10 -0
- package/dist/call-graph.js +138 -9
- package/dist/call-graph.js.map +1 -1
- package/dist/concept-rules/auth-drift.js +2 -0
- package/dist/concept-rules/auth-drift.js.map +1 -1
- package/dist/concept-rules/auth-propagation-drift.js +2 -0
- package/dist/concept-rules/auth-propagation-drift.js.map +1 -1
- package/dist/concept-rules/body-shape-drift.d.ts +1 -1
- package/dist/concept-rules/body-shape-drift.js +5 -3
- package/dist/concept-rules/body-shape-drift.js.map +1 -1
- package/dist/concept-rules/contract-drift.js +3 -1
- package/dist/concept-rules/contract-drift.js.map +1 -1
- package/dist/concept-rules/contract-method-drift.js +2 -0
- package/dist/concept-rules/contract-method-drift.js.map +1 -1
- package/dist/concept-rules/index.js +2 -33
- package/dist/concept-rules/index.js.map +1 -1
- package/dist/concept-rules/request-validation-drift.js +3 -0
- package/dist/concept-rules/request-validation-drift.js.map +1 -1
- package/dist/concept-rules/root-cause.d.ts +4 -0
- package/dist/concept-rules/root-cause.js +31 -0
- package/dist/concept-rules/root-cause.js.map +1 -0
- package/dist/concept-rules/unbounded-collection-query.js +2 -0
- package/dist/concept-rules/unbounded-collection-query.js.map +1 -1
- package/dist/concept-rules/unhandled-api-error-shape.js +2 -0
- package/dist/concept-rules/unhandled-api-error-shape.js.map +1 -1
- package/dist/default-export.d.ts +41 -0
- package/dist/default-export.js +76 -0
- package/dist/default-export.js.map +1 -0
- package/dist/eval.d.ts +67 -0
- package/dist/eval.js +177 -0
- package/dist/eval.js.map +1 -0
- package/dist/file-context.js +32 -13
- package/dist/file-context.js.map +1 -1
- package/dist/file-role.d.ts +6 -0
- package/dist/file-role.js +27 -0
- package/dist/file-role.js.map +1 -1
- package/dist/framework-seeds.d.ts +46 -0
- package/dist/framework-seeds.js +245 -0
- package/dist/framework-seeds.js.map +1 -0
- package/dist/git-env.d.ts +1 -0
- package/dist/git-env.js +25 -0
- package/dist/git-env.js.map +1 -0
- package/dist/graph.js +246 -21
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.js +200 -56
- package/dist/index.js.map +1 -1
- package/dist/llm-bridge.d.ts +35 -9
- package/dist/llm-bridge.js +50 -23
- package/dist/llm-bridge.js.map +1 -1
- package/dist/mappers/ts-concepts.js +87 -20
- package/dist/mappers/ts-concepts.js.map +1 -1
- package/dist/path-canonical.d.ts +34 -0
- package/dist/path-canonical.js +85 -0
- package/dist/path-canonical.js.map +1 -0
- package/dist/policy.d.ts +22 -0
- package/dist/policy.js +47 -0
- package/dist/policy.js.map +1 -0
- package/dist/project-context.d.ts +135 -0
- package/dist/project-context.js +563 -0
- package/dist/project-context.js.map +1 -0
- package/dist/public-api.d.ts +21 -0
- package/dist/public-api.js +17 -2
- package/dist/public-api.js.map +1 -1
- package/dist/reporter.js +22 -0
- package/dist/reporter.js.map +1 -1
- package/dist/rule-quality.d.ts +58 -0
- package/dist/rule-quality.js +357 -0
- package/dist/rule-quality.js.map +1 -0
- package/dist/rules/dead-code.d.ts +2 -2
- package/dist/rules/dead-code.js +88 -4
- package/dist/rules/dead-code.js.map +1 -1
- package/dist/rules/index.d.ts +22 -0
- package/dist/rules/index.js +32 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/kern-source.d.ts +4 -0
- package/dist/rules/kern-source.js +183 -0
- package/dist/rules/kern-source.js.map +1 -1
- package/dist/rules/react.js +52 -3
- package/dist/rules/react.js.map +1 -1
- package/dist/rules/suggest-kern-primitive.js +0 -1
- package/dist/rules/suggest-kern-primitive.js.map +1 -1
- package/dist/semantic-diff.js +2 -0
- package/dist/semantic-diff.js.map +1 -1
- package/dist/suppression/apply-suppression.js +2 -0
- package/dist/suppression/apply-suppression.js.map +1 -1
- package/dist/suppression/parse-directives.d.ts +13 -5
- package/dist/suppression/parse-directives.js +62 -8
- package/dist/suppression/parse-directives.js.map +1 -1
- package/dist/suppression/types.d.ts +9 -0
- package/dist/suppression/types.js +6 -1
- package/dist/suppression/types.js.map +1 -1
- package/dist/taint-crossfile.js +15 -8
- package/dist/taint-crossfile.js.map +1 -1
- package/dist/telemetry.d.ts +126 -0
- package/dist/telemetry.js +303 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +165 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { getRuleRegistry } from './rules/index.js';
|
|
2
|
+
const GUARD_ADVISORY_RULES = new Set([
|
|
3
|
+
'dead-export',
|
|
4
|
+
'sync-in-async',
|
|
5
|
+
'cognitive-complexity',
|
|
6
|
+
'handler-size',
|
|
7
|
+
'unhandled-async',
|
|
8
|
+
]);
|
|
9
|
+
let ruleInfoById;
|
|
10
|
+
function getRuleInfoMap() {
|
|
11
|
+
if (ruleInfoById)
|
|
12
|
+
return ruleInfoById;
|
|
13
|
+
ruleInfoById = new Map();
|
|
14
|
+
for (const info of getRuleRegistry()) {
|
|
15
|
+
if (!ruleInfoById.has(info.id)) {
|
|
16
|
+
ruleInfoById.set(info.id, info);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return ruleInfoById;
|
|
20
|
+
}
|
|
21
|
+
function inferredPrecision(info) {
|
|
22
|
+
return info.precision ?? 'medium';
|
|
23
|
+
}
|
|
24
|
+
function inferredLifecycle(info, precision) {
|
|
25
|
+
if (info.lifecycle)
|
|
26
|
+
return info.lifecycle;
|
|
27
|
+
if (precision === 'experimental')
|
|
28
|
+
return 'experimental';
|
|
29
|
+
if ((info.rolloutPhase ?? 0) > 0)
|
|
30
|
+
return 'candidate';
|
|
31
|
+
return 'stable';
|
|
32
|
+
}
|
|
33
|
+
function inferredCiDefault(info, precision, lifecycle) {
|
|
34
|
+
if (info.ciDefault)
|
|
35
|
+
return info.ciDefault;
|
|
36
|
+
if (info.severity === 'info')
|
|
37
|
+
return 'off';
|
|
38
|
+
if (precision === 'experimental' || lifecycle === 'experimental')
|
|
39
|
+
return 'off';
|
|
40
|
+
if (precision === 'medium' || lifecycle === 'candidate')
|
|
41
|
+
return 'guarded';
|
|
42
|
+
return 'on';
|
|
43
|
+
}
|
|
44
|
+
export function getRuleQualityProfile(ruleId) {
|
|
45
|
+
const info = getRuleInfoMap().get(ruleId);
|
|
46
|
+
if (!info)
|
|
47
|
+
return undefined;
|
|
48
|
+
const precision = inferredPrecision(info);
|
|
49
|
+
const lifecycle = inferredLifecycle(info, precision);
|
|
50
|
+
const ciDefault = inferredCiDefault(info, precision, lifecycle);
|
|
51
|
+
return { ...info, precision, lifecycle, ciDefault };
|
|
52
|
+
}
|
|
53
|
+
export function isRulePromotedForCi(ruleId) {
|
|
54
|
+
const profile = getRuleQualityProfile(ruleId);
|
|
55
|
+
return profile?.ciDefault === 'on' && profile.lifecycle === 'stable';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Apply review-mode calibration after confidence assignment and before suppression.
|
|
59
|
+
*
|
|
60
|
+
* Guard mode is the default for PR/CI review. It keeps high-signal errors visible,
|
|
61
|
+
* but softens advisory and experimental findings to info so they do not compete
|
|
62
|
+
* with correctness/security findings. Audit mode preserves original severities for
|
|
63
|
+
* local investigations.
|
|
64
|
+
*
|
|
65
|
+
* Idempotent: each finding is calibrated at most once per process lifetime. The
|
|
66
|
+
* `calibrated` flag prevents compounding multipliers when graph-mode rerun unions
|
|
67
|
+
* already-calibrated per-file findings with newly-injected cross-file findings.
|
|
68
|
+
*
|
|
69
|
+
* Records each acting stage on `finding.calibrationTrail` so audit policy can
|
|
70
|
+
* surface the calibration chain without recomputing it.
|
|
71
|
+
*/
|
|
72
|
+
export function applyRuleQualityCalibration(findings, config) {
|
|
73
|
+
if (config?.crossStackMode === 'audit')
|
|
74
|
+
return;
|
|
75
|
+
for (const finding of findings) {
|
|
76
|
+
if (finding.calibrated)
|
|
77
|
+
continue;
|
|
78
|
+
const profile = getRuleQualityProfile(finding.ruleId);
|
|
79
|
+
const shouldDemote = GUARD_ADVISORY_RULES.has(finding.ruleId) ||
|
|
80
|
+
profile?.ciDefault === 'off' ||
|
|
81
|
+
profile?.precision === 'experimental' ||
|
|
82
|
+
profile?.lifecycle === 'experimental';
|
|
83
|
+
if (shouldDemote && finding.severity !== 'error') {
|
|
84
|
+
const before = finding.severity;
|
|
85
|
+
finding.severity = 'info';
|
|
86
|
+
recordCalibration(finding, {
|
|
87
|
+
stage: 'rule-quality:demote-advisory',
|
|
88
|
+
factor: 1,
|
|
89
|
+
reason: GUARD_ADVISORY_RULES.has(finding.ruleId)
|
|
90
|
+
? 'rule on guard-advisory list'
|
|
91
|
+
: `rule lifecycle=${profile?.lifecycle ?? 'unknown'} precision=${profile?.precision ?? 'unknown'}`,
|
|
92
|
+
beforeSeverity: before,
|
|
93
|
+
afterSeverity: 'info',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if ((profile?.precision === 'experimental' || profile?.lifecycle === 'experimental') &&
|
|
97
|
+
finding.confidence !== undefined &&
|
|
98
|
+
finding.confidence > 0.6) {
|
|
99
|
+
const before = finding.confidence;
|
|
100
|
+
finding.confidence = 0.6;
|
|
101
|
+
recordCalibration(finding, {
|
|
102
|
+
stage: 'rule-quality:experimental-cap',
|
|
103
|
+
factor: 0.6 / before,
|
|
104
|
+
reason: 'experimental rule capped at 0.6',
|
|
105
|
+
beforeConfidence: before,
|
|
106
|
+
afterConfidence: 0.6,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
finding.calibrated = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Append a calibration stage to a finding's trail. */
|
|
113
|
+
export function recordCalibration(finding, stage) {
|
|
114
|
+
if (!finding.calibrationTrail)
|
|
115
|
+
finding.calibrationTrail = [];
|
|
116
|
+
finding.calibrationTrail.push(stage);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Per-rule confidence multipliers per file role. Default factor is 1.0 (no change).
|
|
120
|
+
*
|
|
121
|
+
* Design constraints driven by the Phase 1 red-team:
|
|
122
|
+
* - **No wildcards.** Every (role, ruleId) pair must be explicit. A `'*':0`
|
|
123
|
+
* entry would silently nuke security findings on any file misclassified as
|
|
124
|
+
* codegen — every entry is enumerated to avoid that.
|
|
125
|
+
* - **Security-layer rules are excluded by construction** at lookup time
|
|
126
|
+
* (see `roleMultiplierFor`). Even if a future edit accidentally lists a
|
|
127
|
+
* security rule here, it is ignored.
|
|
128
|
+
* - **NaN-safe.** Lookup returns 1 for any unmapped pair; we never let
|
|
129
|
+
* `undefined * confidence` propagate.
|
|
130
|
+
*/
|
|
131
|
+
const ROLE_MULTIPLIER = {
|
|
132
|
+
barrel: {
|
|
133
|
+
'dead-export': 0,
|
|
134
|
+
'cognitive-complexity': 0,
|
|
135
|
+
'handler-size': 0,
|
|
136
|
+
'unhandled-async': 0.5,
|
|
137
|
+
},
|
|
138
|
+
codegen: {
|
|
139
|
+
'dead-export': 0,
|
|
140
|
+
'cognitive-complexity': 0,
|
|
141
|
+
'handler-size': 0,
|
|
142
|
+
'sync-in-async': 0,
|
|
143
|
+
'unhandled-async': 0,
|
|
144
|
+
'extra-code': 0,
|
|
145
|
+
'inconsistent-pattern': 0,
|
|
146
|
+
'style-difference': 0,
|
|
147
|
+
'missing-type': 0,
|
|
148
|
+
},
|
|
149
|
+
example: {
|
|
150
|
+
'dead-export': 0,
|
|
151
|
+
'public-api': 0,
|
|
152
|
+
'unhandled-async': 0.5,
|
|
153
|
+
'cognitive-complexity': 0.5,
|
|
154
|
+
},
|
|
155
|
+
test: {
|
|
156
|
+
'handler-size': 0.3,
|
|
157
|
+
'sync-in-async': 0,
|
|
158
|
+
'cognitive-complexity': 0.5,
|
|
159
|
+
'dead-export': 0,
|
|
160
|
+
},
|
|
161
|
+
'rule-definition': {
|
|
162
|
+
'cognitive-complexity': 0,
|
|
163
|
+
},
|
|
164
|
+
runtime: {},
|
|
165
|
+
};
|
|
166
|
+
/** Layer prefixes that are protected from any role multiplier. */
|
|
167
|
+
const PROTECTED_LAYER_PREFIXES = ['security'];
|
|
168
|
+
function isProtectedRule(ruleId) {
|
|
169
|
+
const info = getRuleInfoMap().get(ruleId);
|
|
170
|
+
if (!info)
|
|
171
|
+
return false;
|
|
172
|
+
return PROTECTED_LAYER_PREFIXES.some((prefix) => info.layer.startsWith(prefix));
|
|
173
|
+
}
|
|
174
|
+
/** Look up the role-aware multiplier for a finding. Always returns a finite number in [0, 1]. */
|
|
175
|
+
export function roleMultiplierFor(role, ruleId) {
|
|
176
|
+
if (isProtectedRule(ruleId))
|
|
177
|
+
return 1;
|
|
178
|
+
const fromTable = ROLE_MULTIPLIER[role]?.[ruleId];
|
|
179
|
+
if (typeof fromTable !== 'number' || !Number.isFinite(fromTable))
|
|
180
|
+
return 1;
|
|
181
|
+
return Math.max(0, Math.min(1, fromTable));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Maps a kern rule to the external linter rules that already cover the same
|
|
185
|
+
* concern. If the user has any of these enabled at error/warn, kern's finding
|
|
186
|
+
* is duplicate noise and gets demoted to info.
|
|
187
|
+
*
|
|
188
|
+
* Phase 1 red-team explicitly cautioned against a static table — the right
|
|
189
|
+
* long-term answer is querying the live ESLint instance per-file, which honors
|
|
190
|
+
* `overrides`. That requires async pre-warm. As a sync first cut we use the
|
|
191
|
+
* project-level config; coverage is honest about its limits (no per-file
|
|
192
|
+
* overrides honored) but the common case (one .eslintrc.json at root) works.
|
|
193
|
+
*
|
|
194
|
+
* Security-layer rules are NOT listed here: even if eslint has a similar
|
|
195
|
+
* rule, kern's output is independently valuable (different detection model,
|
|
196
|
+
* audit-trail integrity).
|
|
197
|
+
*/
|
|
198
|
+
const KERN_TO_EXTERNAL = {
|
|
199
|
+
'dead-export': {
|
|
200
|
+
eslint: ['no-unused-vars', '@typescript-eslint/no-unused-vars'],
|
|
201
|
+
biome: ['noUnusedVariables'],
|
|
202
|
+
},
|
|
203
|
+
'unused-import': {
|
|
204
|
+
eslint: ['no-unused-vars', '@typescript-eslint/no-unused-vars', 'unused-imports/no-unused-imports'],
|
|
205
|
+
biome: ['noUnusedImports'],
|
|
206
|
+
},
|
|
207
|
+
'sync-in-async': {
|
|
208
|
+
eslint: ['@typescript-eslint/no-misused-promises', 'no-misused-promises'],
|
|
209
|
+
},
|
|
210
|
+
'unhandled-async': {
|
|
211
|
+
eslint: ['@typescript-eslint/no-floating-promises', 'no-floating-promises'],
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
/** Look up overlap entry; isProtectedRule already shields the security layer. */
|
|
215
|
+
function overlapTargetsFor(ruleId) {
|
|
216
|
+
if (isProtectedRule(ruleId))
|
|
217
|
+
return undefined;
|
|
218
|
+
return KERN_TO_EXTERNAL[ruleId];
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Demote kern findings whose external counterpart is configured at error/warn
|
|
222
|
+
* in the project. Demotion (severity → info, confidence × 0.5) preserves the
|
|
223
|
+
* finding for telemetry while taking it out of the CI gate. Skipped in audit.
|
|
224
|
+
*/
|
|
225
|
+
export function applyOverlapCalibration(findings, external, config) {
|
|
226
|
+
if (config?.crossStackMode === 'audit')
|
|
227
|
+
return;
|
|
228
|
+
if (external.eslintEnabledRules.size === 0 && external.biomeEnabledRules.size === 0)
|
|
229
|
+
return;
|
|
230
|
+
for (const finding of findings) {
|
|
231
|
+
if (finding.calibrated)
|
|
232
|
+
continue;
|
|
233
|
+
const targets = overlapTargetsFor(finding.ruleId);
|
|
234
|
+
if (!targets)
|
|
235
|
+
continue;
|
|
236
|
+
const matchedEslint = targets.eslint?.find((r) => external.eslintEnabledRules.has(r));
|
|
237
|
+
const matchedBiome = targets.biome?.find((r) => external.biomeEnabledRules.has(r));
|
|
238
|
+
if (!matchedEslint && !matchedBiome)
|
|
239
|
+
continue;
|
|
240
|
+
const which = matchedEslint ? `eslint:${matchedEslint}` : matchedBiome ? `biome:${matchedBiome}` : 'unknown';
|
|
241
|
+
const beforeSeverity = finding.severity;
|
|
242
|
+
if (finding.severity !== 'error' && finding.severity !== 'info') {
|
|
243
|
+
finding.severity = 'info';
|
|
244
|
+
}
|
|
245
|
+
let beforeConfidence;
|
|
246
|
+
let afterConfidence;
|
|
247
|
+
if (finding.confidence !== undefined) {
|
|
248
|
+
beforeConfidence = finding.confidence;
|
|
249
|
+
afterConfidence = finding.confidence * 0.5;
|
|
250
|
+
finding.confidence = afterConfidence;
|
|
251
|
+
}
|
|
252
|
+
recordCalibration(finding, {
|
|
253
|
+
stage: 'overlap:external-linter',
|
|
254
|
+
factor: 0.5,
|
|
255
|
+
reason: `external linter already enforces ${which}`,
|
|
256
|
+
beforeConfidence,
|
|
257
|
+
afterConfidence,
|
|
258
|
+
...(beforeSeverity !== finding.severity ? { beforeSeverity, afterSeverity: finding.severity } : {}),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Apply role-aware confidence multipliers. Runs alongside applyRuleQualityCalibration
|
|
264
|
+
* in guard/ci policies; skipped in audit policy.
|
|
265
|
+
*
|
|
266
|
+
* Idempotent via the same `calibrated` flag: once a finding has been through any
|
|
267
|
+
* calibration stage it will not be touched again, so graph-mode rerun on a union
|
|
268
|
+
* does not compound multipliers.
|
|
269
|
+
*/
|
|
270
|
+
export function applyRoleAwareConfidence(findings, fileRole, config) {
|
|
271
|
+
if (config?.crossStackMode === 'audit')
|
|
272
|
+
return;
|
|
273
|
+
for (const finding of findings) {
|
|
274
|
+
if (finding.calibrated)
|
|
275
|
+
continue;
|
|
276
|
+
const factor = roleMultiplierFor(fileRole, finding.ruleId);
|
|
277
|
+
if (factor === 1)
|
|
278
|
+
continue;
|
|
279
|
+
if (finding.confidence !== undefined) {
|
|
280
|
+
const before = finding.confidence;
|
|
281
|
+
const after = before * factor;
|
|
282
|
+
finding.confidence = after;
|
|
283
|
+
recordCalibration(finding, {
|
|
284
|
+
stage: 'role-aware:confidence-multiplier',
|
|
285
|
+
factor,
|
|
286
|
+
reason: `role=${fileRole} reduces confidence on rule '${finding.ruleId}'`,
|
|
287
|
+
beforeConfidence: before,
|
|
288
|
+
afterConfidence: after,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
recordCalibration(finding, {
|
|
293
|
+
stage: 'role-aware:confidence-multiplier',
|
|
294
|
+
factor,
|
|
295
|
+
reason: `role=${fileRole} reduces confidence on rule '${finding.ruleId}' (no native confidence set)`,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
export function applyRuleSupersession(findings, config) {
|
|
301
|
+
if (config?.crossStackMode === 'audit')
|
|
302
|
+
return [...findings];
|
|
303
|
+
const suppressedByRoot = new Map();
|
|
304
|
+
for (const finding of findings) {
|
|
305
|
+
const supersedes = getRuleQualityProfile(finding.ruleId)?.supersedes;
|
|
306
|
+
if (!supersedes || supersedes.length === 0)
|
|
307
|
+
continue;
|
|
308
|
+
const rootKey = findingRootKey(finding);
|
|
309
|
+
const existing = suppressedByRoot.get(rootKey) ?? new Set();
|
|
310
|
+
for (const ruleId of supersedes) {
|
|
311
|
+
existing.add(ruleId);
|
|
312
|
+
}
|
|
313
|
+
suppressedByRoot.set(rootKey, existing);
|
|
314
|
+
}
|
|
315
|
+
return findings.filter((finding) => {
|
|
316
|
+
const suppressed = suppressedByRoot.get(findingRootKey(finding));
|
|
317
|
+
return !suppressed?.has(finding.ruleId);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
function findingRootKey(finding) {
|
|
321
|
+
if (finding.rootCause?.key)
|
|
322
|
+
return finding.rootCause.key;
|
|
323
|
+
const span = finding.primarySpan;
|
|
324
|
+
return `${span.file}:${span.startLine}:${span.startCol}`;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Lightweight self-check for registry hygiene. It is intentionally non-throwing so
|
|
328
|
+
* tests and tooling can decide whether to warn or fail.
|
|
329
|
+
*/
|
|
330
|
+
export function validateRuleQualityRegistry() {
|
|
331
|
+
const issues = [];
|
|
332
|
+
const seen = new Set();
|
|
333
|
+
for (const info of getRuleRegistry()) {
|
|
334
|
+
if (seen.has(info.id)) {
|
|
335
|
+
issues.push({ ruleId: info.id, message: 'duplicate rule registry entry' });
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
seen.add(info.id);
|
|
339
|
+
const profile = getRuleQualityProfile(info.id);
|
|
340
|
+
if (!profile)
|
|
341
|
+
continue;
|
|
342
|
+
if (profile.ciDefault === 'on' && !info.precision) {
|
|
343
|
+
issues.push({ ruleId: info.id, message: 'CI-on rule must declare precision explicitly' });
|
|
344
|
+
}
|
|
345
|
+
if (profile.ciDefault === 'on' && profile.lifecycle !== 'stable') {
|
|
346
|
+
issues.push({ ruleId: info.id, message: 'CI-on rule must be stable' });
|
|
347
|
+
}
|
|
348
|
+
if (profile.lifecycle === 'experimental' && profile.ciDefault === 'on') {
|
|
349
|
+
issues.push({ ruleId: info.id, message: 'experimental rule cannot default to CI-on' });
|
|
350
|
+
}
|
|
351
|
+
if (profile.precision === 'experimental' && profile.severity === 'error') {
|
|
352
|
+
issues.push({ ruleId: info.id, message: 'experimental rule should not default to error severity' });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return issues;
|
|
356
|
+
}
|
|
357
|
+
//# sourceMappingURL=rule-quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-quality.js","sourceRoot":"","sources":["../src/rule-quality.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { CallGraph } from '../call-graph.js';
|
|
8
8
|
import { type PublicApiMap } from '../public-api.js';
|
|
9
|
-
import type { ReviewFinding } from '../types.js';
|
|
10
|
-
export declare function deadExportRule(callGraph: CallGraph, filePath: string, publicApi?: PublicApiMap): ReviewFinding[];
|
|
9
|
+
import type { ReachabilityBlocker, ReviewFinding } from '../types.js';
|
|
10
|
+
export declare function deadExportRule(callGraph: CallGraph, filePath: string, publicApi?: PublicApiMap, blockers?: readonly ReachabilityBlocker[]): ReviewFinding[];
|
|
11
11
|
export declare function crossFileAsyncRule(callGraph: CallGraph, filePath: string): ReviewFinding[];
|
package/dist/rules/dead-code.js
CHANGED
|
@@ -11,8 +11,54 @@ function span(file, line, col = 1) {
|
|
|
11
11
|
}
|
|
12
12
|
// ── Rule: dead-export ───────────────────────────────────────────────────
|
|
13
13
|
// Exported function/variable that is never imported by any file in the graph.
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The confidence ceiling step 9b applies when a ReachabilityBlocker matches
|
|
16
|
+
* a finding's symbol scope. CAP, not floor: `Math.min(confidence, 0.4)`.
|
|
17
|
+
* A real floor would *raise* low-confidence findings (e.g. a 0.6 codegen-
|
|
18
|
+
* demoted dead-export bouncing back up to 0.4 — wait, 0.4 < 0.6, so a
|
|
19
|
+
* floor wouldn't help here, but the principle holds: blockers indicate
|
|
20
|
+
* uncertainty, they should never strengthen a finding). Codex flagged
|
|
21
|
+
* the floor-vs-cap terminology in plan-review #66 as a HIGH-severity
|
|
22
|
+
* gotcha; this constant is named CAP to make the intent obvious.
|
|
23
|
+
*/
|
|
24
|
+
const REACHABILITY_BLOCKER_CAP = 0.4;
|
|
25
|
+
/**
|
|
26
|
+
* Match a (filePath, exportName) blocker against a finding, with the
|
|
27
|
+
* default-alias step 9a wired in: when the seed says `'default'` is
|
|
28
|
+
* blocked but the call graph stored the symbol under its declaration
|
|
29
|
+
* name (`'Page'`), the file's `defaultExportNames` entry tells us the
|
|
30
|
+
* two key the same on-disk symbol.
|
|
31
|
+
*
|
|
32
|
+
* Returns the matched blocker, or undefined when nothing applies.
|
|
33
|
+
*/
|
|
34
|
+
function findMatchingBlocker(blockers, filePath, exportName, defaultExportName) {
|
|
35
|
+
for (const b of blockers) {
|
|
36
|
+
if (b.filePath !== filePath)
|
|
37
|
+
continue;
|
|
38
|
+
if (b.exportName === exportName)
|
|
39
|
+
return b;
|
|
40
|
+
// Default alias: blocker says (path, 'default'), and `exportName` IS
|
|
41
|
+
// the file's default — same symbol.
|
|
42
|
+
if (b.exportName === 'default' && defaultExportName === exportName)
|
|
43
|
+
return b;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Public-API check with the step 9a default-alias wired in: a seed of
|
|
49
|
+
* `(filePath, 'default')` proves `(filePath, internalName)` is public
|
|
50
|
+
* when internalName IS the file's default export.
|
|
51
|
+
*/
|
|
52
|
+
function isPublicApiWithDefaultAlias(publicApi, filePath, exportName, defaultExportName) {
|
|
53
|
+
if (isPublicApi(publicApi, filePath, exportName))
|
|
54
|
+
return true;
|
|
55
|
+
if (defaultExportName === exportName && isPublicApi(publicApi, filePath, 'default'))
|
|
56
|
+
return true;
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
export function deadExportRule(callGraph, filePath, publicApi, blockers = []) {
|
|
15
60
|
const findings = [];
|
|
61
|
+
const defaultExportName = callGraph.defaultExportNames.get(filePath);
|
|
16
62
|
for (const key of callGraph.deadExports) {
|
|
17
63
|
const fn = callGraph.functions.get(key);
|
|
18
64
|
if (!fn || fn.filePath !== filePath)
|
|
@@ -23,7 +69,7 @@ export function deadExportRule(callGraph, filePath, publicApi) {
|
|
|
23
69
|
// Skip intentional public API — package.json entries, curated barrels, config overrides.
|
|
24
70
|
// External consumers (other packages, dynamic loaders, platform entry points) won't
|
|
25
71
|
// appear in the analyzed graph, so their imports can't be observed.
|
|
26
|
-
if (publicApi &&
|
|
72
|
+
if (publicApi && isPublicApiWithDefaultAlias(publicApi, fn.filePath, fn.name, defaultExportName))
|
|
27
73
|
continue;
|
|
28
74
|
// Class methods inherit public-API status from their enclosing class. The
|
|
29
75
|
// call graph tracks `Class.method` as a separate exported symbol whose
|
|
@@ -40,11 +86,48 @@ export function deadExportRule(callGraph, filePath, publicApi) {
|
|
|
40
86
|
// If lots of calls are unresolved, the dead export might actually be used via a dynamic path
|
|
41
87
|
const totalCalls = [...callGraph.functions.values()].reduce((sum, f) => sum + f.calls.length, 0);
|
|
42
88
|
const unresolvedRatio = totalCalls > 0 ? callGraph.unresolvedCallCount / totalCalls : 0;
|
|
43
|
-
const
|
|
89
|
+
const baseConfidence = unresolvedRatio > 0.3 ? 0.6 : unresolvedRatio > 0.1 ? 0.7 : 0.85;
|
|
90
|
+
// Step 9b: symbol-scoped reachability blocker. When the graph couldn't
|
|
91
|
+
// prove this specific (file, name) is unreachable — say a candidate
|
|
92
|
+
// dead export is referenced by an unresolved re-export — the finding
|
|
93
|
+
// STILL emits (so telemetry sees it and fpRateEstimate stays honest)
|
|
94
|
+
// but at info severity with a CAP at 0.4 plus a calibration trail
|
|
95
|
+
// entry. Hard suppression was the v3 design that red-team killed:
|
|
96
|
+
// one weak signal silenced 50 unrelated symbols and fpRateEstimate
|
|
97
|
+
// went out of sync with reality.
|
|
98
|
+
const blocker = findMatchingBlocker(blockers, fn.filePath, fn.name, defaultExportName);
|
|
99
|
+
let severity = 'warning';
|
|
100
|
+
let confidence = baseConfidence;
|
|
101
|
+
let calibrationTrail;
|
|
102
|
+
if (blocker) {
|
|
103
|
+
severity = 'info';
|
|
104
|
+
const before = baseConfidence;
|
|
105
|
+
confidence = Math.min(before, REACHABILITY_BLOCKER_CAP);
|
|
106
|
+
// baseConfidence is one of the dead-export ladder values (0.6/0.7/0.85);
|
|
107
|
+
// it can never be 0, so the division is safe without a guard.
|
|
108
|
+
//
|
|
109
|
+
// INVARIANT — fresh array, never append. The trail is recreated each
|
|
110
|
+
// time deadExportRule fires, so it can't accumulate stages across
|
|
111
|
+
// repeated calls. This pairs with applyRuleQualityCalibration's
|
|
112
|
+
// `calibrated` guard (rule-quality.ts:91) to keep the trail length
|
|
113
|
+
// bounded across the lifetime of any single ReviewFinding object.
|
|
114
|
+
// If a future change introduces persistent ReviewFinding objects
|
|
115
|
+
// reused across watch-mode runs, BOTH guarantees must be revisited
|
|
116
|
+
// — see the idempotence test in rule-quality.test.ts:125.
|
|
117
|
+
calibrationTrail = [
|
|
118
|
+
{
|
|
119
|
+
stage: 'reachability:blocker',
|
|
120
|
+
factor: confidence / before,
|
|
121
|
+
reason: blocker.reason,
|
|
122
|
+
beforeConfidence: before,
|
|
123
|
+
afterConfidence: confidence,
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
}
|
|
44
127
|
findings.push({
|
|
45
128
|
source: 'kern',
|
|
46
129
|
ruleId: 'dead-export',
|
|
47
|
-
severity
|
|
130
|
+
severity,
|
|
48
131
|
category: 'structure',
|
|
49
132
|
message: `Exported function '${fn.name}' is never imported in the analyzed codebase`,
|
|
50
133
|
primarySpan: span(filePath, fn.line),
|
|
@@ -64,6 +147,7 @@ export function deadExportRule(callGraph, filePath, publicApi) {
|
|
|
64
147
|
},
|
|
65
148
|
],
|
|
66
149
|
},
|
|
150
|
+
...(calibrationTrail ? { calibrationTrail } : {}),
|
|
67
151
|
});
|
|
68
152
|
}
|
|
69
153
|
return findings;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dead-code.js","sourceRoot":"","sources":["../../src/rules/dead-code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,WAAW,EAAqB,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,SAAS,IAAI,CAAC,IAAY,EAAE,IAAY,EAAE,GAAG,GAAG,CAAC;IAC/C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAC9E,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAE9E,MAAM,
|
|
1
|
+
{"version":3,"file":"dead-code.js","sourceRoot":"","sources":["../../src/rules/dead-code.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/index.d.ts
CHANGED
|
@@ -31,6 +31,28 @@ export interface RuleInfo {
|
|
|
31
31
|
* 'experimental' — rule may be noisy; hide-by-default, promote after signal data proves it out.
|
|
32
32
|
*/
|
|
33
33
|
precision?: 'high' | 'medium' | 'experimental';
|
|
34
|
+
/**
|
|
35
|
+
* Lifecycle controls how aggressively a rule should participate in default
|
|
36
|
+
* guard/CI runs. Omitted entries are inferred from precision and rolloutPhase.
|
|
37
|
+
*/
|
|
38
|
+
lifecycle?: 'stable' | 'candidate' | 'experimental';
|
|
39
|
+
/**
|
|
40
|
+
* Default CI posture:
|
|
41
|
+
* - on: participates at declared severity
|
|
42
|
+
* - guarded: can be softened in high-signal guard runs
|
|
43
|
+
* - off: emitted as informational unless audit mode is enabled
|
|
44
|
+
*/
|
|
45
|
+
ciDefault?: 'on' | 'guarded' | 'off';
|
|
46
|
+
/**
|
|
47
|
+
* Analysis substrates required for high precision. This is intentionally
|
|
48
|
+
* declarative so new rules have to state their dependency on graph/type/evidence
|
|
49
|
+
* rather than hiding it in implementation comments.
|
|
50
|
+
*/
|
|
51
|
+
requires?: Array<'graph' | 'type-info' | 'concepts' | 'taint' | 'external-tool'>;
|
|
52
|
+
/** Rule IDs that this rule makes redundant when both fire on the same root cause. */
|
|
53
|
+
supersedes?: string[];
|
|
54
|
+
/** True when findings from this rule can safely carry structured autofix actions. */
|
|
55
|
+
fixable?: boolean;
|
|
34
56
|
/**
|
|
35
57
|
* Wave number in the rollout plan. Used by kern-sight to group new rules
|
|
36
58
|
* visually and by `--list-rules` to surface what just landed. Wave 0 = substrate.
|
package/dist/rules/index.js
CHANGED
|
@@ -1071,6 +1071,38 @@ const REGISTRY = [
|
|
|
1071
1071
|
severity: 'warning',
|
|
1072
1072
|
description: 'Network/DB effect without error recovery',
|
|
1073
1073
|
},
|
|
1074
|
+
{
|
|
1075
|
+
id: 'auth-drift',
|
|
1076
|
+
layer: 'concept',
|
|
1077
|
+
severity: 'warning',
|
|
1078
|
+
description: 'Authenticated backend route called by raw client request without visible auth propagation',
|
|
1079
|
+
precision: 'high',
|
|
1080
|
+
supersedes: ['auth-propagation-drift', 'unhandled-api-error-shape'],
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
id: 'contract-drift',
|
|
1084
|
+
layer: 'concept',
|
|
1085
|
+
severity: 'warning',
|
|
1086
|
+
description: 'Client network call has no matching server route contract',
|
|
1087
|
+
precision: 'high',
|
|
1088
|
+
supersedes: ['unhandled-api-error-shape', 'unbounded-collection-query', 'request-validation-drift'],
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
id: 'contract-method-drift',
|
|
1092
|
+
layer: 'concept',
|
|
1093
|
+
severity: 'warning',
|
|
1094
|
+
description: 'Client network method does not match the server route method for the same path',
|
|
1095
|
+
precision: 'high',
|
|
1096
|
+
supersedes: ['unhandled-api-error-shape', 'unbounded-collection-query', 'request-validation-drift'],
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
id: 'body-shape-drift',
|
|
1100
|
+
layer: 'concept',
|
|
1101
|
+
severity: 'warning',
|
|
1102
|
+
description: 'Client request body fields differ from backend route body usage',
|
|
1103
|
+
precision: 'high',
|
|
1104
|
+
supersedes: ['request-validation-drift'],
|
|
1105
|
+
},
|
|
1074
1106
|
{
|
|
1075
1107
|
id: 'unhandled-api-error-shape',
|
|
1076
1108
|
layer: 'concept',
|