@kernlang/review 3.3.7 → 3.3.9

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.
Files changed (53) hide show
  1. package/dist/cache.js +1 -1
  2. package/dist/concept-rules/auth-propagation-drift.d.ts +10 -0
  3. package/dist/concept-rules/auth-propagation-drift.js +83 -0
  4. package/dist/concept-rules/auth-propagation-drift.js.map +1 -0
  5. package/dist/concept-rules/body-shape-drift.d.ts +32 -0
  6. package/dist/concept-rules/body-shape-drift.js +96 -0
  7. package/dist/concept-rules/body-shape-drift.js.map +1 -0
  8. package/dist/concept-rules/cross-stack-utils.d.ts +24 -0
  9. package/dist/concept-rules/cross-stack-utils.js +123 -29
  10. package/dist/concept-rules/cross-stack-utils.js.map +1 -1
  11. package/dist/concept-rules/index.d.ts +4 -2
  12. package/dist/concept-rules/index.js +56 -4
  13. package/dist/concept-rules/index.js.map +1 -1
  14. package/dist/concept-rules/mutation-without-idempotency.d.ts +10 -0
  15. package/dist/concept-rules/mutation-without-idempotency.js +47 -0
  16. package/dist/concept-rules/mutation-without-idempotency.js.map +1 -0
  17. package/dist/concept-rules/param-name-swap.d.ts +22 -0
  18. package/dist/concept-rules/param-name-swap.js +120 -0
  19. package/dist/concept-rules/param-name-swap.js.map +1 -0
  20. package/dist/concept-rules/request-validation-drift.d.ts +11 -0
  21. package/dist/concept-rules/request-validation-drift.js +96 -0
  22. package/dist/concept-rules/request-validation-drift.js.map +1 -0
  23. package/dist/concept-rules/unbounded-collection-query.d.ts +10 -0
  24. package/dist/concept-rules/unbounded-collection-query.js +56 -0
  25. package/dist/concept-rules/unbounded-collection-query.js.map +1 -0
  26. package/dist/concept-rules/unhandled-api-error-shape.d.ts +10 -0
  27. package/dist/concept-rules/unhandled-api-error-shape.js +57 -0
  28. package/dist/concept-rules/unhandled-api-error-shape.js.map +1 -0
  29. package/dist/external-tools.js +52 -3
  30. package/dist/external-tools.js.map +1 -1
  31. package/dist/index.d.ts +13 -0
  32. package/dist/index.js +188 -38
  33. package/dist/index.js.map +1 -1
  34. package/dist/mappers/ts-concepts.js +663 -1
  35. package/dist/mappers/ts-concepts.js.map +1 -1
  36. package/dist/python-fallback.d.ts +2 -0
  37. package/dist/python-fallback.js +506 -0
  38. package/dist/python-fallback.js.map +1 -0
  39. package/dist/reporter.js +84 -1
  40. package/dist/reporter.js.map +1 -1
  41. package/dist/rules/async.js +159 -1
  42. package/dist/rules/async.js.map +1 -1
  43. package/dist/rules/base.js +21 -3
  44. package/dist/rules/base.js.map +1 -1
  45. package/dist/rules/index.js +40 -0
  46. package/dist/rules/index.js.map +1 -1
  47. package/dist/rules/kern-source.js +1 -0
  48. package/dist/rules/kern-source.js.map +1 -1
  49. package/dist/rules/suggest-kern-primitive.js +5 -5
  50. package/dist/rules/suggest-kern-primitive.js.map +1 -1
  51. package/dist/types.d.ts +25 -0
  52. package/dist/types.js.map +1 -1
  53. package/package.json +2 -2
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Rule: mutation-without-idempotency
3
+ *
4
+ * Backend rule — fires on mutating HTTP routes that perform a DB write without
5
+ * visible idempotency, transaction, unique/upsert, or duplicate-protection
6
+ * evidence.
7
+ */
8
+ import { createFingerprint } from '../types.js';
9
+ import { API_PATH_RE } from './cross-stack-utils.js';
10
+ const GUARD_MUTATING_METHODS = new Set(['POST']);
11
+ const AUDIT_MUTATING_METHODS = new Set(['POST', 'PATCH', 'DELETE']);
12
+ export function mutationWithoutIdempotency(ctx) {
13
+ const findings = [];
14
+ for (const node of ctx.concepts.nodes) {
15
+ if (node.kind !== 'entrypoint' || node.payload.kind !== 'entrypoint' || node.payload.subtype !== 'route')
16
+ continue;
17
+ const method = node.payload.httpMethod?.toUpperCase();
18
+ const mutatingMethods = ctx.crossStackMode === 'audit' ? AUDIT_MUTATING_METHODS : GUARD_MUTATING_METHODS;
19
+ if (!method || !mutatingMethods.has(method))
20
+ continue;
21
+ if (!API_PATH_RE.test(node.payload.name))
22
+ continue;
23
+ if (ctx.crossStackMode !== 'audit' && routeHasPathParam(node.payload.name))
24
+ continue;
25
+ if (node.payload.hasDbWrite !== true)
26
+ continue;
27
+ if (node.payload.hasIdempotencyProtection === true)
28
+ continue;
29
+ findings.push({
30
+ source: 'kern',
31
+ ruleId: 'mutation-without-idempotency',
32
+ severity: 'warning',
33
+ category: 'bug',
34
+ message: `Mutating route \`${method} ${node.payload.name}\` writes to the database without visible idempotency key, transaction, unique guard, upsert, or duplicate-protection evidence. Retries or double-submits can create duplicate side effects.`,
35
+ primarySpan: node.primarySpan,
36
+ fingerprint: createFingerprint('mutation-without-idempotency', node.primarySpan.startLine, node.primarySpan.startCol),
37
+ confidence: node.confidence * 0.75,
38
+ });
39
+ }
40
+ return findings;
41
+ }
42
+ function routeHasPathParam(path) {
43
+ return path
44
+ .split('/')
45
+ .some((segment) => segment.startsWith(':') || (segment.startsWith('{') && segment.endsWith('}')));
46
+ }
47
+ //# sourceMappingURL=mutation-without-idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-without-idempotency.js","sourceRoot":"","sources":["../../src/concept-rules/mutation-without-idempotency.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGrD,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACjD,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEpE,MAAM,UAAU,0BAA0B,CAAC,GAAuB;IAChE,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO;YAAE,SAAS;QACnH,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC;QACtD,MAAM,eAAe,GAAG,GAAG,CAAC,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,sBAAsB,CAAC;QACzG,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,SAAS;QACtD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,SAAS;QACnD,IAAI,GAAG,CAAC,cAAc,KAAK,OAAO,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,SAAS;QACrF,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,IAAI;YAAE,SAAS;QAC/C,IAAI,IAAI,CAAC,OAAO,CAAC,wBAAwB,KAAK,IAAI;YAAE,SAAS;QAE7D,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,8BAA8B;YACtC,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,oBAAoB,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,8LAA8L;YACtP,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,iBAAiB,CAC5B,8BAA8B,EAC9B,IAAI,CAAC,WAAW,CAAC,SAAS,EAC1B,IAAI,CAAC,WAAW,CAAC,QAAQ,CAC1B;YACD,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI;SACnC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtG,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Rule: param-name-swap
3
+ *
4
+ * Cross-stack rule — fires when a client URL and a server route share the
5
+ * same path shape (same segment count, same param positions) but a named
6
+ * param appears at a different position on each side. The classic bug:
7
+ *
8
+ * client: fetch(`/users/${userId}/posts/${postId}`) → /users/:userId/posts/:postId
9
+ * server: router.get('/users/:postId/posts/:userId')
10
+ *
11
+ * The path shape matches so `contract-drift` stays silent; the server
12
+ * will pull the wrong value out of `req.params`. This rule narrows on
13
+ * "same name, different position" only — firing on every name mismatch
14
+ * would flood the report, because client param names come from the JS
15
+ * identifier at the call site and server param names come from the route
16
+ * template, and they are not required to agree.
17
+ *
18
+ * Requires graph mode. Silent in single-file review.
19
+ */
20
+ import type { ReviewFinding } from '../types.js';
21
+ import type { ConceptRuleContext } from './index.js';
22
+ export declare function paramNameSwap(ctx: ConceptRuleContext): ReviewFinding[];
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Rule: param-name-swap
3
+ *
4
+ * Cross-stack rule — fires when a client URL and a server route share the
5
+ * same path shape (same segment count, same param positions) but a named
6
+ * param appears at a different position on each side. The classic bug:
7
+ *
8
+ * client: fetch(`/users/${userId}/posts/${postId}`) → /users/:userId/posts/:postId
9
+ * server: router.get('/users/:postId/posts/:userId')
10
+ *
11
+ * The path shape matches so `contract-drift` stays silent; the server
12
+ * will pull the wrong value out of `req.params`. This rule narrows on
13
+ * "same name, different position" only — firing on every name mismatch
14
+ * would flood the report, because client param names come from the JS
15
+ * identifier at the call site and server param names come from the route
16
+ * template, and they are not required to agree.
17
+ *
18
+ * Requires graph mode. Silent in single-file review.
19
+ */
20
+ import { createFingerprint } from '../types.js';
21
+ import { API_PATH_RE, CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutesAcrossGraph, normalizeClientUrl, } from './cross-stack-utils.js';
22
+ export function paramNameSwap(ctx) {
23
+ if (!ctx.allConcepts || ctx.allConcepts.size === 0)
24
+ return [];
25
+ const serverRoutes = collectRoutesAcrossGraph(ctx.allConcepts);
26
+ if (serverRoutes.length === 0)
27
+ return [];
28
+ const clientCalls = [];
29
+ for (const [, conceptMap] of ctx.allConcepts) {
30
+ for (const node of conceptMap.nodes) {
31
+ if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
32
+ continue;
33
+ const target = node.payload.target;
34
+ if (typeof target !== 'string')
35
+ continue;
36
+ const normalized = normalizeClientUrl(target);
37
+ if (!normalized || !API_PATH_RE.test(normalized))
38
+ continue;
39
+ clientCalls.push({ target, normalizedPath: normalized, node });
40
+ }
41
+ }
42
+ if (clientCalls.length === 0)
43
+ return [];
44
+ const findings = [];
45
+ const seen = new Set();
46
+ for (const call of clientCalls) {
47
+ if (call.node.primarySpan.file !== ctx.filePath)
48
+ continue;
49
+ const clientParts = call.normalizedPath.split('/');
50
+ for (const route of serverRoutes) {
51
+ const routeParts = route.path.split('/');
52
+ if (routeParts.length !== clientParts.length)
53
+ continue;
54
+ if (!sameLiterals(clientParts, routeParts))
55
+ continue;
56
+ const clientNames = paramNames(clientParts);
57
+ const routeNames = paramNames(routeParts);
58
+ if (clientNames.length < 2 || routeNames.length < 2)
59
+ continue;
60
+ const swap = findNameSwap(clientNames, routeNames);
61
+ if (!swap)
62
+ continue;
63
+ const fingerprint = createFingerprint('param-name-swap', call.node.primarySpan.startLine, call.node.primarySpan.startCol);
64
+ if (seen.has(fingerprint))
65
+ continue;
66
+ seen.add(fingerprint);
67
+ findings.push({
68
+ source: 'kern',
69
+ ruleId: 'param-name-swap',
70
+ severity: 'warning',
71
+ category: 'bug',
72
+ message: `Path param \`${swap.name}\` appears at position ${swap.clientIndex + 1} in the client URL \`${call.normalizedPath}\` but at position ${swap.serverIndex + 1} in server route \`${route.path}\`. A swap like this makes the server pull the wrong value from \`req.params\`.`,
73
+ primarySpan: call.node.primarySpan,
74
+ fingerprint,
75
+ confidence: call.node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
76
+ });
77
+ break;
78
+ }
79
+ }
80
+ return findings;
81
+ }
82
+ // Compare segments ignoring param names (both `:x` and `{x}` match any `:y`/`{y}`).
83
+ function sameLiterals(a, b) {
84
+ for (let i = 0; i < a.length; i++) {
85
+ const ap = isParamSeg(a[i]);
86
+ const bp = isParamSeg(b[i]);
87
+ if (ap !== bp)
88
+ return false;
89
+ if (!ap && a[i] !== b[i])
90
+ return false;
91
+ }
92
+ return true;
93
+ }
94
+ function isParamSeg(s) {
95
+ return s.startsWith(':') || (s.startsWith('{') && s.endsWith('}'));
96
+ }
97
+ function paramNames(parts) {
98
+ const out = [];
99
+ for (let i = 0; i < parts.length; i++) {
100
+ const p = parts[i];
101
+ if (p.startsWith(':'))
102
+ out.push({ index: i, name: p.slice(1) });
103
+ else if (p.startsWith('{') && p.endsWith('}'))
104
+ out.push({ index: i, name: p.slice(1, -1) });
105
+ }
106
+ return out;
107
+ }
108
+ // A swap exists when a name appears in BOTH sides but at different positions.
109
+ // We only report the first such swap per route to keep the message tight.
110
+ function findNameSwap(client, server) {
111
+ const serverByName = new Map(server.map((p) => [p.name, p.index]));
112
+ for (const cp of client) {
113
+ const sIdx = serverByName.get(cp.name);
114
+ if (sIdx !== undefined && sIdx !== cp.index) {
115
+ return { name: cp.name, clientIndex: cp.index, serverIndex: sIdx };
116
+ }
117
+ }
118
+ return undefined;
119
+ }
120
+ //# sourceMappingURL=param-name-swap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"param-name-swap.js","sourceRoot":"","sources":["../../src/concept-rules/param-name-swap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Rule: request-validation-drift
3
+ *
4
+ * Cross-stack/backend rule:
5
+ * - client sends fields outside the backend's resolved validation schema;
6
+ * - or a mutating backend route reads body fields and writes to DB with no
7
+ * visible body validation.
8
+ */
9
+ import type { ReviewFinding } from '../types.js';
10
+ import type { ConceptRuleContext } from './index.js';
11
+ export declare function requestValidationDrift(ctx: ConceptRuleContext): ReviewFinding[];
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Rule: request-validation-drift
3
+ *
4
+ * Cross-stack/backend rule:
5
+ * - client sends fields outside the backend's resolved validation schema;
6
+ * - or a mutating backend route reads body fields and writes to DB with no
7
+ * visible body validation.
8
+ */
9
+ import { createFingerprint } from '../types.js';
10
+ import { API_PATH_RE, CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutesAcrossGraph, findHighConfidenceRouteForMethod, findMatchingRouteForMethod, normalizeClientUrl, } from './cross-stack-utils.js';
11
+ const GUARD_BODY_METHODS = new Set(['POST']);
12
+ const AUDIT_BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);
13
+ export function requestValidationDrift(ctx) {
14
+ const findings = [];
15
+ findings.push(...backendUnvalidatedBodyFindings(ctx));
16
+ findings.push(...clientExtraFieldFindings(ctx));
17
+ return findings;
18
+ }
19
+ function backendUnvalidatedBodyFindings(ctx) {
20
+ const findings = [];
21
+ for (const node of ctx.concepts.nodes) {
22
+ if (node.kind !== 'entrypoint' || node.payload.kind !== 'entrypoint' || node.payload.subtype !== 'route')
23
+ continue;
24
+ const method = node.payload.httpMethod?.toUpperCase();
25
+ const bodyMethods = ctx.crossStackMode === 'audit' ? AUDIT_BODY_METHODS : GUARD_BODY_METHODS;
26
+ if (!method || !bodyMethods.has(method))
27
+ continue;
28
+ if (!API_PATH_RE.test(node.payload.name))
29
+ continue;
30
+ if (node.payload.hasDbWrite !== true)
31
+ continue;
32
+ if (node.payload.bodyFieldsResolved !== true || !node.payload.bodyFields || node.payload.bodyFields.length === 0) {
33
+ continue;
34
+ }
35
+ if (node.payload.hasBodyValidation === true)
36
+ continue;
37
+ const fields = node.payload.bodyFields.map((field) => `\`${field}\``).join(', ');
38
+ findings.push({
39
+ source: 'kern',
40
+ ruleId: 'request-validation-drift',
41
+ severity: 'warning',
42
+ category: 'bug',
43
+ message: `Route \`${method} ${node.payload.name}\` reads request body fields ${fields} and writes to the database without visible request-body validation.`,
44
+ primarySpan: node.primarySpan,
45
+ fingerprint: createFingerprint('request-validation-drift', node.primarySpan.startLine, node.primarySpan.startCol),
46
+ confidence: node.confidence * 0.8,
47
+ });
48
+ }
49
+ return findings;
50
+ }
51
+ function clientExtraFieldFindings(ctx) {
52
+ if (!ctx.allConcepts || ctx.allConcepts.size === 0)
53
+ return [];
54
+ const serverRoutes = collectRoutesAcrossGraph(ctx.allConcepts);
55
+ if (serverRoutes.length === 0)
56
+ return [];
57
+ const findings = [];
58
+ const localConcepts = ctx.allConcepts.get(ctx.filePath) ?? ctx.concepts;
59
+ for (const node of localConcepts.nodes) {
60
+ if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
61
+ continue;
62
+ if (node.payload.sentFieldsResolved !== true || !node.payload.sentFields)
63
+ continue;
64
+ const target = node.payload.target;
65
+ if (typeof target !== 'string')
66
+ continue;
67
+ const normalized = normalizeClientUrl(target);
68
+ if (!normalized)
69
+ continue;
70
+ const route = ctx.crossStackMode === 'audit'
71
+ ? findMatchingRouteForMethod(normalized, node.payload.method, serverRoutes)
72
+ : findHighConfidenceRouteForMethod(normalized, node.payload.method, serverRoutes);
73
+ if (route?.node?.payload.kind !== 'entrypoint')
74
+ continue;
75
+ if (route.node.payload.bodyValidationResolved !== true || !route.node.payload.validatedBodyFields)
76
+ continue;
77
+ const validated = new Set(route.node.payload.validatedBodyFields);
78
+ const extra = node.payload.sentFields.filter((field) => !validated.has(field));
79
+ if (extra.length === 0)
80
+ continue;
81
+ const fieldList = extra.map((field) => `\`${field}\``).join(', ');
82
+ findings.push({
83
+ source: 'kern',
84
+ ruleId: 'request-validation-drift',
85
+ severity: 'warning',
86
+ category: 'bug',
87
+ message: `Client sends ${fieldList} to \`${target}\`, but the matching backend validation schema does not accept ${extra.length === 1 ? 'that field' : 'those fields'}. Remove the extra payload data or update the backend schema intentionally.`,
88
+ primarySpan: node.primarySpan,
89
+ relatedSpans: [route.node.primarySpan],
90
+ fingerprint: createFingerprint('request-validation-drift', node.primarySpan.startLine, node.primarySpan.startCol),
91
+ confidence: node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
92
+ });
93
+ }
94
+ return findings;
95
+ }
96
+ //# sourceMappingURL=request-validation-drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-validation-drift.js","sourceRoot":"","sources":["../../src/concept-rules/request-validation-drift.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Rule: unbounded-collection-query
3
+ *
4
+ * Cross-stack rule — fires when a client calls a list endpoint without
5
+ * page/cursor/limit parameters and the matching server route appears to return
6
+ * a DB-backed collection without a bound.
7
+ */
8
+ import type { ReviewFinding } from '../types.js';
9
+ import type { ConceptRuleContext } from './index.js';
10
+ export declare function unboundedCollectionQuery(ctx: ConceptRuleContext): ReviewFinding[];
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Rule: unbounded-collection-query
3
+ *
4
+ * Cross-stack rule — fires when a client calls a list endpoint without
5
+ * page/cursor/limit parameters and the matching server route appears to return
6
+ * a DB-backed collection without a bound.
7
+ */
8
+ import { createFingerprint } from '../types.js';
9
+ import { CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutesAcrossGraph, findHighConfidenceRouteForMethod, findMatchingRouteForMethod, normalizeClientUrl, } from './cross-stack-utils.js';
10
+ const PAGINATION_QUERY_PARAMS = new Set(['limit', 'take', 'page', 'pageSize', 'perPage', 'cursor', 'offset', 'skip']);
11
+ export function unboundedCollectionQuery(ctx) {
12
+ if (!ctx.allConcepts || ctx.allConcepts.size === 0)
13
+ return [];
14
+ const serverRoutes = collectRoutesAcrossGraph(ctx.allConcepts);
15
+ if (serverRoutes.length === 0)
16
+ return [];
17
+ const findings = [];
18
+ const localConcepts = ctx.allConcepts.get(ctx.filePath) ?? ctx.concepts;
19
+ for (const node of localConcepts.nodes) {
20
+ if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
21
+ continue;
22
+ if (node.payload.queryParamsResolved !== true)
23
+ continue;
24
+ if (hasPaginationParam(node.payload.queryParams ?? []))
25
+ continue;
26
+ const target = node.payload.target;
27
+ if (typeof target !== 'string')
28
+ continue;
29
+ const normalized = normalizeClientUrl(target);
30
+ if (!normalized)
31
+ continue;
32
+ const route = ctx.crossStackMode === 'audit'
33
+ ? findMatchingRouteForMethod(normalized, node.payload.method, serverRoutes)
34
+ : findHighConfidenceRouteForMethod(normalized, node.payload.method, serverRoutes);
35
+ if (route?.node?.payload.kind !== 'entrypoint')
36
+ continue;
37
+ if (route.node.payload.hasUnboundedCollectionQuery !== true)
38
+ continue;
39
+ findings.push({
40
+ source: 'kern',
41
+ ruleId: 'unbounded-collection-query',
42
+ severity: 'warning',
43
+ category: 'bug',
44
+ message: `Client calls list endpoint \`${target}\` without page/cursor/limit parameters, and the matching server route appears to return an unbounded DB collection. Add pagination on both sides before this endpoint grows.`,
45
+ primarySpan: node.primarySpan,
46
+ relatedSpans: [route.node.primarySpan],
47
+ fingerprint: createFingerprint('unbounded-collection-query', node.primarySpan.startLine, node.primarySpan.startCol),
48
+ confidence: node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
49
+ });
50
+ }
51
+ return findings;
52
+ }
53
+ function hasPaginationParam(params) {
54
+ return params.some((param) => PAGINATION_QUERY_PARAMS.has(param));
55
+ }
56
+ //# sourceMappingURL=unbounded-collection-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unbounded-collection-query.js","sourceRoot":"","sources":["../../src/concept-rules/unbounded-collection-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Rule: unhandled-api-error-shape
3
+ *
4
+ * Cross-stack rule — fires when a client calls a matching project API route
5
+ * without a visible error path while the backend route can explicitly return
6
+ * an error status such as 401/403/404/422/500.
7
+ */
8
+ import type { ReviewFinding } from '../types.js';
9
+ import type { ConceptRuleContext } from './index.js';
10
+ export declare function unhandledApiErrorShape(ctx: ConceptRuleContext): ReviewFinding[];
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Rule: unhandled-api-error-shape
3
+ *
4
+ * Cross-stack rule — fires when a client calls a matching project API route
5
+ * without a visible error path while the backend route can explicitly return
6
+ * an error status such as 401/403/404/422/500.
7
+ */
8
+ import { createFingerprint } from '../types.js';
9
+ import { CROSS_STACK_EXACT_CONFIDENCE, collectRoutesAcrossGraph, findHighConfidenceRouteForMethod, findMatchingRouteForMethod, normalizeClientUrl, } from './cross-stack-utils.js';
10
+ const ERROR_STATUS_CODES = new Set([401, 403, 404, 422, 500]);
11
+ export function unhandledApiErrorShape(ctx) {
12
+ if (!ctx.allConcepts || ctx.allConcepts.size === 0)
13
+ return [];
14
+ const serverRoutes = collectRoutesAcrossGraph(ctx.allConcepts);
15
+ if (serverRoutes.length === 0)
16
+ return [];
17
+ const findings = [];
18
+ const localConcepts = ctx.allConcepts.get(ctx.filePath) ?? ctx.concepts;
19
+ for (const node of localConcepts.nodes) {
20
+ if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
21
+ continue;
22
+ if (node.payload.handlesApiErrors !== false)
23
+ continue;
24
+ const target = node.payload.target;
25
+ if (typeof target !== 'string')
26
+ continue;
27
+ const normalized = normalizeClientUrl(target);
28
+ if (!normalized)
29
+ continue;
30
+ if (!isRawFetchEffect(node.evidence))
31
+ continue;
32
+ const route = ctx.crossStackMode === 'audit'
33
+ ? findMatchingRouteForMethod(normalized, node.payload.method, serverRoutes)
34
+ : findHighConfidenceRouteForMethod(normalized, node.payload.method, serverRoutes);
35
+ const statusCodes = route?.node?.payload.kind === 'entrypoint' ? route.node.payload.errorStatusCodes : undefined;
36
+ const relevantCodes = (statusCodes ?? []).filter((code) => ERROR_STATUS_CODES.has(code));
37
+ if (relevantCodes.length === 0)
38
+ continue;
39
+ const codeList = relevantCodes.join('/');
40
+ findings.push({
41
+ source: 'kern',
42
+ ruleId: 'unhandled-api-error-shape',
43
+ severity: 'warning',
44
+ category: 'bug',
45
+ message: `Client calls \`${target}\` with only the success path handled, but the matching server route can return ${codeList}. Add a \`response.ok\`/status branch, catch path, or error UI for the API error shape.`,
46
+ primarySpan: node.primarySpan,
47
+ relatedSpans: route?.node ? [route.node.primarySpan] : undefined,
48
+ fingerprint: createFingerprint('unhandled-api-error-shape', node.primarySpan.startLine, node.primarySpan.startCol),
49
+ confidence: node.confidence * CROSS_STACK_EXACT_CONFIDENCE,
50
+ });
51
+ }
52
+ return findings;
53
+ }
54
+ function isRawFetchEffect(evidence) {
55
+ return /^\s*(?:await\s+)?fetch\s*\(/.test(evidence);
56
+ }
57
+ //# sourceMappingURL=unhandled-api-error-shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unhandled-api-error-shape.js","sourceRoot":"","sources":["../../src/concept-rules/unhandled-api-error-shape.ts"],"names":[],"mappings":"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"}
@@ -6,9 +6,17 @@
6
6
  *
7
7
  * Phase 3 of the review pipeline.
8
8
  */
9
+ import { existsSync } from 'fs';
10
+ import { dirname, resolve } from 'path';
9
11
  import { createProject } from './inferrer.js';
10
12
  import { debugDetail } from './review-health.js';
11
13
  import { createFingerprint } from './types.js';
14
+ function optionalPackageName(...parts) {
15
+ return parts.join('');
16
+ }
17
+ function importOptionalModule(specifier) {
18
+ return import(specifier);
19
+ }
12
20
  /**
13
21
  * Node-style error check — used to distinguish "optional peer dep missing" (quietly skip)
14
22
  * from "the peer dep is installed but failed to load" (surface as degraded-mode health note).
@@ -35,10 +43,10 @@ function isModuleNotFound(err) {
35
43
  export async function runESLint(filePaths, cwd, health) {
36
44
  // Dynamic import — ESLint is an optional peer dep. MODULE_NOT_FOUND at this step means
37
45
  // "not installed" (quiet skip); anything else is a real load failure worth surfacing.
38
- const eslintModuleName = 'eslint';
46
+ const eslintModuleName = optionalPackageName('es', 'lint');
39
47
  let ESLint;
40
48
  try {
41
- const eslintModule = (await import(eslintModuleName));
49
+ const eslintModule = (await importOptionalModule(eslintModuleName));
42
50
  ESLint = eslintModule.ESLint || eslintModule.default?.ESLint;
43
51
  }
44
52
  catch (err) {
@@ -163,7 +171,8 @@ export function runTSCDiagnostics(project, options = {}, health) {
163
171
  // both point at the same user-side remedy, both are environmental from review's POV.)
164
172
  const isLoadingNoise = code === 6059 || code === 6307;
165
173
  const isEnvironmentalNoise = code === 2792 || code === 17004 || code === 2580 || code === 2591;
166
- if ((isLoadingNoise || isEnvironmentalNoise) && options.downgradeProjectLoadingErrors) {
174
+ if (options.downgradeProjectLoadingErrors &&
175
+ (isLoadingNoise || isEnvironmentalNoise || isReviewModeModuleResolutionNoise(code, messageStr, filePath))) {
167
176
  continue;
168
177
  }
169
178
  findings.push({
@@ -191,6 +200,46 @@ export function runTSCDiagnostics(project, options = {}, health) {
191
200
  }
192
201
  return findings;
193
202
  }
203
+ function isReviewModeModuleResolutionNoise(code, message, importerFilePath) {
204
+ if (code !== 2307)
205
+ return false;
206
+ const specifier = extractMissingModuleSpecifier(message);
207
+ if (!specifier)
208
+ return false;
209
+ // KERN-generated facades are commonly imported as `.js` from TS source and
210
+ // materialized by `kern compile`. In guard mode, a missing generated facade is
211
+ // pipeline ordering noise unless the explicit lint/typecheck phase says
212
+ // otherwise.
213
+ if (isGeneratedModuleSpecifier(specifier))
214
+ return true;
215
+ // Bare package misses (`vitest`, `ai`, etc.) are dependency-install or workspace
216
+ // context failures in review mode. The explicit `--lint` tsc path still reports
217
+ // them as real compiler errors.
218
+ if (isBareModuleSpecifier(specifier))
219
+ return true;
220
+ // TS ESM commonly imports `./foo.js` while the source file is `foo.ts`. If the
221
+ // corresponding TS source exists, this is a moduleResolution mismatch in
222
+ // review's ad-hoc Project, not a code bug.
223
+ return isTsBackedJsSpecifier(specifier, importerFilePath);
224
+ }
225
+ function extractMissingModuleSpecifier(message) {
226
+ const match = message.match(/Cannot find module ['"]([^'"]+)['"]/);
227
+ return match?.[1];
228
+ }
229
+ function isGeneratedModuleSpecifier(specifier) {
230
+ const normalized = specifier.replace(/\\/g, '/');
231
+ return /(?:^|\/)generated\//.test(normalized) || /(?:^|\/)__generated__\//.test(normalized);
232
+ }
233
+ function isBareModuleSpecifier(specifier) {
234
+ return !specifier.startsWith('.') && !specifier.startsWith('/') && !/^[A-Za-z]:[\\/]/.test(specifier);
235
+ }
236
+ function isTsBackedJsSpecifier(specifier, importerFilePath) {
237
+ if (!specifier.startsWith('.') || !/\.(?:mjs|cjs|js|jsx)$/.test(specifier))
238
+ return false;
239
+ const resolved = resolve(dirname(importerFilePath), specifier);
240
+ const withoutJsExt = resolved.replace(/\.(?:mjs|cjs|js|jsx)$/, '');
241
+ return ['.ts', '.tsx', '.mts', '.cts', '.d.ts'].some((ext) => existsSync(`${withoutJsExt}${ext}`));
242
+ }
194
243
  // ── tsc Diagnostics from file paths ───────────────────────────────────
195
244
  /**
196
245
  * Run TypeScript compiler diagnostics from file paths (no pre-existing Project).
@@ -1 +1 @@
1
- {"version":3,"file":"external-tools.js","sourceRoot":"","sources":["../src/external-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"external-tools.js","sourceRoot":"","sources":["../src/external-tools.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  *
9
9
  * v2: Unified ReviewFinding pipeline. All findings merged into single array.
10
10
  */
11
+ import type { ConceptMap } from '@kernlang/core';
11
12
  import type { GraphOptions } from './types.js';
12
13
  import type { ReviewConfig, ReviewReport } from './types.js';
13
14
  export type { CallGraph, CallSite, FunctionNode } from './call-graph.js';
@@ -82,6 +83,18 @@ export declare function refreshFsProjectFromDisk(): number;
82
83
  /** True when the file is codegen output — detected via common path patterns or a @generated header. */
83
84
  export declare function isGeneratedFile(filePath: string, source?: string): boolean;
84
85
  export declare function isReviewableFile(filePath: string): boolean;
86
+ /**
87
+ * Extract concept maps from a set of files without running any review
88
+ * rules. Returns one entry per file that was parsed successfully;
89
+ * unparseable or unknown-extension files are skipped silently.
90
+ *
91
+ * Intended for consumers (kern-guard) that want to cache a repo's IR
92
+ * once on push and replay it into later `reviewGraph` calls via
93
+ * `ReviewConfig.externalConcepts`. Much cheaper than running the full
94
+ * review pipeline — no rule evaluation, no import-graph resolution, no
95
+ * health aggregation.
96
+ */
97
+ export declare function extractConceptsForGraph(filePaths: string[]): Map<string, ConceptMap>;
85
98
  /**
86
99
  * Review a single file. Auto-detects language from extension.
87
100
  * Uses a filesystem-backed ts-morph Project for type-aware analysis.