@kernlang/review 2.0.0 → 3.1.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/dist/concept-rules/boundary-mutation.d.ts +13 -0
- package/dist/concept-rules/boundary-mutation.js +40 -0
- package/dist/concept-rules/boundary-mutation.js.map +1 -0
- package/dist/concept-rules/ignored-error.d.ts +13 -0
- package/dist/concept-rules/ignored-error.js +40 -0
- package/dist/concept-rules/ignored-error.js.map +1 -0
- package/dist/concept-rules/illegal-dependency.d.ts +13 -0
- package/dist/concept-rules/illegal-dependency.js +49 -0
- package/dist/concept-rules/illegal-dependency.js.map +1 -0
- package/dist/concept-rules/index.d.ts +15 -0
- package/dist/concept-rules/index.js +27 -0
- package/dist/concept-rules/index.js.map +1 -0
- package/dist/concept-rules/unguarded-effect.d.ts +13 -0
- package/dist/concept-rules/unguarded-effect.js +58 -0
- package/dist/concept-rules/unguarded-effect.js.map +1 -0
- package/dist/concept-rules/unrecovered-effect.d.ts +13 -0
- package/dist/concept-rules/unrecovered-effect.js +61 -0
- package/dist/concept-rules/unrecovered-effect.js.map +1 -0
- package/dist/confidence.d.ts +92 -0
- package/dist/confidence.js +263 -0
- package/dist/confidence.js.map +1 -0
- package/dist/differ.js +4 -2
- package/dist/differ.js.map +1 -1
- package/dist/external-tools.js +7 -3
- package/dist/external-tools.js.map +1 -1
- package/dist/file-role.d.ts +10 -0
- package/dist/file-role.js +80 -0
- package/dist/file-role.js.map +1 -0
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +152 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +46 -3
- package/dist/index.js +313 -27
- package/dist/index.js.map +1 -1
- package/dist/inferrer.js +123 -25
- package/dist/inferrer.js.map +1 -1
- package/dist/kern-lint.d.ts +18 -0
- package/dist/kern-lint.js +24 -0
- package/dist/kern-lint.js.map +1 -0
- package/dist/llm-bridge.d.ts +42 -0
- package/dist/llm-bridge.js +176 -0
- package/dist/llm-bridge.js.map +1 -0
- package/dist/llm-review.d.ts +8 -1
- package/dist/llm-review.js +20 -7
- package/dist/llm-review.js.map +1 -1
- package/dist/mappers/ts-concepts.d.ts +9 -0
- package/dist/mappers/ts-concepts.js +518 -0
- package/dist/mappers/ts-concepts.js.map +1 -0
- package/dist/quality-rules.d.ts +3 -3
- package/dist/quality-rules.js +3 -11
- package/dist/quality-rules.js.map +1 -1
- package/dist/reporter.d.ts +19 -3
- package/dist/reporter.js +232 -20
- package/dist/reporter.js.map +1 -1
- package/dist/rules/base.js +167 -15
- package/dist/rules/base.js.map +1 -1
- package/dist/rules/confidence.d.ts +37 -0
- package/dist/rules/confidence.js +159 -0
- package/dist/rules/confidence.js.map +1 -0
- package/dist/rules/dead-logic.d.ts +13 -0
- package/dist/rules/dead-logic.js +393 -0
- package/dist/rules/dead-logic.js.map +1 -0
- package/dist/rules/express.js +69 -2
- package/dist/rules/express.js.map +1 -1
- package/dist/rules/ground-layer.d.ts +23 -0
- package/dist/rules/ground-layer.js +132 -0
- package/dist/rules/ground-layer.js.map +1 -0
- package/dist/rules/index.d.ts +1 -1
- package/dist/rules/index.js +8 -2
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/kern-source.d.ts +16 -0
- package/dist/rules/kern-source.js +726 -0
- package/dist/rules/kern-source.js.map +1 -0
- package/dist/rules/nextjs.js +38 -10
- package/dist/rules/nextjs.js.map +1 -1
- package/dist/rules/null-safety.d.ts +12 -0
- package/dist/rules/null-safety.js +123 -0
- package/dist/rules/null-safety.js.map +1 -0
- package/dist/rules/react.js +64 -1
- package/dist/rules/react.js.map +1 -1
- package/dist/rules/security-v2.d.ts +12 -0
- package/dist/rules/security-v2.js +415 -0
- package/dist/rules/security-v2.js.map +1 -0
- package/dist/rules/security-v3.d.ts +12 -0
- package/dist/rules/security-v3.js +397 -0
- package/dist/rules/security-v3.js.map +1 -0
- package/dist/rules/security-v4.d.ts +22 -0
- package/dist/rules/security-v4.js +688 -0
- package/dist/rules/security-v4.js.map +1 -0
- package/dist/rules/security.d.ts +12 -0
- package/dist/rules/security.js +286 -0
- package/dist/rules/security.js.map +1 -0
- package/dist/rules/utils.d.ts +7 -0
- package/dist/rules/utils.js +21 -0
- package/dist/rules/utils.js.map +1 -0
- package/dist/rules/vue.js +1 -1
- package/dist/rules/vue.js.map +1 -1
- package/dist/spec-checker.d.ts +83 -0
- package/dist/spec-checker.js +405 -0
- package/dist/spec-checker.js.map +1 -0
- package/dist/suppression/apply-suppression.d.ts +17 -0
- package/dist/suppression/apply-suppression.js +94 -0
- package/dist/suppression/apply-suppression.js.map +1 -0
- package/dist/suppression/index.d.ts +6 -0
- package/dist/suppression/index.js +6 -0
- package/dist/suppression/index.js.map +1 -0
- package/dist/suppression/parse-directives.d.ts +25 -0
- package/dist/suppression/parse-directives.js +161 -0
- package/dist/suppression/parse-directives.js.map +1 -0
- package/dist/suppression/types.d.ts +32 -0
- package/dist/suppression/types.js +5 -0
- package/dist/suppression/types.js.map +1 -0
- package/dist/taint.d.ts +115 -0
- package/dist/taint.js +1052 -0
- package/dist/taint.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -4
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Checker — verifies .kern contracts against TypeScript implementation.
|
|
3
|
+
*
|
|
4
|
+
* The .kern file is a machine-readable security contract. This module
|
|
5
|
+
* cross-checks declared auth, validation, guards, middleware, and error
|
|
6
|
+
* handling against what the TypeScript code actually does.
|
|
7
|
+
*
|
|
8
|
+
* No other tool can do this — .kern IS the spec, and this verifies reality.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { parse } from '@kernlang/core';
|
|
12
|
+
import { createFingerprint } from './types.js';
|
|
13
|
+
// ── Extract spec contracts from .kern ────────────────────────────────────
|
|
14
|
+
export function extractSpecContracts(kernSource, kernFile) {
|
|
15
|
+
const ast = parse(kernSource);
|
|
16
|
+
const contracts = [];
|
|
17
|
+
collectRoutes(ast, contracts, kernFile);
|
|
18
|
+
return contracts;
|
|
19
|
+
}
|
|
20
|
+
function collectRoutes(node, out, kernFile) {
|
|
21
|
+
if (node.type === 'route') {
|
|
22
|
+
const method = String(node.props?.method || 'get').toLowerCase();
|
|
23
|
+
const path = String(node.props?.path || '/');
|
|
24
|
+
const routeKey = `${method.toUpperCase()} ${path}`;
|
|
25
|
+
const contract = {
|
|
26
|
+
method,
|
|
27
|
+
path,
|
|
28
|
+
routeKey,
|
|
29
|
+
guards: [],
|
|
30
|
+
middleware: [],
|
|
31
|
+
errors: [],
|
|
32
|
+
hasHandler: false,
|
|
33
|
+
line: node.loc?.line || 1,
|
|
34
|
+
kernFile,
|
|
35
|
+
};
|
|
36
|
+
// Walk children for auth, validate, guard, middleware, error, handler
|
|
37
|
+
if (node.children) {
|
|
38
|
+
for (const child of node.children) {
|
|
39
|
+
switch (child.type) {
|
|
40
|
+
case 'auth':
|
|
41
|
+
contract.auth = { mode: String(child.props?.mode || 'required') };
|
|
42
|
+
break;
|
|
43
|
+
case 'validate':
|
|
44
|
+
contract.validate = { schema: String(child.props?.schema || '') };
|
|
45
|
+
break;
|
|
46
|
+
case 'guard': {
|
|
47
|
+
const elseVal = child.props?.else;
|
|
48
|
+
contract.guards.push({
|
|
49
|
+
name: String(child.props?.name || 'guard'),
|
|
50
|
+
expr: child.props?.expr ? String(child.props.expr) : undefined,
|
|
51
|
+
elseStatus: typeof elseVal === 'number' ? elseVal : parseInt(String(elseVal || '404'), 10),
|
|
52
|
+
});
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case 'middleware': {
|
|
56
|
+
const names = [];
|
|
57
|
+
if (child.props?.names && Array.isArray(child.props.names)) {
|
|
58
|
+
names.push(...child.props.names);
|
|
59
|
+
}
|
|
60
|
+
else if (child.props?.name) {
|
|
61
|
+
names.push(String(child.props.name));
|
|
62
|
+
}
|
|
63
|
+
if (names.length > 0) {
|
|
64
|
+
contract.middleware.push({ names });
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case 'error':
|
|
69
|
+
if (child.props?.status) {
|
|
70
|
+
contract.errors.push({
|
|
71
|
+
status: typeof child.props.status === 'number'
|
|
72
|
+
? child.props.status
|
|
73
|
+
: parseInt(String(child.props.status), 10),
|
|
74
|
+
message: child.props?.message ? String(child.props.message) : undefined,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
case 'handler':
|
|
79
|
+
contract.hasHandler = true;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
out.push(contract);
|
|
85
|
+
}
|
|
86
|
+
// Recurse into ALL children (server, document nodes contain routes)
|
|
87
|
+
if (node.children) {
|
|
88
|
+
for (const child of node.children) {
|
|
89
|
+
collectRoutes(child, out, kernFile);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// ── Extract implementation routes from .ts ───────────────────────────────
|
|
94
|
+
const ROUTE_REGEX = /\b(app|router|server)\.(get|post|put|delete|patch|head|options)\s*\(\s*(['"`])([^'"`]+)\3/gi;
|
|
95
|
+
export function extractImplRoutes(tsSource, filePath) {
|
|
96
|
+
const routes = [];
|
|
97
|
+
const lines = tsSource.split('\n');
|
|
98
|
+
let match;
|
|
99
|
+
ROUTE_REGEX.lastIndex = 0;
|
|
100
|
+
while ((match = ROUTE_REGEX.exec(tsSource)) !== null) {
|
|
101
|
+
const method = match[2].toLowerCase();
|
|
102
|
+
const path = match[4];
|
|
103
|
+
const routeKey = `${method.toUpperCase()} ${path}`;
|
|
104
|
+
// Find line number
|
|
105
|
+
const beforeMatch = tsSource.slice(0, match.index);
|
|
106
|
+
const startLine = beforeMatch.split('\n').length;
|
|
107
|
+
// Extract middleware args between path and handler
|
|
108
|
+
const afterPath = tsSource.slice(match.index + match[0].length);
|
|
109
|
+
const middlewareArgs = extractMiddlewareArgs(afterPath);
|
|
110
|
+
// Extract handler body
|
|
111
|
+
const handlerBody = extractHandlerBody(afterPath);
|
|
112
|
+
routes.push({
|
|
113
|
+
method,
|
|
114
|
+
path,
|
|
115
|
+
routeKey,
|
|
116
|
+
handlerBody,
|
|
117
|
+
middlewareArgs,
|
|
118
|
+
filePath,
|
|
119
|
+
startLine,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return routes;
|
|
123
|
+
}
|
|
124
|
+
function extractMiddlewareArgs(afterPath) {
|
|
125
|
+
// After the path string, there are comma-separated middleware names
|
|
126
|
+
// before the final (req, res) => { handler. Extract the identifiers.
|
|
127
|
+
const names = [];
|
|
128
|
+
// Match: , identifier, identifier, ... async? (req
|
|
129
|
+
const argsSection = afterPath.match(/^((?:, ?\w+){0,10}) ?, ?(?:async )?\(/);
|
|
130
|
+
if (!argsSection)
|
|
131
|
+
return names;
|
|
132
|
+
const argsText = argsSection[1];
|
|
133
|
+
const parts = argsText.split(',').map(s => s.trim()).filter(Boolean);
|
|
134
|
+
for (const part of parts) {
|
|
135
|
+
if (/^\w+$/.test(part)) {
|
|
136
|
+
names.push(part);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return names;
|
|
140
|
+
}
|
|
141
|
+
function extractHandlerBody(afterPath) {
|
|
142
|
+
// Find the handler function body — look for (req, res) => { or function(req, res) {
|
|
143
|
+
const handlerStart = afterPath.search(/(?:async\s*)?\([^)]*\)\s*(?:=>)?\s*\{/);
|
|
144
|
+
if (handlerStart < 0)
|
|
145
|
+
return '';
|
|
146
|
+
const braceStart = afterPath.indexOf('{', handlerStart);
|
|
147
|
+
if (braceStart < 0)
|
|
148
|
+
return '';
|
|
149
|
+
let depth = 1;
|
|
150
|
+
let i = braceStart + 1;
|
|
151
|
+
let inString = null;
|
|
152
|
+
while (i < afterPath.length && depth > 0) {
|
|
153
|
+
const ch = afterPath[i];
|
|
154
|
+
if (inString) {
|
|
155
|
+
if (ch === inString && afterPath[i - 1] !== '\\')
|
|
156
|
+
inString = null;
|
|
157
|
+
else if (ch === '\n' && inString !== '`')
|
|
158
|
+
inString = null; // single/double quotes don't span lines
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
if (ch === '"' || ch === "'" || ch === '`')
|
|
162
|
+
inString = ch;
|
|
163
|
+
else if (ch === '{')
|
|
164
|
+
depth++;
|
|
165
|
+
else if (ch === '}')
|
|
166
|
+
depth--;
|
|
167
|
+
}
|
|
168
|
+
i++;
|
|
169
|
+
}
|
|
170
|
+
return afterPath.slice(braceStart + 1, i - 1).trim();
|
|
171
|
+
}
|
|
172
|
+
// ── Route matching ───────────────────────────────────────────────────────
|
|
173
|
+
function pathsMatch(a, b) {
|
|
174
|
+
const sa = a.split('/').filter(Boolean);
|
|
175
|
+
const sb = b.split('/').filter(Boolean);
|
|
176
|
+
if (sa.length !== sb.length)
|
|
177
|
+
return false;
|
|
178
|
+
return sa.every((seg, i) => {
|
|
179
|
+
const isParamA = seg.startsWith(':');
|
|
180
|
+
const isParamB = sb[i].startsWith(':');
|
|
181
|
+
if (isParamA && isParamB)
|
|
182
|
+
return true;
|
|
183
|
+
if (isParamA || isParamB)
|
|
184
|
+
return false;
|
|
185
|
+
return seg === sb[i];
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
export function matchRoutes(specs, impls) {
|
|
189
|
+
const matched = [];
|
|
190
|
+
const usedImpls = new Set();
|
|
191
|
+
for (const spec of specs) {
|
|
192
|
+
// Try exact routeKey match first
|
|
193
|
+
let implIdx = impls.findIndex((impl, i) => !usedImpls.has(i) && impl.routeKey === spec.routeKey);
|
|
194
|
+
// Fallback: fuzzy path match (handles :id vs :userId)
|
|
195
|
+
if (implIdx < 0) {
|
|
196
|
+
implIdx = impls.findIndex((impl, i) => !usedImpls.has(i) &&
|
|
197
|
+
impl.method === spec.method &&
|
|
198
|
+
pathsMatch(impl.path, spec.path));
|
|
199
|
+
}
|
|
200
|
+
if (implIdx >= 0) {
|
|
201
|
+
matched.push({ spec, impl: impls[implIdx] });
|
|
202
|
+
usedImpls.add(implIdx);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const unmatchedSpecs = specs.filter(s => !matched.some(m => m.spec === s));
|
|
206
|
+
const unmatchedImpls = impls.filter((_, i) => !usedImpls.has(i));
|
|
207
|
+
return { matched, unmatchedSpecs, unmatchedImpls };
|
|
208
|
+
}
|
|
209
|
+
// ── Contract verification ────────────────────────────────────────────────
|
|
210
|
+
const AUTH_MIDDLEWARE = /\b(auth|authenticate|requireAuth|requireLicense|verifyToken|jwtVerify|bearerAuth|isAuthenticated|authMiddleware|passport|requirePro)\b/i;
|
|
211
|
+
const AUTH_BODY = /\breq\.(user|auth)\b|verifyToken\s*\(|authenticate\s*\(|checkAuth\s*\(|requireLicense\b/;
|
|
212
|
+
const VALIDATION_CALL = /\.(parse|safeParse|validate|validateSync)\s?\(/;
|
|
213
|
+
const GUARD_CONDITIONAL = /if\s?\(\s?!?\s?\w+/;
|
|
214
|
+
function checkAuth(spec, impl) {
|
|
215
|
+
if (!spec.auth)
|
|
216
|
+
return null;
|
|
217
|
+
const hasAuthMiddleware = impl.middlewareArgs.some(a => AUTH_MIDDLEWARE.test(a));
|
|
218
|
+
const hasAuthInBody = AUTH_BODY.test(impl.handlerBody);
|
|
219
|
+
if (!hasAuthMiddleware && !hasAuthInBody) {
|
|
220
|
+
return {
|
|
221
|
+
kind: 'spec-auth-missing',
|
|
222
|
+
detail: `.kern declares 'auth ${spec.auth.mode}' on ${spec.routeKey} but no auth middleware or check found in implementation`,
|
|
223
|
+
suggestion: 'Add auth middleware (e.g., requireLicense) to the route handler chain',
|
|
224
|
+
kernFile: spec.kernFile,
|
|
225
|
+
kernLine: spec.line,
|
|
226
|
+
tsFile: impl.filePath,
|
|
227
|
+
tsLine: impl.startLine,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
function checkValidate(spec, impl) {
|
|
233
|
+
if (!spec.validate)
|
|
234
|
+
return null;
|
|
235
|
+
const hasValidation = VALIDATION_CALL.test(impl.handlerBody);
|
|
236
|
+
const hasSchemaRef = impl.handlerBody.includes(spec.validate.schema);
|
|
237
|
+
if (!hasValidation) {
|
|
238
|
+
return {
|
|
239
|
+
kind: 'spec-validate-missing',
|
|
240
|
+
detail: `.kern declares 'validate ${spec.validate.schema}' on ${spec.routeKey} but no .parse()/.safeParse() found in handler`,
|
|
241
|
+
suggestion: `Add ${spec.validate.schema}.safeParse(req.body) before processing the request`,
|
|
242
|
+
kernFile: spec.kernFile,
|
|
243
|
+
kernLine: spec.line,
|
|
244
|
+
tsFile: impl.filePath,
|
|
245
|
+
tsLine: impl.startLine,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
function checkGuards(spec, impl) {
|
|
251
|
+
const violations = [];
|
|
252
|
+
for (const guard of spec.guards) {
|
|
253
|
+
const hasConditional = GUARD_CONDITIONAL.test(impl.handlerBody);
|
|
254
|
+
const hasStatus = new RegExp(`\\.status\\s*\\(\\s*${guard.elseStatus}\\s*\\)`).test(impl.handlerBody) ||
|
|
255
|
+
new RegExp(`sendStatus\\s*\\(\\s*${guard.elseStatus}\\s*\\)`).test(impl.handlerBody);
|
|
256
|
+
if (!hasConditional || !hasStatus) {
|
|
257
|
+
violations.push({
|
|
258
|
+
kind: 'spec-guard-missing',
|
|
259
|
+
detail: `.kern declares 'guard ${guard.name} else=${guard.elseStatus}' on ${spec.routeKey} but ${!hasConditional ? 'no conditional check' : `no ${guard.elseStatus} response`} found`,
|
|
260
|
+
suggestion: `Add: if (!${guard.name}) return res.status(${guard.elseStatus}).json({ error: '...' })`,
|
|
261
|
+
kernFile: spec.kernFile,
|
|
262
|
+
kernLine: spec.line,
|
|
263
|
+
tsFile: impl.filePath,
|
|
264
|
+
tsLine: impl.startLine,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return violations;
|
|
269
|
+
}
|
|
270
|
+
function checkMiddleware(spec, impl) {
|
|
271
|
+
const violations = [];
|
|
272
|
+
const implMiddleware = impl.middlewareArgs.join(' ').toLowerCase();
|
|
273
|
+
const bodyLower = impl.handlerBody.toLowerCase();
|
|
274
|
+
for (const mw of spec.middleware) {
|
|
275
|
+
for (const name of mw.names) {
|
|
276
|
+
const nameLower = name.toLowerCase();
|
|
277
|
+
if (!implMiddleware.includes(nameLower) && !bodyLower.includes(nameLower)) {
|
|
278
|
+
violations.push({
|
|
279
|
+
kind: 'spec-middleware-missing',
|
|
280
|
+
detail: `.kern declares 'middleware ${name}' on ${spec.routeKey} but '${name}' not found in route registration or handler`,
|
|
281
|
+
suggestion: `Add ${name} middleware to the route: router.${spec.method}('${spec.path}', ${name}, handler)`,
|
|
282
|
+
kernFile: spec.kernFile,
|
|
283
|
+
kernLine: spec.line,
|
|
284
|
+
tsFile: impl.filePath,
|
|
285
|
+
tsLine: impl.startLine,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return violations;
|
|
291
|
+
}
|
|
292
|
+
function checkErrors(spec, impl) {
|
|
293
|
+
const violations = [];
|
|
294
|
+
for (const err of spec.errors) {
|
|
295
|
+
// 500 is special — try/catch satisfies it
|
|
296
|
+
if (err.status === 500 && /try\s*\{[\s\S]*?\}\s*catch/.test(impl.handlerBody))
|
|
297
|
+
continue;
|
|
298
|
+
const hasStatus = new RegExp(`\\.status\\s*\\(\\s*${err.status}\\s*\\)`).test(impl.handlerBody) ||
|
|
299
|
+
new RegExp(`sendStatus\\s*\\(\\s*${err.status}\\s*\\)`).test(impl.handlerBody);
|
|
300
|
+
if (!hasStatus) {
|
|
301
|
+
violations.push({
|
|
302
|
+
kind: 'spec-error-unhandled',
|
|
303
|
+
detail: `.kern declares 'error ${err.status}${err.message ? ` "${err.message}"` : ''}' on ${spec.routeKey} but status ${err.status} is never sent`,
|
|
304
|
+
suggestion: `Ensure the handler can respond with res.status(${err.status}).json({ error: '${err.message || 'Error'}' })`,
|
|
305
|
+
kernFile: spec.kernFile,
|
|
306
|
+
kernLine: spec.line,
|
|
307
|
+
tsFile: impl.filePath,
|
|
308
|
+
tsLine: impl.startLine,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return violations;
|
|
313
|
+
}
|
|
314
|
+
export function verifyRouteContract(spec, impl) {
|
|
315
|
+
return [
|
|
316
|
+
checkAuth(spec, impl),
|
|
317
|
+
checkValidate(spec, impl),
|
|
318
|
+
...checkGuards(spec, impl),
|
|
319
|
+
...checkMiddleware(spec, impl),
|
|
320
|
+
...checkErrors(spec, impl),
|
|
321
|
+
].filter((v) => v !== null);
|
|
322
|
+
}
|
|
323
|
+
// ── Main entry ───────────────────────────────────────────────────────────
|
|
324
|
+
export function checkSpec(kernSource, kernFile, tsSource, tsFile) {
|
|
325
|
+
const specs = extractSpecContracts(kernSource, kernFile);
|
|
326
|
+
const impls = extractImplRoutes(tsSource, tsFile);
|
|
327
|
+
const { matched, unmatchedSpecs, unmatchedImpls } = matchRoutes(specs, impls);
|
|
328
|
+
const violations = [];
|
|
329
|
+
// Check each matched route
|
|
330
|
+
for (const { spec, impl } of matched) {
|
|
331
|
+
violations.push(...verifyRouteContract(spec, impl));
|
|
332
|
+
}
|
|
333
|
+
// Routes in .kern with no implementation
|
|
334
|
+
for (const spec of unmatchedSpecs) {
|
|
335
|
+
violations.push({
|
|
336
|
+
kind: 'spec-unimplemented',
|
|
337
|
+
detail: `${spec.routeKey} declared in .kern but no matching route handler found in implementation`,
|
|
338
|
+
suggestion: `Implement the ${spec.method.toUpperCase()} ${spec.path} route handler`,
|
|
339
|
+
kernFile: spec.kernFile,
|
|
340
|
+
kernLine: spec.line,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
// Routes in .ts with no .kern declaration
|
|
344
|
+
for (const impl of unmatchedImpls) {
|
|
345
|
+
violations.push({
|
|
346
|
+
kind: 'spec-undeclared',
|
|
347
|
+
detail: `${impl.routeKey} exists in implementation but is not declared in .kern spec — undocumented endpoint`,
|
|
348
|
+
suggestion: `Add to .kern: route ${impl.method.toUpperCase()} ${impl.path}`,
|
|
349
|
+
tsFile: impl.filePath,
|
|
350
|
+
tsLine: impl.startLine,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return { violations, matched, unmatchedSpecs, unmatchedImpls };
|
|
354
|
+
}
|
|
355
|
+
// ── Finding conversion ───────────────────────────────────────────────────
|
|
356
|
+
const SEVERITY_MAP = {
|
|
357
|
+
'spec-auth-missing': 'error',
|
|
358
|
+
'spec-validate-missing': 'warning',
|
|
359
|
+
'spec-guard-missing': 'warning',
|
|
360
|
+
'spec-middleware-missing': 'warning',
|
|
361
|
+
'spec-error-unhandled': 'info',
|
|
362
|
+
'spec-unimplemented': 'error',
|
|
363
|
+
'spec-undeclared': 'info',
|
|
364
|
+
};
|
|
365
|
+
export function specViolationsToFindings(result) {
|
|
366
|
+
return result.violations.map(v => {
|
|
367
|
+
const file = v.kernFile || v.tsFile || '';
|
|
368
|
+
const line = v.kernLine || v.tsLine || 1;
|
|
369
|
+
const primarySpan = {
|
|
370
|
+
file,
|
|
371
|
+
startLine: line,
|
|
372
|
+
startCol: 1,
|
|
373
|
+
endLine: line,
|
|
374
|
+
endCol: 1,
|
|
375
|
+
};
|
|
376
|
+
const relatedSpans = [];
|
|
377
|
+
if (v.kernFile && v.tsFile) {
|
|
378
|
+
relatedSpans.push({
|
|
379
|
+
file: v.tsFile,
|
|
380
|
+
startLine: v.tsLine || 1,
|
|
381
|
+
startCol: 1,
|
|
382
|
+
endLine: v.tsLine || 1,
|
|
383
|
+
endCol: 1,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
source: 'kern',
|
|
388
|
+
ruleId: v.kind,
|
|
389
|
+
severity: SEVERITY_MAP[v.kind],
|
|
390
|
+
category: 'bug',
|
|
391
|
+
message: v.detail,
|
|
392
|
+
primarySpan,
|
|
393
|
+
...(relatedSpans.length > 0 ? { relatedSpans } : {}),
|
|
394
|
+
suggestion: v.suggestion,
|
|
395
|
+
fingerprint: createFingerprint(v.kind, line, 1),
|
|
396
|
+
};
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
// ── File-based entry points ──────────────────────────────────────────────
|
|
400
|
+
export function checkSpecFiles(kernFilePath, tsFilePath) {
|
|
401
|
+
const kernSource = readFileSync(kernFilePath, 'utf-8');
|
|
402
|
+
const tsSource = readFileSync(tsFilePath, 'utf-8');
|
|
403
|
+
return checkSpec(kernSource, kernFilePath, tsSource, tsFilePath);
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=spec-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-checker.js","sourceRoot":"","sources":["../src/spec-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply suppression directives to findings.
|
|
3
|
+
* Runs after sortAndDedup(), before checkEnforcement().
|
|
4
|
+
*/
|
|
5
|
+
import type { ReviewFinding } from '../types.js';
|
|
6
|
+
import type { SuppressionResult, StrictMode } from './types.js';
|
|
7
|
+
import type { ReviewConfig } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Apply all suppression directives to a set of findings.
|
|
10
|
+
*
|
|
11
|
+
* @param findings - Deduplicated, sorted findings from the pipeline
|
|
12
|
+
* @param source - Source text of the file (for parsing inline directives)
|
|
13
|
+
* @param filePath - File path
|
|
14
|
+
* @param config - ReviewConfig (for disabledRules)
|
|
15
|
+
* @param strict - CI strict mode: false = respect all, 'inline' = ignore inline, 'all' = ignore everything
|
|
16
|
+
*/
|
|
17
|
+
export declare function applySuppression(findings: ReviewFinding[], source: string, filePath: string, config?: ReviewConfig, strict?: StrictMode): SuppressionResult;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply suppression directives to findings.
|
|
3
|
+
* Runs after sortAndDedup(), before checkEnforcement().
|
|
4
|
+
*/
|
|
5
|
+
import { parseDirectives, configDirectives } from './parse-directives.js';
|
|
6
|
+
function isSuppressed(finding, directive) {
|
|
7
|
+
// Rule ID must match
|
|
8
|
+
if (!directive.ruleIds.includes(finding.ruleId))
|
|
9
|
+
return false;
|
|
10
|
+
// Config-level directives (file='*') apply to all files
|
|
11
|
+
if (directive.file === '*')
|
|
12
|
+
return true;
|
|
13
|
+
// File must match
|
|
14
|
+
if (directive.file !== finding.primarySpan.file)
|
|
15
|
+
return false;
|
|
16
|
+
// File-level: suppress all matching rules in file
|
|
17
|
+
if (directive.type === 'file')
|
|
18
|
+
return true;
|
|
19
|
+
// Line-level: finding must be on the directive's target line
|
|
20
|
+
return finding.primarySpan.startLine === directive.line;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Apply all suppression directives to a set of findings.
|
|
24
|
+
*
|
|
25
|
+
* @param findings - Deduplicated, sorted findings from the pipeline
|
|
26
|
+
* @param source - Source text of the file (for parsing inline directives)
|
|
27
|
+
* @param filePath - File path
|
|
28
|
+
* @param config - ReviewConfig (for disabledRules)
|
|
29
|
+
* @param strict - CI strict mode: false = respect all, 'inline' = ignore inline, 'all' = ignore everything
|
|
30
|
+
*/
|
|
31
|
+
export function applySuppression(findings, source, filePath, config, strict = false) {
|
|
32
|
+
// Parse inline directives from source
|
|
33
|
+
const { directives: inlineDirectives, warnings } = parseDirectives(source, filePath);
|
|
34
|
+
// Build config-level directives
|
|
35
|
+
const cfgDirectives = configDirectives(config?.disabledRules ?? []);
|
|
36
|
+
// Determine which directives are active based on strict mode
|
|
37
|
+
let activeDirectives;
|
|
38
|
+
if (strict === 'all') {
|
|
39
|
+
activeDirectives = [];
|
|
40
|
+
}
|
|
41
|
+
else if (strict === 'inline') {
|
|
42
|
+
// Only config-level directives are active
|
|
43
|
+
activeDirectives = cfgDirectives;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
activeDirectives = [...inlineDirectives, ...cfgDirectives];
|
|
47
|
+
}
|
|
48
|
+
const allDirectives = [...inlineDirectives, ...cfgDirectives];
|
|
49
|
+
// Track which directives matched at least one finding
|
|
50
|
+
const matchedDirectives = new Set();
|
|
51
|
+
const passed = [];
|
|
52
|
+
const suppressed = [];
|
|
53
|
+
for (const finding of findings) {
|
|
54
|
+
const matchingDirective = activeDirectives.find(d => isSuppressed(finding, d));
|
|
55
|
+
if (matchingDirective) {
|
|
56
|
+
matchedDirectives.add(matchingDirective);
|
|
57
|
+
suppressed.push(finding);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
passed.push(finding);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Find unused inline directives (config-level ones are intentionally exempt)
|
|
64
|
+
const unusedDirectives = inlineDirectives.filter(d => !matchedDirectives.has(d));
|
|
65
|
+
// Add warnings from parsing + unused directive warnings
|
|
66
|
+
const allWarnings = [...warnings];
|
|
67
|
+
// Only report unused directives when not in strict mode (in strict, inline directives are intentionally ignored)
|
|
68
|
+
if (!strict) {
|
|
69
|
+
for (const d of unusedDirectives) {
|
|
70
|
+
allWarnings.push({
|
|
71
|
+
source: 'kern',
|
|
72
|
+
ruleId: 'kern-ignore-unused',
|
|
73
|
+
severity: 'warning',
|
|
74
|
+
category: 'style',
|
|
75
|
+
message: `Unused kern-ignore for '${d.ruleIds.join(', ')}' — no matching findings`,
|
|
76
|
+
primarySpan: {
|
|
77
|
+
file: d.file,
|
|
78
|
+
startLine: d.commentLine ?? 1,
|
|
79
|
+
startCol: 1,
|
|
80
|
+
endLine: d.commentLine ?? 1,
|
|
81
|
+
endCol: 1,
|
|
82
|
+
},
|
|
83
|
+
fingerprint: `unused-${d.commentLine}-${d.ruleIds.join(',')}`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
findings: [...passed, ...allWarnings],
|
|
89
|
+
suppressed,
|
|
90
|
+
directives: allDirectives,
|
|
91
|
+
unusedDirectives,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=apply-suppression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-suppression.js","sourceRoot":"","sources":["../../src/suppression/apply-suppression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suppression module — inline comments + config-level rule suppression.
|
|
3
|
+
*/
|
|
4
|
+
export type { SuppressionDirective, SuppressionResult, StrictMode } from './types.js';
|
|
5
|
+
export { parseDirectives, configDirectives, isConceptRule } from './parse-directives.js';
|
|
6
|
+
export { applySuppression } from './apply-suppression.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suppression module — inline comments + config-level rule suppression.
|
|
3
|
+
*/
|
|
4
|
+
export { parseDirectives, configDirectives, isConceptRule } from './parse-directives.js';
|
|
5
|
+
export { applySuppression } from './apply-suppression.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/suppression/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse kern-ignore directives from source text.
|
|
3
|
+
*
|
|
4
|
+
* Supported syntax:
|
|
5
|
+
* // kern-ignore <rule-id>[, <rule-id>...] — suppress on same or next non-comment line
|
|
6
|
+
* // kern-ignore-file <rule-id>[, <rule-id>...] — suppress for entire file (first 5 lines)
|
|
7
|
+
* # kern-ignore <rule-id>[, <rule-id>...] — Python variant
|
|
8
|
+
* # kern-ignore-file <rule-id>[, <rule-id>...] — Python variant
|
|
9
|
+
*/
|
|
10
|
+
import type { SuppressionDirective } from './types.js';
|
|
11
|
+
import type { ReviewFinding } from '../types.js';
|
|
12
|
+
export declare function isConceptRule(ruleId: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Parse all suppression directives from source text.
|
|
15
|
+
* Returns directives + any warnings (e.g., bare kern-ignore, concept rule on line-level).
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseDirectives(source: string, filePath: string): {
|
|
18
|
+
directives: SuppressionDirective[];
|
|
19
|
+
warnings: ReviewFinding[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Create config-level suppression directives from disabledRules.
|
|
23
|
+
* These apply to all files (file field is '*').
|
|
24
|
+
*/
|
|
25
|
+
export declare function configDirectives(disabledRules: string[]): SuppressionDirective[];
|