@kernlang/review 3.3.9 → 3.4.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.
Files changed (97) hide show
  1. package/dist/call-graph.d.ts +10 -0
  2. package/dist/call-graph.js +138 -9
  3. package/dist/call-graph.js.map +1 -1
  4. package/dist/concept-rules/auth-drift.js +2 -0
  5. package/dist/concept-rules/auth-drift.js.map +1 -1
  6. package/dist/concept-rules/auth-propagation-drift.js +2 -0
  7. package/dist/concept-rules/auth-propagation-drift.js.map +1 -1
  8. package/dist/concept-rules/body-shape-drift.d.ts +1 -1
  9. package/dist/concept-rules/body-shape-drift.js +5 -3
  10. package/dist/concept-rules/body-shape-drift.js.map +1 -1
  11. package/dist/concept-rules/contract-drift.js +3 -1
  12. package/dist/concept-rules/contract-drift.js.map +1 -1
  13. package/dist/concept-rules/contract-method-drift.js +2 -0
  14. package/dist/concept-rules/contract-method-drift.js.map +1 -1
  15. package/dist/concept-rules/index.js +2 -33
  16. package/dist/concept-rules/index.js.map +1 -1
  17. package/dist/concept-rules/request-validation-drift.js +3 -0
  18. package/dist/concept-rules/request-validation-drift.js.map +1 -1
  19. package/dist/concept-rules/root-cause.d.ts +4 -0
  20. package/dist/concept-rules/root-cause.js +31 -0
  21. package/dist/concept-rules/root-cause.js.map +1 -0
  22. package/dist/concept-rules/unbounded-collection-query.js +2 -0
  23. package/dist/concept-rules/unbounded-collection-query.js.map +1 -1
  24. package/dist/concept-rules/unhandled-api-error-shape.js +2 -0
  25. package/dist/concept-rules/unhandled-api-error-shape.js.map +1 -1
  26. package/dist/default-export.d.ts +41 -0
  27. package/dist/default-export.js +76 -0
  28. package/dist/default-export.js.map +1 -0
  29. package/dist/eval.d.ts +67 -0
  30. package/dist/eval.js +177 -0
  31. package/dist/eval.js.map +1 -0
  32. package/dist/file-context.js +32 -13
  33. package/dist/file-context.js.map +1 -1
  34. package/dist/file-role.d.ts +6 -0
  35. package/dist/file-role.js +27 -0
  36. package/dist/file-role.js.map +1 -1
  37. package/dist/framework-seeds.d.ts +46 -0
  38. package/dist/framework-seeds.js +245 -0
  39. package/dist/framework-seeds.js.map +1 -0
  40. package/dist/git-env.d.ts +1 -0
  41. package/dist/git-env.js +25 -0
  42. package/dist/git-env.js.map +1 -0
  43. package/dist/graph.js +246 -21
  44. package/dist/graph.js.map +1 -1
  45. package/dist/index.d.ts +10 -2
  46. package/dist/index.js +200 -56
  47. package/dist/index.js.map +1 -1
  48. package/dist/mappers/ts-concepts.js +87 -20
  49. package/dist/mappers/ts-concepts.js.map +1 -1
  50. package/dist/path-canonical.d.ts +34 -0
  51. package/dist/path-canonical.js +85 -0
  52. package/dist/path-canonical.js.map +1 -0
  53. package/dist/policy.d.ts +22 -0
  54. package/dist/policy.js +47 -0
  55. package/dist/policy.js.map +1 -0
  56. package/dist/project-context.d.ts +135 -0
  57. package/dist/project-context.js +563 -0
  58. package/dist/project-context.js.map +1 -0
  59. package/dist/public-api.d.ts +21 -0
  60. package/dist/public-api.js +17 -2
  61. package/dist/public-api.js.map +1 -1
  62. package/dist/reporter.js +22 -0
  63. package/dist/reporter.js.map +1 -1
  64. package/dist/rule-quality.d.ts +58 -0
  65. package/dist/rule-quality.js +357 -0
  66. package/dist/rule-quality.js.map +1 -0
  67. package/dist/rules/dead-code.d.ts +2 -2
  68. package/dist/rules/dead-code.js +88 -4
  69. package/dist/rules/dead-code.js.map +1 -1
  70. package/dist/rules/index.d.ts +22 -0
  71. package/dist/rules/index.js +32 -0
  72. package/dist/rules/index.js.map +1 -1
  73. package/dist/rules/kern-source.d.ts +4 -0
  74. package/dist/rules/kern-source.js +183 -0
  75. package/dist/rules/kern-source.js.map +1 -1
  76. package/dist/rules/react.js +52 -3
  77. package/dist/rules/react.js.map +1 -1
  78. package/dist/rules/suggest-kern-primitive.js +0 -1
  79. package/dist/rules/suggest-kern-primitive.js.map +1 -1
  80. package/dist/semantic-diff.js +2 -0
  81. package/dist/semantic-diff.js.map +1 -1
  82. package/dist/suppression/apply-suppression.js +2 -0
  83. package/dist/suppression/apply-suppression.js.map +1 -1
  84. package/dist/suppression/parse-directives.d.ts +13 -5
  85. package/dist/suppression/parse-directives.js +62 -8
  86. package/dist/suppression/parse-directives.js.map +1 -1
  87. package/dist/suppression/types.d.ts +9 -0
  88. package/dist/suppression/types.js +6 -1
  89. package/dist/suppression/types.js.map +1 -1
  90. package/dist/taint-crossfile.js +15 -8
  91. package/dist/taint-crossfile.js.map +1 -1
  92. package/dist/telemetry.d.ts +126 -0
  93. package/dist/telemetry.js +303 -0
  94. package/dist/telemetry.js.map +1 -0
  95. package/dist/types.d.ts +165 -1
  96. package/dist/types.js.map +1 -1
  97. package/package.json +4 -3
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { createFingerprint } from '../types.js';
10
10
  import { API_PATH_RE, CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutesAcrossGraph, findHighConfidenceRouteForMethod, findMatchingRouteForMethod, normalizeClientUrl, } from './cross-stack-utils.js';
11
+ import { apiCallRootCause, routeRootCause } from './root-cause.js';
11
12
  const GUARD_BODY_METHODS = new Set(['POST']);
12
13
  const AUDIT_BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);
13
14
  export function requestValidationDrift(ctx) {
@@ -44,6 +45,7 @@ function backendUnvalidatedBodyFindings(ctx) {
44
45
  primarySpan: node.primarySpan,
45
46
  fingerprint: createFingerprint('request-validation-drift', node.primarySpan.startLine, node.primarySpan.startCol),
46
47
  confidence: node.confidence * 0.8,
48
+ rootCause: routeRootCause(node, method),
47
49
  });
48
50
  }
49
51
  return findings;
@@ -89,6 +91,7 @@ function clientExtraFieldFindings(ctx) {
89
91
  relatedSpans: [route.node.primarySpan],
90
92
  fingerprint: createFingerprint('request-validation-drift', node.primarySpan.startLine, node.primarySpan.startCol),
91
93
  confidence: node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
94
+ rootCause: apiCallRootCause(node, normalized, node.payload.method, route.node),
92
95
  });
93
96
  }
94
97
  return findings;
@@ -1 +1 @@
1
- {"version":3,"file":"request-validation-drift.js","sourceRoot":"","sources":["../../src/concept-rules/request-validation-drift.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"request-validation-drift.js","sourceRoot":"","sources":["../../src/concept-rules/request-validation-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,WAAW,EACX,gCAAgC,EAChC,wBAAwB,EACxB,gCAAgC,EAChC,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAC7C,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAE7D,MAAM,UAAU,sBAAsB,CAAC,GAAuB;IAC5D,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,CAAC,GAAG,8BAA8B,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,IAAI,CAAC,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,8BAA8B,CAAC,GAAuB;IAC7D,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,WAAW,GAAG,GAAG,CAAC,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,kBAAkB,CAAC;QAC7F,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,SAAS;QAClD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,SAAS;QACnD,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,IAAI;YAAE,SAAS;QAC/C,IAAI,IAAI,CAAC,OAAO,CAAC,kBAAkB,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjH,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,iBAAiB,KAAK,IAAI;YAAE,SAAS;QAEtD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjF,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,0BAA0B;YAClC,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,WAAW,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,gCAAgC,MAAM,sEAAsE;YAC3J,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,iBAAiB,CAAC,0BAA0B,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YACjH,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,GAAG;YACjC,SAAS,EAAE,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC;SACxC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,wBAAwB,CAAC,GAAuB;IACvD,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9D,MAAM,YAAY,GAAG,wBAAwB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/D,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC;IAExE,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS;YAAE,SAAS;QAC7G,IAAI,IAAI,CAAC,OAAO,CAAC,kBAAkB,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU;YAAE,SAAS;QACnF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACnC,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,SAAS;QACzC,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,UAAU;YAAE,SAAS;QAE1B,MAAM,KAAK,GACT,GAAG,CAAC,cAAc,KAAK,OAAO;YAC5B,CAAC,CAAC,0BAA0B,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC;YAC3E,CAAC,CAAC,gCAAgC,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACtF,IAAI,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,YAAY;YAAE,SAAS;QACzD,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,sBAAsB,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB;YAAE,SAAS;QAE5G,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAEjC,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,0BAA0B;YAClC,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,gBAAgB,SAAS,SAAS,MAAM,kEAAkE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,6EAA6E;YAClP,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC;YACtC,WAAW,EAAE,iBAAiB,CAAC,0BAA0B,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YACjH,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,gCAAgC;YAC9D,SAAS,EAAE,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;SAC/E,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ConceptNode } from '@kernlang/core';
2
+ import type { RootCause } from '../types.js';
3
+ export declare function apiCallRootCause(clientNode: ConceptNode, normalizedPath: string, method?: string, routeNode?: ConceptNode): RootCause;
4
+ export declare function routeRootCause(routeNode: ConceptNode, method?: string): RootCause;
@@ -0,0 +1,31 @@
1
+ function methodKey(method, fallback = 'GET') {
2
+ return (method ?? fallback).toUpperCase();
3
+ }
4
+ export function apiCallRootCause(clientNode, normalizedPath, method, routeNode) {
5
+ const httpMethod = methodKey(method);
6
+ const routePart = routeNode ? ` route=${routeNode.id}` : '';
7
+ return {
8
+ kind: 'api-call',
9
+ key: `api-call client=${clientNode.id} method=${httpMethod} path=${normalizedPath}${routePart}`,
10
+ facets: {
11
+ clientNodeId: clientNode.id,
12
+ method: httpMethod,
13
+ path: normalizedPath,
14
+ ...(routeNode ? { routeNodeId: routeNode.id } : {}),
15
+ },
16
+ };
17
+ }
18
+ export function routeRootCause(routeNode, method) {
19
+ const name = routeNode.payload.kind === 'entrypoint' ? routeNode.payload.name : routeNode.id;
20
+ const httpMethod = methodKey(method, 'ANY');
21
+ return {
22
+ kind: 'route',
23
+ key: `route node=${routeNode.id} method=${httpMethod} path=${name}`,
24
+ facets: {
25
+ routeNodeId: routeNode.id,
26
+ method: httpMethod,
27
+ path: name,
28
+ },
29
+ };
30
+ }
31
+ //# sourceMappingURL=root-cause.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-cause.js","sourceRoot":"","sources":["../../src/concept-rules/root-cause.ts"],"names":[],"mappings":"AAGA,SAAS,SAAS,CAAC,MAA0B,EAAE,QAAQ,GAAG,KAAK;IAC7D,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,UAAuB,EACvB,cAAsB,EACtB,MAAe,EACf,SAAuB;IAEvB,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,GAAG,EAAE,mBAAmB,UAAU,CAAC,EAAE,WAAW,UAAU,SAAS,cAAc,GAAG,SAAS,EAAE;QAC/F,MAAM,EAAE;YACN,YAAY,EAAE,UAAU,CAAC,EAAE;YAC3B,MAAM,EAAE,UAAU;YAClB,IAAI,EAAE,cAAc;YACpB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpD;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAsB,EAAE,MAAe;IACpE,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;IAC7F,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5C,OAAO;QACL,IAAI,EAAE,OAAO;QACb,GAAG,EAAE,cAAc,SAAS,CAAC,EAAE,WAAW,UAAU,SAAS,IAAI,EAAE;QACnE,MAAM,EAAE;YACN,WAAW,EAAE,SAAS,CAAC,EAAE;YACzB,MAAM,EAAE,UAAU;YAClB,IAAI,EAAE,IAAI;SACX;KACF,CAAC;AACJ,CAAC"}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { createFingerprint } from '../types.js';
9
9
  import { CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutesAcrossGraph, findHighConfidenceRouteForMethod, findMatchingRouteForMethod, normalizeClientUrl, } from './cross-stack-utils.js';
10
+ import { apiCallRootCause } from './root-cause.js';
10
11
  const PAGINATION_QUERY_PARAMS = new Set(['limit', 'take', 'page', 'pageSize', 'perPage', 'cursor', 'offset', 'skip']);
11
12
  export function unboundedCollectionQuery(ctx) {
12
13
  if (!ctx.allConcepts || ctx.allConcepts.size === 0)
@@ -46,6 +47,7 @@ export function unboundedCollectionQuery(ctx) {
46
47
  relatedSpans: [route.node.primarySpan],
47
48
  fingerprint: createFingerprint('unbounded-collection-query', node.primarySpan.startLine, node.primarySpan.startCol),
48
49
  confidence: node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
50
+ rootCause: apiCallRootCause(node, normalized, node.payload.method, route.node),
49
51
  });
50
52
  }
51
53
  return findings;
@@ -1 +1 @@
1
- {"version":3,"file":"unbounded-collection-query.js","sourceRoot":"","sources":["../../src/concept-rules/unbounded-collection-query.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,gCAAgC,EAChC,wBAAwB,EACxB,gCAAgC,EAChC,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAEtH,MAAM,UAAU,wBAAwB,CAAC,GAAuB;IAC9D,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9D,MAAM,YAAY,GAAG,wBAAwB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/D,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC;IAExE,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS;YAAE,SAAS;QAC7G,IAAI,IAAI,CAAC,OAAO,CAAC,mBAAmB,KAAK,IAAI;YAAE,SAAS;QACxD,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;YAAE,SAAS;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACnC,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,SAAS;QACzC,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,UAAU;YAAE,SAAS;QAE1B,MAAM,KAAK,GACT,GAAG,CAAC,cAAc,KAAK,OAAO;YAC5B,CAAC,CAAC,0BAA0B,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC;YAC3E,CAAC,CAAC,gCAAgC,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACtF,IAAI,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,YAAY;YAAE,SAAS;QACzD,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,2BAA2B,KAAK,IAAI;YAAE,SAAS;QAEtE,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,4BAA4B;YACpC,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,gCAAgC,MAAM,+KAA+K;YAC9N,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC;YACtC,WAAW,EAAE,iBAAiB,CAC5B,4BAA4B,EAC5B,IAAI,CAAC,WAAW,CAAC,SAAS,EAC1B,IAAI,CAAC,WAAW,CAAC,QAAQ,CAC1B;YACD,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,gCAAgC;SAC/D,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAyB;IACnD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AACpE,CAAC"}
1
+ {"version":3,"file":"unbounded-collection-query.js","sourceRoot":"","sources":["../../src/concept-rules/unbounded-collection-query.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { createFingerprint } from '../types.js';
9
9
  import { CROSS_STACK_EXACT_CONFIDENCE, collectRoutesAcrossGraph, findHighConfidenceRouteForMethod, findMatchingRouteForMethod, normalizeClientUrl, } from './cross-stack-utils.js';
10
+ import { apiCallRootCause } from './root-cause.js';
10
11
  const ERROR_STATUS_CODES = new Set([401, 403, 404, 422, 500]);
11
12
  export function unhandledApiErrorShape(ctx) {
12
13
  if (!ctx.allConcepts || ctx.allConcepts.size === 0)
@@ -47,6 +48,7 @@ export function unhandledApiErrorShape(ctx) {
47
48
  relatedSpans: route?.node ? [route.node.primarySpan] : undefined,
48
49
  fingerprint: createFingerprint('unhandled-api-error-shape', node.primarySpan.startLine, node.primarySpan.startCol),
49
50
  confidence: node.confidence * CROSS_STACK_EXACT_CONFIDENCE,
51
+ rootCause: apiCallRootCause(node, normalized, node.payload.method, route?.node),
50
52
  });
51
53
  }
52
54
  return findings;
@@ -1 +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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"}
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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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Default-export resolver — bridges the gap between how seeds express
3
+ * "this file's default export is public" (`{filePath}#default`) and how
4
+ * the call graph indexes the same symbol internally.
5
+ *
6
+ * For `export default function Page() {}`, the call graph stores the
7
+ * function under its declaration name (`Page`). The framework seed
8
+ * (Next.js page convention, etc.) uses `default`. Without a bridge, the
9
+ * (filePath, 'Page') dead-export check looks up (filePath, 'default') in
10
+ * the seed map and misses — flagging Page as dead. This module returns
11
+ * the call-graph-internal name so the caller can also accept that.
12
+ *
13
+ * Step 9b wires this into the dead-export rule. Splitting it lets us
14
+ * test the resolver against every TypeScript default-export shape in
15
+ * isolation — there are five distinct cases and getting any one wrong
16
+ * silently re-introduces FPs.
17
+ */
18
+ import { type SourceFile } from 'ts-morph';
19
+ /**
20
+ * Resolve the call-graph-internal name of a source file's default export.
21
+ *
22
+ * Returns the identifier the call graph would track the symbol under —
23
+ * `'Page'` for `export default function Page() {}`, `'x'` for
24
+ * `export default x` or `export { x as default }`, and `undefined` for
25
+ * anonymous defaults (`export default function () {}` / `export default 42`)
26
+ * or files with no default export at all.
27
+ *
28
+ * The five cases red-team #11 enumerated:
29
+ * 1. `export default function Page() {}` → `'Page'`
30
+ * 2. `export default class Page {}` → `'Page'`
31
+ * 3. `export default x` (where x is a local identifier) → `'x'`
32
+ * 4. `export { x as default }` → `'x'`
33
+ * 5. anonymous (`export default function () {}`, `export default 42`,
34
+ * `export default { ... }`) → undefined
35
+ *
36
+ * `undefined` callers should fall back to the literal `'default'` —
37
+ * that's how the call graph keys anonymous defaults internally
38
+ * (resolveDefaultExportBinding in call-graph.ts uses 'default' as the
39
+ * fallback name when the symbol carries no identifier).
40
+ */
41
+ export declare function resolveDefaultExportName(sourceFile: SourceFile): string | undefined;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Default-export resolver — bridges the gap between how seeds express
3
+ * "this file's default export is public" (`{filePath}#default`) and how
4
+ * the call graph indexes the same symbol internally.
5
+ *
6
+ * For `export default function Page() {}`, the call graph stores the
7
+ * function under its declaration name (`Page`). The framework seed
8
+ * (Next.js page convention, etc.) uses `default`. Without a bridge, the
9
+ * (filePath, 'Page') dead-export check looks up (filePath, 'default') in
10
+ * the seed map and misses — flagging Page as dead. This module returns
11
+ * the call-graph-internal name so the caller can also accept that.
12
+ *
13
+ * Step 9b wires this into the dead-export rule. Splitting it lets us
14
+ * test the resolver against every TypeScript default-export shape in
15
+ * isolation — there are five distinct cases and getting any one wrong
16
+ * silently re-introduces FPs.
17
+ */
18
+ import { SyntaxKind } from 'ts-morph';
19
+ /**
20
+ * Resolve the call-graph-internal name of a source file's default export.
21
+ *
22
+ * Returns the identifier the call graph would track the symbol under —
23
+ * `'Page'` for `export default function Page() {}`, `'x'` for
24
+ * `export default x` or `export { x as default }`, and `undefined` for
25
+ * anonymous defaults (`export default function () {}` / `export default 42`)
26
+ * or files with no default export at all.
27
+ *
28
+ * The five cases red-team #11 enumerated:
29
+ * 1. `export default function Page() {}` → `'Page'`
30
+ * 2. `export default class Page {}` → `'Page'`
31
+ * 3. `export default x` (where x is a local identifier) → `'x'`
32
+ * 4. `export { x as default }` → `'x'`
33
+ * 5. anonymous (`export default function () {}`, `export default 42`,
34
+ * `export default { ... }`) → undefined
35
+ *
36
+ * `undefined` callers should fall back to the literal `'default'` —
37
+ * that's how the call graph keys anonymous defaults internally
38
+ * (resolveDefaultExportBinding in call-graph.ts uses 'default' as the
39
+ * fallback name when the symbol carries no identifier).
40
+ */
41
+ export function resolveDefaultExportName(sourceFile) {
42
+ const symbol = sourceFile.getDefaultExportSymbol();
43
+ if (!symbol)
44
+ return undefined;
45
+ for (const decl of symbol.getDeclarations()) {
46
+ const kind = decl.getKind();
47
+ // Case 3 + 4: `export default x` and `export { x as default }`.
48
+ // Both surface as ExportAssignment in ts-morph; getExpression() is
49
+ // the identifier when a name is in play.
50
+ if (kind === SyntaxKind.ExportAssignment) {
51
+ const expr = decl.getExpression();
52
+ if (expr.getKind() === SyntaxKind.Identifier) {
53
+ const text = expr.getText();
54
+ if (text)
55
+ return text;
56
+ }
57
+ // Anything else under an ExportAssignment is anonymous (literal,
58
+ // object expression, function expression without a name, …).
59
+ // Fall through to the next declaration just in case (rare for
60
+ // ExportSpecifier-style aliasing the symbol resolved to).
61
+ continue;
62
+ }
63
+ // Cases 1 + 2 + the named-function-expression form of 5: anything
64
+ // with a `getName()` method (FunctionDeclaration, ClassDeclaration,
65
+ // FunctionExpression, ClassExpression). Anonymous functions/classes
66
+ // declared inline via `export default function () {}` have no name.
67
+ const named = decl;
68
+ if (typeof named.getName === 'function') {
69
+ const name = named.getName();
70
+ if (name)
71
+ return name;
72
+ }
73
+ }
74
+ return undefined;
75
+ }
76
+ //# sourceMappingURL=default-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-export.js","sourceRoot":"","sources":["../src/default-export.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAqD,UAAU,EAAE,MAAM,UAAU,CAAC;AAEzF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAsB;IAC7D,MAAM,MAAM,GAAG,UAAU,CAAC,sBAAsB,EAAE,CAAC;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,eAAe,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAE5B,gEAAgE;QAChE,mEAAmE;QACnE,yCAAyC;QACzC,IAAI,IAAI,KAAK,UAAU,CAAC,gBAAgB,EAAE,CAAC;YACzC,MAAM,IAAI,GAAI,IAAyB,CAAC,aAAa,EAAE,CAAC;YACxD,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC5B,IAAI,IAAI;oBAAE,OAAO,IAAI,CAAC;YACxB,CAAC;YACD,iEAAiE;YACjE,6DAA6D;YAC7D,8DAA8D;YAC9D,0DAA0D;YAC1D,SAAS;QACX,CAAC;QAED,kEAAkE;QAClE,oEAAoE;QACpE,oEAAoE;QACpE,oEAAoE;QACpE,MAAM,KAAK,GAAG,IAAqD,CAAC;QACpE,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
package/dist/eval.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ import type { ReviewConfig, ReviewFinding, ReviewReport } from './types.js';
2
+ export interface ReviewEvalFindingExpectation {
3
+ ruleId: string;
4
+ file?: string;
5
+ severity?: ReviewFinding['severity'];
6
+ messageIncludes?: string;
7
+ rootCauseKind?: NonNullable<ReviewFinding['rootCause']>['kind'];
8
+ minCount?: number;
9
+ }
10
+ export interface ReviewEvalExpectations {
11
+ present?: ReviewEvalFindingExpectation[];
12
+ absent?: ReviewEvalFindingExpectation[];
13
+ maxFindings?: number;
14
+ maxErrors?: number;
15
+ maxWarnings?: number;
16
+ maxDurationMs?: number;
17
+ }
18
+ export interface ReviewEvalCaseConfig {
19
+ target?: string;
20
+ policy?: ReviewConfig['policy'];
21
+ crossStackMode?: ReviewConfig['crossStackMode'];
22
+ minConfidence?: number;
23
+ maxErrors?: number;
24
+ maxWarnings?: number;
25
+ disabledRules?: string[];
26
+ strict?: ReviewConfig['strict'];
27
+ strictParse?: boolean;
28
+ requireConfidenceAnnotations?: boolean;
29
+ }
30
+ export interface ReviewEvalCase {
31
+ name: string;
32
+ files: string[];
33
+ graph?: boolean;
34
+ maxDepth?: number;
35
+ config?: ReviewEvalCaseConfig;
36
+ expect: ReviewEvalExpectations;
37
+ }
38
+ export interface ReviewEvalManifest {
39
+ schemaVersion?: 1;
40
+ cases: ReviewEvalCase[];
41
+ }
42
+ export interface ReviewEvalCaseResult {
43
+ name: string;
44
+ passed: boolean;
45
+ files: string[];
46
+ findings: number;
47
+ errors: number;
48
+ warnings: number;
49
+ notes: number;
50
+ durationMs?: number;
51
+ failures: string[];
52
+ }
53
+ export interface ReviewEvalSummary {
54
+ passed: boolean;
55
+ cases: number;
56
+ passedCases: number;
57
+ failedCases: number;
58
+ failures: number;
59
+ results: ReviewEvalCaseResult[];
60
+ }
61
+ export interface ReviewEvalRunMetadata {
62
+ durationMs?: number;
63
+ }
64
+ export declare function normalizeReviewEvalManifest(value: unknown): ReviewEvalManifest;
65
+ export declare function evaluateReviewReports(testCase: ReviewEvalCase, reports: readonly ReviewReport[], metadata?: ReviewEvalRunMetadata): ReviewEvalCaseResult;
66
+ export declare function summarizeReviewEvalResults(results: readonly ReviewEvalCaseResult[]): ReviewEvalSummary;
67
+ export declare function formatReviewEvalSummary(summary: ReviewEvalSummary): string;
package/dist/eval.js ADDED
@@ -0,0 +1,177 @@
1
+ export function normalizeReviewEvalManifest(value) {
2
+ if (!isRecord(value))
3
+ throw new Error('eval manifest must be an object');
4
+ const rawCases = value.cases;
5
+ if (!Array.isArray(rawCases) || rawCases.length === 0) {
6
+ throw new Error('eval manifest must contain a non-empty cases array');
7
+ }
8
+ return {
9
+ schemaVersion: value.schemaVersion === undefined ? undefined : 1,
10
+ cases: rawCases.map((entry, index) => normalizeReviewEvalCase(entry, index)),
11
+ };
12
+ }
13
+ export function evaluateReviewReports(testCase, reports, metadata = {}) {
14
+ const findings = reports.flatMap((report) => report.findings);
15
+ const failures = [];
16
+ const expected = testCase.expect ?? {};
17
+ for (const expectation of expected.present ?? []) {
18
+ const count = countMatches(findings, expectation);
19
+ const minCount = expectation.minCount ?? 1;
20
+ if (count < minCount) {
21
+ failures.push(`expected ${describeExpectation(expectation)} at least ${minCount} time(s), found ${count}`);
22
+ }
23
+ }
24
+ for (const expectation of expected.absent ?? []) {
25
+ const count = countMatches(findings, expectation);
26
+ if (count > 0) {
27
+ failures.push(`expected no ${describeExpectation(expectation)}, found ${count}`);
28
+ }
29
+ }
30
+ if (expected.maxFindings !== undefined && findings.length > expected.maxFindings) {
31
+ failures.push(`expected at most ${expected.maxFindings} finding(s), found ${findings.length}`);
32
+ }
33
+ const errors = findings.filter((finding) => finding.severity === 'error').length;
34
+ const warnings = findings.filter((finding) => finding.severity === 'warning').length;
35
+ const notes = findings.filter((finding) => finding.severity === 'info').length;
36
+ if (expected.maxErrors !== undefined && errors > expected.maxErrors) {
37
+ failures.push(`expected at most ${expected.maxErrors} error(s), found ${errors}`);
38
+ }
39
+ if (expected.maxWarnings !== undefined && warnings > expected.maxWarnings) {
40
+ failures.push(`expected at most ${expected.maxWarnings} warning(s), found ${warnings}`);
41
+ }
42
+ if (expected.maxDurationMs !== undefined &&
43
+ metadata.durationMs !== undefined &&
44
+ metadata.durationMs > expected.maxDurationMs) {
45
+ failures.push(`expected duration <= ${expected.maxDurationMs}ms, observed ${metadata.durationMs}ms`);
46
+ }
47
+ return {
48
+ name: testCase.name,
49
+ passed: failures.length === 0,
50
+ files: testCase.files,
51
+ findings: findings.length,
52
+ errors,
53
+ warnings,
54
+ notes,
55
+ ...(metadata.durationMs !== undefined ? { durationMs: metadata.durationMs } : {}),
56
+ failures,
57
+ };
58
+ }
59
+ export function summarizeReviewEvalResults(results) {
60
+ const failedCases = results.filter((result) => !result.passed).length;
61
+ return {
62
+ passed: failedCases === 0,
63
+ cases: results.length,
64
+ passedCases: results.length - failedCases,
65
+ failedCases,
66
+ failures: results.reduce((sum, result) => sum + result.failures.length, 0),
67
+ results: [...results],
68
+ };
69
+ }
70
+ export function formatReviewEvalSummary(summary) {
71
+ const lines = [
72
+ 'KERN Review Eval',
73
+ '',
74
+ `Cases: ${summary.passedCases}/${summary.cases} passed`,
75
+ `Failures: ${summary.failures}`,
76
+ ];
77
+ for (const result of summary.results) {
78
+ const status = result.passed ? 'PASS' : 'FAIL';
79
+ const duration = result.durationMs !== undefined ? `, ${result.durationMs}ms` : '';
80
+ lines.push('', `${status} ${result.name} (${result.findings} findings${duration})`);
81
+ for (const failure of result.failures) {
82
+ lines.push(` - ${failure}`);
83
+ }
84
+ }
85
+ return lines.join('\n');
86
+ }
87
+ function normalizeReviewEvalCase(value, index) {
88
+ if (!isRecord(value))
89
+ throw new Error(`eval case ${index + 1} must be an object`);
90
+ if (typeof value.name !== 'string' || value.name.length === 0) {
91
+ throw new Error(`eval case ${index + 1} must declare a non-empty name`);
92
+ }
93
+ if (!Array.isArray(value.files) || value.files.some((file) => typeof file !== 'string')) {
94
+ throw new Error(`eval case '${value.name}' must declare a files string array`);
95
+ }
96
+ if (!isRecord(value.expect)) {
97
+ throw new Error(`eval case '${value.name}' must declare expect`);
98
+ }
99
+ return {
100
+ name: value.name,
101
+ files: value.files,
102
+ ...(value.graph !== undefined ? { graph: Boolean(value.graph) } : {}),
103
+ ...(typeof value.maxDepth === 'number' ? { maxDepth: value.maxDepth } : {}),
104
+ ...(isRecord(value.config) ? { config: value.config } : {}),
105
+ expect: normalizeExpectations(value.expect),
106
+ };
107
+ }
108
+ function normalizeExpectations(value) {
109
+ return {
110
+ ...(Array.isArray(value.present)
111
+ ? { present: value.present.map((entry) => normalizeFindingExpectation(entry)) }
112
+ : {}),
113
+ ...(Array.isArray(value.absent) ? { absent: value.absent.map((entry) => normalizeFindingExpectation(entry)) } : {}),
114
+ ...(typeof value.maxFindings === 'number' ? { maxFindings: value.maxFindings } : {}),
115
+ ...(typeof value.maxErrors === 'number' ? { maxErrors: value.maxErrors } : {}),
116
+ ...(typeof value.maxWarnings === 'number' ? { maxWarnings: value.maxWarnings } : {}),
117
+ ...(typeof value.maxDurationMs === 'number' ? { maxDurationMs: value.maxDurationMs } : {}),
118
+ };
119
+ }
120
+ function normalizeFindingExpectation(value) {
121
+ if (typeof value === 'string')
122
+ return { ruleId: value };
123
+ if (!isRecord(value) || typeof value.ruleId !== 'string') {
124
+ throw new Error('finding expectation must be a rule id string or object with ruleId');
125
+ }
126
+ return {
127
+ ruleId: value.ruleId,
128
+ ...(typeof value.file === 'string' ? { file: value.file } : {}),
129
+ ...(isSeverity(value.severity) ? { severity: value.severity } : {}),
130
+ ...(typeof value.messageIncludes === 'string' ? { messageIncludes: value.messageIncludes } : {}),
131
+ ...(typeof value.rootCauseKind === 'string'
132
+ ? { rootCauseKind: value.rootCauseKind }
133
+ : {}),
134
+ ...(typeof value.minCount === 'number' ? { minCount: value.minCount } : {}),
135
+ };
136
+ }
137
+ function countMatches(findings, expectation) {
138
+ return findings.filter((finding) => findingMatches(finding, expectation)).length;
139
+ }
140
+ function findingMatches(finding, expectation) {
141
+ if (finding.ruleId !== expectation.ruleId)
142
+ return false;
143
+ if (expectation.severity && finding.severity !== expectation.severity)
144
+ return false;
145
+ if (expectation.messageIncludes && !finding.message.includes(expectation.messageIncludes))
146
+ return false;
147
+ if (expectation.rootCauseKind && finding.rootCause?.kind !== expectation.rootCauseKind)
148
+ return false;
149
+ if (expectation.file && !pathMatches(finding.primarySpan.file, expectation.file))
150
+ return false;
151
+ return true;
152
+ }
153
+ function pathMatches(actual, expected) {
154
+ const normalizedActual = normalizePath(actual);
155
+ const normalizedExpected = normalizePath(expected);
156
+ return normalizedActual === normalizedExpected || normalizedActual.endsWith(`/${normalizedExpected}`);
157
+ }
158
+ function normalizePath(path) {
159
+ return path.replace(/\\/g, '/').replace(/^\/+/, '');
160
+ }
161
+ function describeExpectation(expectation) {
162
+ const parts = [expectation.ruleId];
163
+ if (expectation.file)
164
+ parts.push(`in ${expectation.file}`);
165
+ if (expectation.severity)
166
+ parts.push(`severity=${expectation.severity}`);
167
+ if (expectation.rootCauseKind)
168
+ parts.push(`rootCause=${expectation.rootCauseKind}`);
169
+ return parts.join(' ');
170
+ }
171
+ function isSeverity(value) {
172
+ return value === 'error' || value === 'warning' || value === 'info';
173
+ }
174
+ function isRecord(value) {
175
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
176
+ }
177
+ //# sourceMappingURL=eval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.js","sourceRoot":"","sources":["../src/eval.ts"],"names":[],"mappings":"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"}
@@ -155,37 +155,49 @@ function traceImportChain(targetPath, fileMap, entrySet) {
155
155
  export function buildFileContextMap(graph) {
156
156
  const contextMap = new Map();
157
157
  const fileMap = new Map();
158
- const entrySet = new Set(graph.entryFiles);
158
+ // Internal maps key by canonicalPath (red-team #9 / Option B). Cross-file
159
+ // edges (gf.imports / gf.importedBy / graph.entryFiles include canonical
160
+ // forms after the canonicalisation pass in graph.ts), and ts-morph paths
161
+ // round-trip through canonical, so canonical is the only stable key.
162
+ // Display paths are reported via gf.path on the value side, not the key.
163
+ const entrySet = new Set(graph.entryFiles.map((p) => graph.files.find((g) => g.path === p)?.canonicalPath ?? p));
159
164
  for (const gf of graph.files) {
160
- fileMap.set(gf.path, gf);
165
+ fileMap.set(gf.canonicalPath, gf);
161
166
  }
162
- // Classify entry points
167
+ // Classify entry points (key by canonicalPath so subsequent lookups match)
163
168
  const entryBoundaries = new Map();
164
169
  for (const entry of graph.entryFiles) {
165
- entryBoundaries.set(entry, classifyEntryPoint(entry));
170
+ const entryGf = graph.files.find((g) => g.path === entry);
171
+ const key = entryGf?.canonicalPath ?? entry;
172
+ // classifyEntryPoint reads suffix patterns (page.tsx, route.ts, etc.) —
173
+ // those are the same regardless of /var vs /private/var, so passing
174
+ // either form works. Use display so reads from disk hit the right file.
175
+ entryBoundaries.set(key, classifyEntryPoint(entry));
166
176
  }
167
177
  // Client boundary propagation cache
168
178
  const clientBoundaryCache = new Map();
169
179
  for (const gf of graph.files) {
170
- const isClient = isWithinClientBoundary(gf.path, fileMap, entrySet, clientBoundaryCache, new Set());
180
+ const isClient = isWithinClientBoundary(gf.canonicalPath, fileMap, entrySet, clientBoundaryCache, new Set());
181
+ // hasUseClientDirective reads from disk — pass display so symlinked
182
+ // paths work even on systems without realpath idempotence guarantees.
171
183
  const hasDirective = hasUseClientDirective(gf.path);
172
- const importChain = traceImportChain(gf.path, fileMap, entrySet);
184
+ const importChain = traceImportChain(gf.canonicalPath, fileMap, entrySet);
173
185
  // Determine boundary from import chain
174
186
  let boundary = 'unknown';
175
187
  if (isClient || hasDirective) {
176
188
  boundary = 'client';
177
189
  }
178
- else if (entrySet.has(gf.path)) {
179
- boundary = entryBoundaries.get(gf.path) || 'unknown';
190
+ else if (entrySet.has(gf.canonicalPath)) {
191
+ boundary = entryBoundaries.get(gf.canonicalPath) || 'unknown';
180
192
  }
181
193
  else {
182
194
  // Inherit boundary from entry points that import this file
183
195
  const entryBounds = new Set();
184
196
  for (const entry of graph.entryFiles) {
185
- // Check if this entry eventually imports this file
186
- const entryGf = fileMap.get(entry);
187
- if (entryGf && canReach(entry, gf.path, fileMap, new Set())) {
188
- entryBounds.add(entryBoundaries.get(entry) || 'unknown');
197
+ const entryGf = graph.files.find((g) => g.path === entry);
198
+ const entryKey = entryGf?.canonicalPath ?? entry;
199
+ if (entryGf && canReach(entryKey, gf.canonicalPath, fileMap, new Set())) {
200
+ entryBounds.add(entryBoundaries.get(entryKey) || 'unknown');
189
201
  }
190
202
  }
191
203
  if (entryBounds.size === 1) {
@@ -198,10 +210,17 @@ export function buildFileContextMap(graph) {
198
210
  // Find which entry points reach this file
199
211
  const reachableEntries = [];
200
212
  for (const entry of graph.entryFiles) {
201
- if (entry === gf.path || canReach(entry, gf.path, fileMap, new Set())) {
213
+ const entryGf = graph.files.find((g) => g.path === entry);
214
+ const entryKey = entryGf?.canonicalPath ?? entry;
215
+ if (entryKey === gf.canonicalPath || canReach(entryKey, gf.canonicalPath, fileMap, new Set())) {
216
+ // Push the DISPLAY entry path so consumers see what the user
217
+ // passed in, not the realpath-resolved form.
202
218
  reachableEntries.push(entry);
203
219
  }
204
220
  }
221
+ // contextMap keyed by gf.path (display) so callers querying by the
222
+ // path they passed in find the entry directly. Internal lookups
223
+ // (fileMap, traceImportChain) use canonicalPath — see above.
205
224
  contextMap.set(gf.path, {
206
225
  boundary,
207
226
  entryPoints: reachableEntries,