api-tests-coverage 1.0.15 → 1.0.17

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 (139) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-C2jmWITn.js +1 -0
  2. package/dist/dashboard/dist/assets/_baseUniq-DE6cyzJb.js +1 -0
  3. package/dist/dashboard/dist/assets/arc-B-Q4nGPT.js +1 -0
  4. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-C_5dqWCI.js +36 -0
  5. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-DbGIO6Kt.js +122 -0
  6. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-CAFpcejP.js +10 -0
  7. package/dist/dashboard/dist/assets/channel-Di9el3wE.js +1 -0
  8. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-DY1boKsq.js +1 -0
  9. package/dist/dashboard/dist/assets/chunk-55IACEB6-BSL35gyW.js +1 -0
  10. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-eTDXrKrv.js +165 -0
  11. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-M-8I3jEy.js +220 -0
  12. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-bSA0XiS0.js +15 -0
  13. package/dist/dashboard/dist/assets/chunk-QN33PNHL-BrOIYUBs.js +1 -0
  14. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CliaQGD4.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-CyhcxGB1.js +1 -0
  16. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-BkGN4Cpz.js +1 -0
  17. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-BkGN4Cpz.js +1 -0
  18. package/dist/dashboard/dist/assets/clone-Cvq8JuOb.js +1 -0
  19. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-BUkL7Wtq.js +1 -0
  20. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-B8oEROJc.js +4 -0
  21. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-5uki9Dw8.js +24 -0
  22. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-BRNhmby2.js +43 -0
  23. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-D-ku_X8U.js +24 -0
  24. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-DGl6gPe2.js +60 -0
  25. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Co89qYBD.js +162 -0
  26. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-2r3WpWQC.js +267 -0
  27. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-CuJ5l3TK.js +65 -0
  28. package/dist/dashboard/dist/assets/graph-ZtgwAPQj.js +1 -0
  29. package/dist/dashboard/dist/assets/index-D3sRJga7.js +777 -0
  30. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-ujnMqVz3.js +2 -0
  31. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DQzfeBIo.js +139 -0
  32. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-ueIaoeks.js +89 -0
  33. package/dist/dashboard/dist/assets/layout-B1fTYUMj.js +1 -0
  34. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-B7wYeLe1.js +68 -0
  35. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-Bf8vKEOf.js +30 -0
  36. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-CM8qiFLR.js +7 -0
  37. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-DPTtP4Ve.js +64 -0
  38. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DEVTdH0h.js +10 -0
  39. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-Bjr5wgXg.js +145 -0
  40. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DDrhZYly.js +1 -0
  41. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Im6pH8C-.js +1 -0
  42. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-DAT3r9va.js +61 -0
  43. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-BlA8rg0m.js +162 -0
  44. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-7aSkQtVu.js +7 -0
  45. package/dist/src/ast/astTypes.d.ts +86 -0
  46. package/dist/src/ast/astTypes.d.ts.map +1 -1
  47. package/dist/src/discovery/fileClassifier.d.ts.map +1 -1
  48. package/dist/src/discovery/fileClassifier.js +15 -16
  49. package/dist/src/discovery/frameworkDetector.d.ts +28 -0
  50. package/dist/src/discovery/frameworkDetector.d.ts.map +1 -0
  51. package/dist/src/discovery/frameworkDetector.js +189 -0
  52. package/dist/src/discovery/projectDiscovery.d.ts +5 -1
  53. package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
  54. package/dist/src/discovery/projectDiscovery.js +8 -1
  55. package/dist/src/inference/routeInference.d.ts.map +1 -1
  56. package/dist/src/inference/routeInference.js +224 -1
  57. package/dist/src/languages/java/graphqlSchemaParser.d.ts +65 -0
  58. package/dist/src/languages/java/graphqlSchemaParser.d.ts.map +1 -0
  59. package/dist/src/languages/java/graphqlSchemaParser.js +164 -0
  60. package/dist/src/languages/java/mybatisXmlParser.d.ts +52 -0
  61. package/dist/src/languages/java/mybatisXmlParser.d.ts.map +1 -0
  62. package/dist/src/languages/java/mybatisXmlParser.js +107 -0
  63. package/dist/src/languages/java/semanticBuilder.d.ts.map +1 -1
  64. package/dist/src/languages/java/semanticBuilder.js +69 -12
  65. package/dist/src/languages/javascript/angularDetector.d.ts +74 -0
  66. package/dist/src/languages/javascript/angularDetector.d.ts.map +1 -0
  67. package/dist/src/languages/javascript/angularDetector.js +227 -0
  68. package/dist/src/languages/javascript/assertionResolver.js +6 -4
  69. package/dist/src/languages/javascript/hapiDetector.d.ts +40 -0
  70. package/dist/src/languages/javascript/hapiDetector.d.ts.map +1 -0
  71. package/dist/src/languages/javascript/hapiDetector.js +174 -0
  72. package/dist/src/languages/javascript/mongooseDetector.d.ts +65 -0
  73. package/dist/src/languages/javascript/mongooseDetector.d.ts.map +1 -0
  74. package/dist/src/languages/javascript/mongooseDetector.js +237 -0
  75. package/dist/src/languages/javascript/vueDetector.d.ts +42 -0
  76. package/dist/src/languages/javascript/vueDetector.d.ts.map +1 -0
  77. package/dist/src/languages/javascript/vueDetector.js +109 -0
  78. package/dist/src/languages/python/index.d.ts +6 -2
  79. package/dist/src/languages/python/index.d.ts.map +1 -1
  80. package/dist/src/languages/python/index.js +200 -5
  81. package/dist/src/languages/python/testPatternDetector.d.ts +70 -0
  82. package/dist/src/languages/python/testPatternDetector.d.ts.map +1 -0
  83. package/dist/src/languages/python/testPatternDetector.js +201 -0
  84. package/dist/src/pipeline/confidence.d.ts +6 -1
  85. package/dist/src/pipeline/confidence.d.ts.map +1 -1
  86. package/dist/src/pipeline/confidence.js +8 -3
  87. package/dist/src/pipeline/graph.d.ts.map +1 -1
  88. package/dist/src/pipeline/graph.js +16 -4
  89. package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -1
  90. package/dist/src/pipeline/stages/ast/astStage.js +51 -1
  91. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts +44 -0
  92. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts.map +1 -0
  93. package/dist/src/pipeline/stages/ast/baseUrlComposer.js +97 -0
  94. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts +54 -0
  95. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts.map +1 -0
  96. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.js +88 -0
  97. package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -1
  98. package/dist/src/pipeline/stages/ast/crossFileResolver.js +39 -1
  99. package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -1
  100. package/dist/src/pipeline/stages/ast/graphBuilder.js +81 -0
  101. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts +41 -0
  102. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts.map +1 -0
  103. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.js +101 -0
  104. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts +18 -0
  105. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts.map +1 -0
  106. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.js +96 -0
  107. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts +46 -0
  108. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts.map +1 -0
  109. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.js +314 -0
  110. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts +17 -0
  111. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts.map +1 -0
  112. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.js +65 -0
  113. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts +17 -0
  114. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts.map +1 -0
  115. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.js +114 -0
  116. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts +27 -0
  117. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts.map +1 -0
  118. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.js +130 -0
  119. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts +17 -0
  120. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts.map +1 -0
  121. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.js +80 -0
  122. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts +24 -0
  123. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts.map +1 -0
  124. package/dist/src/pipeline/stages/ast/rulesEnforcer.js +411 -0
  125. package/dist/src/pipeline/stages/ast/types.d.ts +114 -1
  126. package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -1
  127. package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +2 -0
  128. package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -1
  129. package/dist/src/pipeline/stages/merge/conflictDetector.js +54 -2
  130. package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -1
  131. package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +67 -3
  132. package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -1
  133. package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +8 -1
  134. package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +8 -4
  135. package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
  136. package/dist/src/pipeline/stages/tia/testLayerClassifier.js +41 -10
  137. package/dist/src/pipeline/types.d.ts +1 -1
  138. package/dist/src/pipeline/types.d.ts.map +1 -1
  139. package/package.json +3 -3
@@ -4,9 +4,11 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PythonAnalyzer = void 0;
7
+ exports.classifyFlaskSecurity = classifyFlaskSecurity;
7
8
  const parserRegistry_1 = require("../../ast/parserRegistry");
8
9
  const treeSitterUtils_1 = require("../shared/treeSitterUtils");
9
10
  const resolvePaths_1 = require("../../coverage/deep-analysis/resolvePaths");
11
+ const testPatternDetector_1 = require("./testPatternDetector");
10
12
  // ─── Parser ───────────────────────────────────────────────────────────────────
11
13
  let cachedParser = undefined;
12
14
  let parserLoaded = false;
@@ -44,7 +46,7 @@ class PythonAnalyzer {
44
46
  }
45
47
  }
46
48
  buildSemanticModel(parsed, _context) {
47
- var _a, _b;
49
+ var _a, _b, _c, _d;
48
50
  const root = (_b = (_a = parsed.ast) === null || _a === void 0 ? void 0 : _a.rootNode) !== null && _b !== void 0 ? _b : parsed.ast;
49
51
  if (!root)
50
52
  return emptyModel(parsed.filePath);
@@ -52,6 +54,23 @@ class PythonAnalyzer {
52
54
  const functions = extractPythonFunctions(root, constants);
53
55
  const assertions = extractPythonAssertions(root);
54
56
  const businessRuleRefs = extractPythonBusinessRefs(root);
57
+ // Feature 27: Flask/FastAPI pattern detection
58
+ const decoratorStacks = extractFlaskDecoratorStacks(root, parsed.filePath);
59
+ const routeRegistrations = extractFlaskRouteRegistrations(root, parsed.filePath);
60
+ // Feature 27: webtest API call detection
61
+ const sourceText = (_d = (_c = root.text) !== null && _c !== void 0 ? _c : parsed.content) !== null && _d !== void 0 ? _d : '';
62
+ const webtestCalls = (0, testPatternDetector_1.detectWebtestCalls)(sourceText, parsed.filePath);
63
+ if (webtestCalls.length > 0) {
64
+ const httpCalls = (0, testPatternDetector_1.webtestCallsToHttpCalls)(webtestCalls);
65
+ // Merge webtest HTTP calls into the functions map under a synthetic entry
66
+ const webtestFunc = {
67
+ name: '__webtest_calls__',
68
+ parameters: [],
69
+ bodyHttpCalls: httpCalls,
70
+ calledFunctions: [],
71
+ };
72
+ functions.set('__webtest_calls__', webtestFunc);
73
+ }
55
74
  return {
56
75
  filePath: parsed.filePath,
57
76
  language: 'python',
@@ -63,6 +82,8 @@ class PythonAnalyzer {
63
82
  assertions,
64
83
  businessRuleRefs,
65
84
  flowRefs: [],
85
+ decoratorStacks,
86
+ routeRegistrations,
66
87
  };
67
88
  }
68
89
  extractHttpInteractions(model, _ctx) {
@@ -97,8 +118,23 @@ class PythonAnalyzer {
97
118
  extractBusinessRuleRefs(model) {
98
119
  return model.businessRuleRefs;
99
120
  }
100
- extractFlowRefs(_model) {
101
- return [];
121
+ extractFlowRefs(model) {
122
+ // Feature 27: Resolve pytest fixture chains
123
+ const refs = [...model.flowRefs];
124
+ // Use function parameter names to discover fixture dependencies
125
+ // In pytest, function parameters that aren't built-in fixtures are fixture references
126
+ const builtinFixtures = new Set([
127
+ 'request', 'tmp_path', 'tmpdir', 'capsys', 'capfd', 'monkeypatch',
128
+ 'pytestconfig', 'recwarn', 'caplog', 'cache', 'self',
129
+ ]);
130
+ for (const [funcName, func] of model.functions) {
131
+ for (const param of func.parameters) {
132
+ if (param && !builtinFixtures.has(param)) {
133
+ refs.push({ flowId: `fixture:${param}`, source: 'tag' });
134
+ }
135
+ }
136
+ }
137
+ return refs;
102
138
  }
103
139
  }
104
140
  exports.PythonAnalyzer = PythonAnalyzer;
@@ -179,10 +215,12 @@ function extractPythonHttpCalls(body, constants, out) {
179
215
  if (!funcNode)
180
216
  continue;
181
217
  const funcText = (_c = funcNode.text) !== null && _c !== void 0 ? _c : '';
182
- const match = funcText.match(/(?:requests|httpx|self\.client|client|self\.app|app)\.(get|post|put|patch|delete|head|options)/i);
218
+ const match = funcText.match(/(?:requests|httpx|self\.client|client|self\.app|app|testapp|self\.testapp)\.(get|post|post_json|put|put_json|patch|patch_json|delete|delete_json|head|options)/i);
183
219
  if (!match)
184
220
  continue;
185
221
  const [, methodName] = match;
222
+ // Normalize webtest methods: post_json → POST, put_json → PUT, etc.
223
+ const normalizedMethod = methodName.replace(/_json$/i, '').toUpperCase();
186
224
  const argList = (_e = (_d = call.childForFieldName) === null || _d === void 0 ? void 0 : _d.call(call, 'arguments')) !== null && _e !== void 0 ? _e : (0, treeSitterUtils_1.firstChildOfType)(call, 'argument_list');
187
225
  if (!argList)
188
226
  continue;
@@ -195,7 +233,7 @@ function extractPythonHttpCalls(body, constants, out) {
195
233
  // Resolve f-string variables against constants
196
234
  const resolvedPath = rawPath.includes('{') ? resolveFString(rawPath, constants) : rawPath;
197
235
  out.push({
198
- method: methodName.toUpperCase(),
236
+ method: normalizedMethod,
199
237
  rawPathArg: rawPath,
200
238
  resolvedPath,
201
239
  normalizedPath: resolvedPath.startsWith('/') ? (0, resolvePaths_1.normalizePathToTemplate)(resolvedPath) : undefined,
@@ -290,4 +328,161 @@ function emptyModel(filePath) {
290
328
  flowRefs: [],
291
329
  };
292
330
  }
331
+ // ─── Flask / FastAPI pattern extraction (Feature 27) ─────────────────────────
332
+ /**
333
+ * Extract decorator stacks for Flask/FastAPI route functions.
334
+ * Groups decorators by the function they decorate:
335
+ * @blueprint.route('/articles', methods=['GET'])
336
+ * @use_kwargs({...})
337
+ * @marshal_with(ArticleSchema)
338
+ * @jwt_required
339
+ * def get_articles():
340
+ * → DecoratorStack for 'get_articles' with 4 decorators
341
+ */
342
+ function extractFlaskDecoratorStacks(root, filePath) {
343
+ var _a, _b, _c, _d, _e, _f, _g;
344
+ const stacks = [];
345
+ const fnDefs = (0, treeSitterUtils_1.findNodes)(root, ['function_definition']);
346
+ for (const fn of fnDefs) {
347
+ const nameNode = (_b = (_a = fn.childForFieldName) === null || _a === void 0 ? void 0 : _a.call(fn, 'name')) !== null && _b !== void 0 ? _b : (0, treeSitterUtils_1.firstChildOfType)(fn, 'identifier');
348
+ const funcName = (_c = nameNode === null || nameNode === void 0 ? void 0 : nameNode.text) !== null && _c !== void 0 ? _c : '';
349
+ if (!funcName)
350
+ continue;
351
+ const decorators = [];
352
+ for (let i = 0; i < ((_d = fn === null || fn === void 0 ? void 0 : fn.childCount) !== null && _d !== void 0 ? _d : 0); i++) {
353
+ const child = fn.child(i);
354
+ if ((child === null || child === void 0 ? void 0 : child.type) !== 'decorator')
355
+ continue;
356
+ const decText = (_e = child.text) !== null && _e !== void 0 ? _e : '';
357
+ const dec = parseFlaskDecorator(decText, (_f = child.startPosition) === null || _f === void 0 ? void 0 : _f.row);
358
+ if (dec)
359
+ decorators.push(dec);
360
+ }
361
+ if (decorators.length > 0) {
362
+ stacks.push({
363
+ functionName: funcName,
364
+ decorators,
365
+ sourceFile: filePath,
366
+ line: ((_g = fn.startPosition) === null || _g === void 0 ? void 0 : _g.row) ? fn.startPosition.row + 1 : undefined,
367
+ });
368
+ }
369
+ }
370
+ return stacks;
371
+ }
372
+ /**
373
+ * Parse a single Flask/FastAPI decorator into a DecoratorInfo.
374
+ */
375
+ function parseFlaskDecorator(text, line) {
376
+ // @blueprint.route('/path', methods=['GET', 'POST'])
377
+ const routeMatch = text.match(/@(\w+)\.route\s*\(\s*['"]([^'"]+)['"]/);
378
+ if (routeMatch) {
379
+ const args = { path: routeMatch[2] };
380
+ const methodsMatch = text.match(/methods\s*=\s*\[([^\]]+)\]/);
381
+ if (methodsMatch)
382
+ args.methods = methodsMatch[1].replace(/['"]/g, '').trim();
383
+ return { name: 'route', fullText: text, args, line };
384
+ }
385
+ // @use_kwargs({...}) or @use_kwargs(SchemaClass)
386
+ const useKwargsMatch = text.match(/@use_kwargs\s*\((.+)\)/s);
387
+ if (useKwargsMatch) {
388
+ return { name: 'use_kwargs', fullText: text, args: { schema: useKwargsMatch[1].trim() }, line };
389
+ }
390
+ // @marshal_with(SchemaClass)
391
+ const marshalMatch = text.match(/@marshal_with\s*\(\s*(\w+)/);
392
+ if (marshalMatch) {
393
+ return { name: 'marshal_with', fullText: text, args: { schema: marshalMatch[1] }, line };
394
+ }
395
+ // @jwt_required, @jwt_required(), @jwt_required(optional=True), @jwt_optional
396
+ if (text.includes('@jwt_required') || text.includes('@jwt_optional')) {
397
+ const isOptional = text.includes('jwt_optional') || text.includes('optional=True') || text.includes('optional = True');
398
+ return {
399
+ name: isOptional ? 'jwt_optional' : 'jwt_required',
400
+ fullText: text,
401
+ args: { optional: isOptional ? 'true' : 'false' },
402
+ line,
403
+ };
404
+ }
405
+ // @login_required
406
+ if (text.includes('@login_required')) {
407
+ return { name: 'login_required', fullText: text, args: {}, line };
408
+ }
409
+ // Generic decorator
410
+ const genericMatch = text.match(/@(\w[\w.]*)/);
411
+ if (genericMatch) {
412
+ return { name: genericMatch[1], fullText: text, line };
413
+ }
414
+ return null;
415
+ }
416
+ /**
417
+ * Extract Flask Blueprint route registrations.
418
+ * Detects: Blueprint() constructors, register_blueprint() calls,
419
+ * @blueprint.route() registrations.
420
+ */
421
+ function extractFlaskRouteRegistrations(root, filePath) {
422
+ var _a, _b, _c;
423
+ const registrations = [];
424
+ // Use regex on the full source text since tree-sitter node traversal
425
+ // for call arguments is complex. This is a targeted pattern match.
426
+ const sourceText = (_a = root.text) !== null && _a !== void 0 ? _a : '';
427
+ const lines = sourceText.split('\n');
428
+ for (let i = 0; i < lines.length; i++) {
429
+ const line = lines[i];
430
+ // Blueprint constructor: bp = Blueprint('name', __name__, url_prefix='/api')
431
+ const bpMatch = line.match(/(\w+)\s*=\s*Blueprint\s*\(\s*['"](\w+)['"](?:[^)]*?url_prefix\s*=\s*['"]([^'"]+)['"])?\s*\)/);
432
+ if (bpMatch) {
433
+ registrations.push({
434
+ registrarName: bpMatch[1],
435
+ path: (_b = bpMatch[3]) !== null && _b !== void 0 ? _b : '',
436
+ sourceFile: filePath,
437
+ line: i + 1,
438
+ });
439
+ continue;
440
+ }
441
+ // register_blueprint(bp, url_prefix='/api/articles')
442
+ const regMatch = line.match(/register_blueprint\s*\(\s*(\w+)(?:\s*,\s*url_prefix\s*=\s*['"]([^'"]+)['"])?\s*\)/);
443
+ if (regMatch) {
444
+ registrations.push({
445
+ registrarName: regMatch[1],
446
+ path: (_c = regMatch[2]) !== null && _c !== void 0 ? _c : '',
447
+ targetModule: regMatch[1],
448
+ sourceFile: filePath,
449
+ line: i + 1,
450
+ });
451
+ continue;
452
+ }
453
+ // @blueprint.route('/path', methods=[...])
454
+ const routeMatch = line.match(/@(\w+)\.route\s*\(\s*['"]([^'"]+)['"]/);
455
+ if (routeMatch) {
456
+ const methodsMatch = line.match(/methods\s*=\s*\[([^\]]+)\]/);
457
+ const methods = methodsMatch
458
+ ? methodsMatch[1].replace(/['"]/g, '').split(',').map((m) => m.trim().toUpperCase())
459
+ : ['GET'];
460
+ registrations.push({
461
+ registrarName: routeMatch[1],
462
+ path: routeMatch[2],
463
+ methods,
464
+ sourceFile: filePath,
465
+ line: i + 1,
466
+ });
467
+ }
468
+ }
469
+ return registrations;
470
+ }
471
+ /**
472
+ * Classify JWT/auth security from decorator information.
473
+ */
474
+ function classifyFlaskSecurity(decorators) {
475
+ for (const dec of decorators) {
476
+ if (dec.name === 'jwt_required') {
477
+ return { type: 'jwt', required: true, optional: false, sourcePattern: '@jwt_required' };
478
+ }
479
+ if (dec.name === 'jwt_optional') {
480
+ return { type: 'jwt', required: false, optional: true, sourcePattern: '@jwt_optional' };
481
+ }
482
+ if (dec.name === 'login_required') {
483
+ return { type: 'session', required: true, optional: false, sourcePattern: '@login_required' };
484
+ }
485
+ }
486
+ return undefined;
487
+ }
293
488
  (0, parserRegistry_1.registerAnalyzer)('python', () => new PythonAnalyzer());
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Python test pattern detector (Feature 27, Sub-PR 4)
3
+ *
4
+ * Detects:
5
+ * 1. Factory Boy factories (factory.Factory subclasses, SubFactory, Meta.model)
6
+ * 2. webtest API calls (TestApp, testapp.get/post_json/put_json/delete)
7
+ * 3. pytest fixture chains (@pytest.fixture, fixture dependencies)
8
+ */
9
+ import type { SemanticHttpCall } from '../../ast/astTypes';
10
+ export interface FactoryBoyFactory {
11
+ /** The factory class name (e.g. 'ArticleFactory') */
12
+ className: string;
13
+ /** The model the factory creates (from Meta.model) */
14
+ modelName?: string;
15
+ /** SubFactory references */
16
+ subFactories: string[];
17
+ /** RelatedFactoryList references */
18
+ relatedFactoryLists: string[];
19
+ sourceFile: string;
20
+ line?: number;
21
+ }
22
+ /**
23
+ * Detect Factory Boy factory classes from source text.
24
+ * Looks for `class XFactory(factory.Factory):` and parses Meta.model, SubFactory, RelatedFactoryList.
25
+ */
26
+ export declare function detectFactoryBoyFactories(sourceText: string, filePath: string): FactoryBoyFactory[];
27
+ export interface WebtestApiCall {
28
+ method: string;
29
+ path: string;
30
+ sourceFile: string;
31
+ line?: number;
32
+ }
33
+ /**
34
+ * Detect webtest HTTP calls from source text.
35
+ * Looks for `testapp.get('/path')`, `testapp.post_json('/path', {...})`, etc.
36
+ */
37
+ export declare function detectWebtestCalls(sourceText: string, filePath: string): WebtestApiCall[];
38
+ /**
39
+ * Convert webtest API calls into SemanticHttpCall objects.
40
+ */
41
+ export declare function webtestCallsToHttpCalls(calls: WebtestApiCall[]): SemanticHttpCall[];
42
+ export interface PytestFixture {
43
+ /** Name of the fixture function */
44
+ name: string;
45
+ /** Scope: 'function' (default), 'class', 'module', 'session' */
46
+ scope: string;
47
+ /** Parameter names that are dependencies on other fixtures */
48
+ dependencies: string[];
49
+ sourceFile: string;
50
+ line?: number;
51
+ }
52
+ /**
53
+ * Detect @pytest.fixture functions and their parameter dependencies.
54
+ */
55
+ export declare function detectPytestFixtures(sourceText: string, filePath: string): PytestFixture[];
56
+ /**
57
+ * Detect if a file creates a webtest TestApp instance.
58
+ * Looks for `TestApp(app)` or `TestApp(...)`.
59
+ */
60
+ export declare function hasWebtestTestApp(sourceText: string): boolean;
61
+ /**
62
+ * Check if a file uses real DB fixtures (not mocked).
63
+ * Looks for pytest fixtures that reference db, database, session, engine, etc.
64
+ */
65
+ export declare function hasRealDbFixtures(sourceText: string): boolean;
66
+ /**
67
+ * Check if a file uses Factory Boy factories.
68
+ */
69
+ export declare function usesFactoryBoy(sourceText: string): boolean;
70
+ //# sourceMappingURL=testPatternDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testPatternDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/python/testPatternDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAmB,MAAM,oBAAoB,CAAC;AAK5E,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oCAAoC;IACpC,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAiEnG;AAID,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAiBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,EAAE,CA6BzF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,gBAAgB,EAAE,CAUnF;AAID,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAyC1F;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAO7D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE1D"}
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ /**
3
+ * Python test pattern detector (Feature 27, Sub-PR 4)
4
+ *
5
+ * Detects:
6
+ * 1. Factory Boy factories (factory.Factory subclasses, SubFactory, Meta.model)
7
+ * 2. webtest API calls (TestApp, testapp.get/post_json/put_json/delete)
8
+ * 3. pytest fixture chains (@pytest.fixture, fixture dependencies)
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.detectFactoryBoyFactories = detectFactoryBoyFactories;
12
+ exports.detectWebtestCalls = detectWebtestCalls;
13
+ exports.webtestCallsToHttpCalls = webtestCallsToHttpCalls;
14
+ exports.detectPytestFixtures = detectPytestFixtures;
15
+ exports.hasWebtestTestApp = hasWebtestTestApp;
16
+ exports.hasRealDbFixtures = hasRealDbFixtures;
17
+ exports.usesFactoryBoy = usesFactoryBoy;
18
+ /**
19
+ * Detect Factory Boy factory classes from source text.
20
+ * Looks for `class XFactory(factory.Factory):` and parses Meta.model, SubFactory, RelatedFactoryList.
21
+ */
22
+ function detectFactoryBoyFactories(sourceText, filePath) {
23
+ const factories = [];
24
+ const lines = sourceText.split('\n');
25
+ // Regex for class definition inheriting from factory.Factory or factory.DjangoModelFactory etc.
26
+ const classPattern = /^class\s+(\w+)\s*\(\s*(?:factory\.(?:Factory|DjangoModelFactory|SQLAlchemyModelFactory|MongoEngineFactory)|Factory)\s*\)/;
27
+ const metaModelPattern = /model\s*=\s*(\w+)/;
28
+ const subFactoryPattern = /=\s*(?:factory\.)?SubFactory\s*\(\s*['"]?(\w+)/g;
29
+ const relatedFactoryPattern = /=\s*(?:factory\.)?RelatedFactoryList\s*\(\s*['"]?(\w+)/g;
30
+ let currentFactory = null;
31
+ let classIndent = -1;
32
+ for (let i = 0; i < lines.length; i++) {
33
+ const line = lines[i];
34
+ const indent = line.search(/\S/);
35
+ // If we're inside a factory class, check for end of class
36
+ if (currentFactory && indent >= 0 && indent <= classIndent && line.trim().length > 0) {
37
+ factories.push(currentFactory);
38
+ currentFactory = null;
39
+ classIndent = -1;
40
+ }
41
+ const classMatch = line.match(classPattern);
42
+ if (classMatch) {
43
+ if (currentFactory)
44
+ factories.push(currentFactory);
45
+ currentFactory = {
46
+ className: classMatch[1],
47
+ subFactories: [],
48
+ relatedFactoryLists: [],
49
+ sourceFile: filePath,
50
+ line: i + 1,
51
+ };
52
+ classIndent = indent;
53
+ continue;
54
+ }
55
+ if (currentFactory) {
56
+ // Meta.model
57
+ const metaMatch = line.match(metaModelPattern);
58
+ if (metaMatch) {
59
+ currentFactory.modelName = metaMatch[1];
60
+ }
61
+ // SubFactory
62
+ let sfMatch;
63
+ subFactoryPattern.lastIndex = 0;
64
+ while ((sfMatch = subFactoryPattern.exec(line)) !== null) {
65
+ currentFactory.subFactories.push(sfMatch[1]);
66
+ }
67
+ // RelatedFactoryList
68
+ let rfMatch;
69
+ relatedFactoryPattern.lastIndex = 0;
70
+ while ((rfMatch = relatedFactoryPattern.exec(line)) !== null) {
71
+ currentFactory.relatedFactoryLists.push(rfMatch[1]);
72
+ }
73
+ }
74
+ }
75
+ // Push last factory if still open
76
+ if (currentFactory)
77
+ factories.push(currentFactory);
78
+ return factories;
79
+ }
80
+ /** Map webtest methods to HTTP methods */
81
+ const WEBTEST_METHOD_MAP = {
82
+ get: 'GET',
83
+ post: 'POST',
84
+ post_json: 'POST',
85
+ put: 'PUT',
86
+ put_json: 'PUT',
87
+ patch: 'PATCH',
88
+ patch_json: 'PATCH',
89
+ delete: 'DELETE',
90
+ delete_json: 'DELETE',
91
+ head: 'HEAD',
92
+ options: 'OPTIONS',
93
+ };
94
+ /**
95
+ * Detect webtest HTTP calls from source text.
96
+ * Looks for `testapp.get('/path')`, `testapp.post_json('/path', {...})`, etc.
97
+ */
98
+ function detectWebtestCalls(sourceText, filePath) {
99
+ const calls = [];
100
+ const lines = sourceText.split('\n');
101
+ // Pattern: any_var.METHOD('/path'...) where METHOD is a webtest HTTP method
102
+ const webtestMethods = Object.keys(WEBTEST_METHOD_MAP).join('|');
103
+ const callPattern = new RegExp(`\\w+\\.(${webtestMethods})\\s*\\(\\s*['"]([^'"]+)['"]`, 'g');
104
+ for (let i = 0; i < lines.length; i++) {
105
+ const line = lines[i];
106
+ callPattern.lastIndex = 0;
107
+ let match;
108
+ while ((match = callPattern.exec(line)) !== null) {
109
+ const method = WEBTEST_METHOD_MAP[match[1]];
110
+ if (method) {
111
+ calls.push({
112
+ method,
113
+ path: match[2],
114
+ sourceFile: filePath,
115
+ line: i + 1,
116
+ });
117
+ }
118
+ }
119
+ }
120
+ return calls;
121
+ }
122
+ /**
123
+ * Convert webtest API calls into SemanticHttpCall objects.
124
+ */
125
+ function webtestCallsToHttpCalls(calls) {
126
+ return calls.map((call) => ({
127
+ method: call.method,
128
+ rawPathArg: call.path,
129
+ resolvedPath: call.path,
130
+ normalizedPath: call.path.startsWith('/') ? call.path : undefined,
131
+ resolutionType: 'direct',
132
+ confidence: 'high',
133
+ line: call.line,
134
+ }));
135
+ }
136
+ /**
137
+ * Detect @pytest.fixture functions and their parameter dependencies.
138
+ */
139
+ function detectPytestFixtures(sourceText, filePath) {
140
+ const fixtures = [];
141
+ const lines = sourceText.split('\n');
142
+ for (let i = 0; i < lines.length; i++) {
143
+ const line = lines[i];
144
+ // @pytest.fixture or @pytest.fixture(scope='...')
145
+ const fixtureMatch = line.match(/@pytest\.fixture(?:\s*\(([^)]*)\))?/);
146
+ if (!fixtureMatch)
147
+ continue;
148
+ // Parse scope from decorator args
149
+ let scope = 'function';
150
+ if (fixtureMatch[1]) {
151
+ const scopeMatch = fixtureMatch[1].match(/scope\s*=\s*['"](\w+)['"]/);
152
+ if (scopeMatch)
153
+ scope = scopeMatch[1];
154
+ }
155
+ // Find the next def line (typically the line right after the decorator)
156
+ for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
157
+ const defMatch = lines[j].match(/def\s+(\w+)\s*\(([^)]*)\)/);
158
+ if (defMatch) {
159
+ const name = defMatch[1];
160
+ const params = defMatch[2]
161
+ .split(',')
162
+ .map((p) => p.trim().split(':')[0].split('=')[0].trim())
163
+ .filter((p) => p.length > 0 && p !== 'request');
164
+ fixtures.push({
165
+ name,
166
+ scope,
167
+ dependencies: params,
168
+ sourceFile: filePath,
169
+ line: j + 1,
170
+ });
171
+ break;
172
+ }
173
+ }
174
+ }
175
+ return fixtures;
176
+ }
177
+ // ─── TestApp Detection ──────────────────────────────────────────────────────
178
+ /**
179
+ * Detect if a file creates a webtest TestApp instance.
180
+ * Looks for `TestApp(app)` or `TestApp(...)`.
181
+ */
182
+ function hasWebtestTestApp(sourceText) {
183
+ return /TestApp\s*\(/.test(sourceText);
184
+ }
185
+ /**
186
+ * Check if a file uses real DB fixtures (not mocked).
187
+ * Looks for pytest fixtures that reference db, database, session, engine, etc.
188
+ */
189
+ function hasRealDbFixtures(sourceText) {
190
+ // Check for DB fixture names in function parameters
191
+ const dbFixturePattern = /def\s+\w+\s*\([^)]*\b(db|database|dbsession|db_session|engine|session|connection)\b/;
192
+ // Also check for common DB setup patterns
193
+ const dbSetupPattern = /(?:create_all|Base\.metadata|sessionmaker|create_engine|init_db|setup_database)/;
194
+ return dbFixturePattern.test(sourceText) || dbSetupPattern.test(sourceText);
195
+ }
196
+ /**
197
+ * Check if a file uses Factory Boy factories.
198
+ */
199
+ function usesFactoryBoy(sourceText) {
200
+ return /(?:import\s+factory|from\s+factory\s+import|factory\.Factory|factory\.SubFactory)/.test(sourceText);
201
+ }
@@ -65,6 +65,11 @@ export declare function hasPartialResolution(graph: CoverageKnowledgeGraph, path
65
65
  /**
66
66
  * Build a ConfidenceEvidence object from a coverage path in the graph.
67
67
  * Inspects the nodes and their source stages to determine what evidence exists.
68
+ *
69
+ * Optional `iastConfirmed` and `dastConfirmed` flags allow the caller to
70
+ * inject runtime confirmation state that may not be discoverable by walking
71
+ * `pathNodeIds` alone (e.g. when IAST/DAST nodes are not directly on the
72
+ * endpoint→test path in the graph).
68
73
  */
69
- export declare function buildConfidenceEvidence(graph: CoverageKnowledgeGraph, pathNodeIds: string[], isStaticOnlyMode: boolean): ConfidenceEvidence;
74
+ export declare function buildConfidenceEvidence(graph: CoverageKnowledgeGraph, pathNodeIds: string[], isStaticOnlyMode: boolean, iastConfirmed?: boolean, dastConfirmed?: boolean): ConfidenceEvidence;
70
75
  //# sourceMappingURL=confidence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"confidence.d.ts","sourceRoot":"","sources":["../../../src/pipeline/confidence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAa,MAAM,SAAS,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAYtD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAwBtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAoBpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAGlF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAIpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,kBAAkB,EACrB,CAAC,EAAE,kBAAkB,GACpB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,GAAG,kBAAkB,CAS9E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,EACrB,gBAAgB,EAAE,OAAO,GACxB,kBAAkB,CA6CpB"}
1
+ {"version":3,"file":"confidence.d.ts","sourceRoot":"","sources":["../../../src/pipeline/confidence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAa,MAAM,SAAS,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAYtD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAwBtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAoBpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAGlF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAIpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,kBAAkB,EACrB,CAAC,EAAE,kBAAkB,GACpB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,GAAG,kBAAkB,CAS9E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,EACrB,gBAAgB,EAAE,OAAO,EACzB,aAAa,CAAC,EAAE,OAAO,EACvB,aAAa,CAAC,EAAE,OAAO,GACtB,kBAAkB,CA6CpB"}
@@ -151,8 +151,13 @@ function hasPartialResolution(graph, pathNodeIds) {
151
151
  /**
152
152
  * Build a ConfidenceEvidence object from a coverage path in the graph.
153
153
  * Inspects the nodes and their source stages to determine what evidence exists.
154
+ *
155
+ * Optional `iastConfirmed` and `dastConfirmed` flags allow the caller to
156
+ * inject runtime confirmation state that may not be discoverable by walking
157
+ * `pathNodeIds` alone (e.g. when IAST/DAST nodes are not directly on the
158
+ * endpoint→test path in the graph).
154
159
  */
155
- function buildConfidenceEvidence(graph, pathNodeIds, isStaticOnlyMode) {
160
+ function buildConfidenceEvidence(graph, pathNodeIds, isStaticOnlyMode, iastConfirmed, dastConfirmed) {
156
161
  var _a, _b;
157
162
  const sourceStages = new Set();
158
163
  let hasIastConfirmation = false;
@@ -187,8 +192,8 @@ function buildConfidenceEvidence(graph, pathNodeIds, isStaticOnlyMode) {
187
192
  }
188
193
  return {
189
194
  sourceStages: Array.from(sourceStages),
190
- hasIastConfirmation,
191
- hasDastConfirmation,
195
+ hasIastConfirmation: hasIastConfirmation || (iastConfirmed !== null && iastConfirmed !== void 0 ? iastConfirmed : false),
196
+ hasDastConfirmation: hasDastConfirmation || (dastConfirmed !== null && dastConfirmed !== void 0 ? dastConfirmed : false),
192
197
  hasAssertionConfirmation,
193
198
  hasMockBoundary,
194
199
  hasPartialResolution: hasPartial,
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../../src/pipeline/graph.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACV,MAAM,SAAS,CAAC;AAEjB,qBAAa,sBAAsB;IACjC,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,KAAK,CAAqC;IAIlD,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAI9B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI1C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI5B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE;IAIhD,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE;IAI9C,WAAW,IAAI,SAAS,EAAE;IAI1B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAID,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAI9B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI1C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI5B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B,mDAAmD;IACnD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IAIzC,8CAA8C;IAC9C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IAIvC,sEAAsE;IACtE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAQ9D,wCAAwC;IACxC,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE;IAIhD,WAAW,IAAI,SAAS,EAAE;IAI1B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAID;;;OAGG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE;IA4B5E;;;OAGG;IACH,iBAAiB,CACf,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,aAAa,GACtB,OAAO;IAiCV;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,EAAE;IAwCpD,cAAc,IAAI;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,KAAK,EAAE,SAAS,EAAE,CAAA;KAAE;IAO5D,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,KAAK,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,sBAAsB;IAajG,KAAK,IAAI,IAAI;CAId"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../../src/pipeline/graph.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACV,MAAM,SAAS,CAAC;AAEjB,qBAAa,sBAAsB;IACjC,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,KAAK,CAAqC;IAIlD,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAI9B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI1C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI5B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAa/B,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE;IAIhD,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE;IAI9C,WAAW,IAAI,SAAS,EAAE;IAI1B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAID,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAI9B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI1C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI5B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B,mDAAmD;IACnD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IAIzC,8CAA8C;IAC9C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IAIvC,sEAAsE;IACtE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAQ9D,wCAAwC;IACxC,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE;IAIhD,WAAW,IAAI,SAAS,EAAE;IAI1B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAID;;;OAGG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE;IA4B5E;;;OAGG;IACH,iBAAiB,CACf,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,aAAa,GACtB,OAAO;IAmCV;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,EAAE;IAwCpD,cAAc,IAAI;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,KAAK,EAAE,SAAS,EAAE,CAAA;KAAE;IAO5D,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,KAAK,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,sBAAsB;IAajG,KAAK,IAAI,IAAI;CAId"}
@@ -23,7 +23,16 @@ class CoverageKnowledgeGraph {
23
23
  return this.nodes.has(id);
24
24
  }
25
25
  removeNode(id) {
26
- return this.nodes.delete(id);
26
+ const deleted = this.nodes.delete(id);
27
+ if (deleted) {
28
+ // Remove all edges that reference this node to avoid dangling edges
29
+ for (const [edgeId, edge] of this.edges) {
30
+ if (edge.sourceNodeId === id || edge.targetNodeId === id) {
31
+ this.edges.delete(edgeId);
32
+ }
33
+ }
34
+ }
35
+ return deleted;
27
36
  }
28
37
  getNodesByType(type) {
29
38
  return Array.from(this.nodes.values()).filter((n) => n.type === type);
@@ -112,9 +121,11 @@ class CoverageKnowledgeGraph {
112
121
  while (queue.length > 0) {
113
122
  const path = queue.shift();
114
123
  const currentId = path[path.length - 1];
115
- if (visited.has(currentId))
124
+ // Don't skip endId — we need to check every path that arrives at it
125
+ if (currentId !== endId && visited.has(currentId))
116
126
  continue;
117
- visited.add(currentId);
127
+ if (currentId !== endId)
128
+ visited.add(currentId);
118
129
  if (currentId === endId) {
119
130
  // Check interior nodes (not start or end)
120
131
  for (let i = 1; i < path.length - 1; i++) {
@@ -122,7 +133,8 @@ class CoverageKnowledgeGraph {
122
133
  if ((node === null || node === void 0 ? void 0 : node.type) === nodeType)
123
134
  return true;
124
135
  }
125
- return false;
136
+ // Don't return false here — other paths may still contain the node type
137
+ continue;
126
138
  }
127
139
  const outEdges = this.getEdgesFrom(currentId);
128
140
  for (const edge of outEdges) {
@@ -1 +1 @@
1
- {"version":3,"file":"astStage.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/astStage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAkB9C,qBAAa,QAAS,YAAW,aAAa,CAAC,cAAc,CAAC;IAC5D,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,SAAS;IAEpB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;CAyKjE"}
1
+ {"version":3,"file":"astStage.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/astStage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA2B9C,qBAAa,QAAS,YAAW,aAAa,CAAC,cAAc,CAAC;IAC5D,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,SAAS;IAEpB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;CAoNjE"}