api-tests-coverage 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/dist/assets/_basePickBy-CYB1KXah.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-Bwm426M6.js +1 -0
- package/dist/dashboard/dist/assets/arc-B7p8x22e.js +1 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-wVr1_uNB.js +36 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-BBXc88fn.js +122 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-BsgzPfQ3.js +10 -0
- package/dist/dashboard/dist/assets/channel-psxgcQ_j.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-BF8loPLD.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-C3HNF-UF.js +1 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-wQ6TCEMq.js +165 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-B7xHuqZu.js +220 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-K3PC79JF.js +15 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-CmeZ1h1Z.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-Cyg7Km90.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-C8KNXDi7.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-AMwn99HP.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-AMwn99HP.js +1 -0
- package/dist/dashboard/dist/assets/clone-KEkbvJY9.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-YL9kFxCl.js +1 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-NZWnQN_Y.js +4 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-DGtyS7lD.js +24 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-CSCGZUfr.js +43 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-DdqZVGN1.js +24 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-Dhb_VQMS.js +60 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-gKUH-GJ2.js +162 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-Dm_lLo9y.js +267 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-DM9AW1aP.js +65 -0
- package/dist/dashboard/dist/assets/graph-Clj85F2M.js +1 -0
- package/dist/dashboard/dist/assets/index-CqEIqNus.js +781 -0
- package/dist/dashboard/dist/assets/index-xecKLQ58.css +1 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BMp4C5wf.js +2 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BC0GSZ7W.js +139 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-D6aRd_q1.js +89 -0
- package/dist/dashboard/dist/assets/layout-BbJNDkTr.js +1 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-B93XW27v.js +68 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-9G1tEuaq.js +30 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-jDtdB4Ws.js +7 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-WIJ0qiJG.js +64 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-Cb4WB9UB.js +10 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-BqGJWVUS.js +145 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-0Wd-KmOv.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-BlwaoFEG.js +1 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-CAmQOjBu.js +61 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CRP-WvE-.js +162 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-5DoR2_q5.js +7 -0
- package/dist/dashboard/dist/index.html +2 -2
- package/dist/src/config/defaultConfig.d.ts.map +1 -1
- package/dist/src/config/defaultConfig.js +37 -0
- package/dist/src/config/types.d.ts +42 -0
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/config/validateConfig.d.ts.map +1 -1
- package/dist/src/config/validateConfig.js +3 -0
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -1
- package/dist/src/discovery/fileClassifier.js +15 -16
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
- package/dist/src/discovery/projectDiscovery.js +4 -1
- package/dist/src/generation/ai-flow-exporter.d.ts +7 -0
- package/dist/src/generation/ai-flow-exporter.d.ts.map +1 -0
- package/dist/src/generation/ai-flow-exporter.js +260 -0
- package/dist/src/generation/context-builder.d.ts +16 -0
- package/dist/src/generation/context-builder.d.ts.map +1 -0
- package/dist/src/generation/context-builder.js +170 -0
- package/dist/src/generation/engine.d.ts +19 -0
- package/dist/src/generation/engine.d.ts.map +1 -0
- package/dist/src/generation/engine.js +204 -0
- package/dist/src/generation/file-router.d.ts +8 -0
- package/dist/src/generation/file-router.d.ts.map +1 -0
- package/dist/src/generation/file-router.js +98 -0
- package/dist/src/generation/gap-extractor.d.ts +7 -0
- package/dist/src/generation/gap-extractor.d.ts.map +1 -0
- package/dist/src/generation/gap-extractor.js +291 -0
- package/dist/src/generation/index.d.ts +9 -0
- package/dist/src/generation/index.d.ts.map +1 -0
- package/dist/src/generation/index.js +15 -0
- package/dist/src/generation/quality-scorer.d.ts +15 -0
- package/dist/src/generation/quality-scorer.d.ts.map +1 -0
- package/dist/src/generation/quality-scorer.js +273 -0
- package/dist/src/generation/template-renderer.d.ts +12 -0
- package/dist/src/generation/template-renderer.d.ts.map +1 -0
- package/dist/src/generation/template-renderer.js +546 -0
- package/dist/src/generation/types.d.ts +269 -0
- package/dist/src/generation/types.d.ts.map +1 -0
- package/dist/src/generation/types.js +6 -0
- package/dist/src/index.js +113 -0
- package/dist/src/languages/java/semanticBuilder.d.ts.map +1 -1
- package/dist/src/languages/java/semanticBuilder.js +69 -12
- package/dist/src/languages/javascript/angularDetector.d.ts.map +1 -1
- package/dist/src/languages/javascript/angularDetector.js +50 -17
- package/dist/src/languages/javascript/assertionResolver.js +6 -4
- package/dist/src/languages/javascript/hapiDetector.d.ts.map +1 -1
- package/dist/src/languages/javascript/hapiDetector.js +48 -5
- package/dist/src/languages/javascript/vueDetector.d.ts +2 -0
- package/dist/src/languages/javascript/vueDetector.d.ts.map +1 -1
- package/dist/src/languages/javascript/vueDetector.js +22 -0
- package/dist/src/languages/python/index.d.ts +1 -1
- package/dist/src/languages/python/index.d.ts.map +1 -1
- package/dist/src/languages/python/index.js +33 -3
- package/dist/src/pipeline/confidence.d.ts +6 -1
- package/dist/src/pipeline/confidence.d.ts.map +1 -1
- package/dist/src/pipeline/confidence.js +8 -3
- package/dist/src/pipeline/graph.d.ts.map +1 -1
- package/dist/src/pipeline/graph.js +16 -4
- package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/astStage.js +46 -2
- package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/baseUrlComposer.js +18 -4
- package/dist/src/pipeline/stages/ast/crossFileResolver.js +29 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/graphBuilder.js +81 -0
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts +3 -1
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.js +34 -14
- package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.js +22 -3
- package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.js +104 -28
- package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.js +56 -0
- package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.js +43 -18
- package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/rulesEnforcer.js +336 -45
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +2 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -1
- package/dist/src/pipeline/stages/merge/conflictDetector.js +54 -2
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -1
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +67 -3
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +8 -1
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +8 -4
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/testLayerClassifier.js +36 -10
- package/dist/src/pipeline/types.d.ts +1 -1
- package/dist/src/pipeline/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -66,8 +66,8 @@ function tryExtractAssertion(callNode) {
|
|
|
66
66
|
if (!subject)
|
|
67
67
|
return null;
|
|
68
68
|
const methodName = (_j = (_h = (_g = callee.property) === null || _g === void 0 ? void 0 : _g.name) === null || _h === void 0 ? void 0 : _h.toLowerCase()) !== null && _j !== void 0 ? _j : '';
|
|
69
|
-
const assertionType =
|
|
70
|
-
const
|
|
69
|
+
const { variable, assertionType: subjectType } = classifyExpectSubject(subject);
|
|
70
|
+
const assertionType = classifyAssertionMethod(methodName, subjectType);
|
|
71
71
|
return { assertionType, subjectVariable: variable, line: (_l = (_k = callNode.loc) === null || _k === void 0 ? void 0 : _k.start) === null || _l === void 0 ? void 0 : _l.line };
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -132,13 +132,15 @@ function classifyExpectSubject(subject) {
|
|
|
132
132
|
}
|
|
133
133
|
return { variable: undefined, assertionType: 'body-field' };
|
|
134
134
|
}
|
|
135
|
-
function classifyAssertionMethod(methodName) {
|
|
135
|
+
function classifyAssertionMethod(methodName, subjectType) {
|
|
136
136
|
if (methodName === 'tobe' ||
|
|
137
137
|
methodName === 'toequal' ||
|
|
138
138
|
methodName === 'tostrictequal' ||
|
|
139
139
|
methodName === 'tobetruthy' ||
|
|
140
140
|
methodName === 'tobefalsy') {
|
|
141
|
-
|
|
141
|
+
// These methods are ambiguous — use subject context to decide.
|
|
142
|
+
// Only classify as status-code when the subject involves status.
|
|
143
|
+
return subjectType === 'status-code' ? 'status-code' : 'body-field';
|
|
142
144
|
}
|
|
143
145
|
if (methodName === 'tohaveproperty' || methodName === 'tocontain' || methodName === 'tomatch') {
|
|
144
146
|
return 'body-field';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hapiDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/hapiDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,KAAK,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAkBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"hapiDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/hapiDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,KAAK,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAkBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CA6HlF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAmBtF"}
|
|
@@ -30,7 +30,7 @@ const BOOM_STATUS_MAP = {
|
|
|
30
30
|
* Detect HapiJS route definitions from source text.
|
|
31
31
|
*/
|
|
32
32
|
function detectHapiRoutes(sourceText, filePath) {
|
|
33
|
-
var _a, _b;
|
|
33
|
+
var _a, _b, _c, _d;
|
|
34
34
|
const routes = [];
|
|
35
35
|
const lines = sourceText.split('\n');
|
|
36
36
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -55,7 +55,7 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
55
55
|
if (authSimple) {
|
|
56
56
|
auth = { strategy: authSimple[1], mode: 'required' };
|
|
57
57
|
}
|
|
58
|
-
// auth: { mode: 'try' } or auth: { mode: 'optional' }
|
|
58
|
+
// auth: { mode: 'try' } or auth: { mode: 'optional' } — single-line case
|
|
59
59
|
const authMode = nearLine.match(/(?:auth|mode)\s*:\s*[{]?\s*mode\s*:\s*['"](\w+)['"]/);
|
|
60
60
|
if (authMode) {
|
|
61
61
|
auth = {
|
|
@@ -63,6 +63,19 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
63
63
|
mode: authMode[1] === 'try' || authMode[1] === 'optional' ? 'optional' : 'required',
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
+
else if (/auth\s*:\s*\{/.test(nearLine) || /auth\s*\{/.test(nearLine)) {
|
|
67
|
+
// Multi-line auth object: scan the next 5 lines for mode: 'try' / 'optional'
|
|
68
|
+
for (let m = j + 1; m < Math.min(j + 6, lines.length); m++) {
|
|
69
|
+
const modeMatch = lines[m].match(/mode\s*:\s*['"](\w+)['"]/);
|
|
70
|
+
if (modeMatch) {
|
|
71
|
+
auth = {
|
|
72
|
+
strategy: auth === null || auth === void 0 ? void 0 : auth.strategy,
|
|
73
|
+
mode: modeMatch[1] === 'try' || modeMatch[1] === 'optional' ? 'optional' : 'required',
|
|
74
|
+
};
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
66
79
|
// auth: false
|
|
67
80
|
if (/auth\s*:\s*false/.test(nearLine)) {
|
|
68
81
|
auth = undefined; // Public route
|
|
@@ -74,8 +87,9 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
74
87
|
// Scan validation block
|
|
75
88
|
for (let k = j; k < Math.min(j + 15, lines.length); k++) {
|
|
76
89
|
const valLine = lines[k];
|
|
77
|
-
// query: { ... }
|
|
90
|
+
// query: { ... } — extract Joi fields from same line and subsequent lines
|
|
78
91
|
if (/query\s*:/.test(valLine)) {
|
|
92
|
+
// Same-line extraction
|
|
79
93
|
const paramMatch = valLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
80
94
|
if (paramMatch) {
|
|
81
95
|
for (const pm of paramMatch) {
|
|
@@ -84,17 +98,46 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
84
98
|
queryParams.push(name);
|
|
85
99
|
}
|
|
86
100
|
}
|
|
101
|
+
// Multi-line extraction: scan subsequent lines for fieldName: Joi. patterns
|
|
102
|
+
for (let q = k + 1; q < Math.min(k + 16, lines.length); q++) {
|
|
103
|
+
const subLine = lines[q];
|
|
104
|
+
if (/}\s*[),]/.test(subLine) || /]\s*,/.test(subLine))
|
|
105
|
+
break;
|
|
106
|
+
const fieldMatch = subLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
107
|
+
if (fieldMatch) {
|
|
108
|
+
for (const fm of fieldMatch) {
|
|
109
|
+
const name = (_b = fm.match(/(\w+)\s*:/)) === null || _b === void 0 ? void 0 : _b[1];
|
|
110
|
+
if (name && !queryParams.includes(name))
|
|
111
|
+
queryParams.push(name);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
87
115
|
}
|
|
88
|
-
// payload: { ... }
|
|
116
|
+
// payload: { ... } — extract Joi fields from same line and subsequent lines
|
|
89
117
|
if (/payload\s*:/.test(valLine)) {
|
|
118
|
+
// Same-line extraction
|
|
90
119
|
const paramMatch = valLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
91
120
|
if (paramMatch) {
|
|
92
121
|
for (const pm of paramMatch) {
|
|
93
|
-
const name = (
|
|
122
|
+
const name = (_c = pm.match(/(\w+)\s*:/)) === null || _c === void 0 ? void 0 : _c[1];
|
|
94
123
|
if (name)
|
|
95
124
|
payloadParams.push(name);
|
|
96
125
|
}
|
|
97
126
|
}
|
|
127
|
+
// Multi-line extraction: scan subsequent lines for fieldName: Joi. patterns
|
|
128
|
+
for (let q = k + 1; q < Math.min(k + 16, lines.length); q++) {
|
|
129
|
+
const subLine = lines[q];
|
|
130
|
+
if (/}\s*[),]/.test(subLine) || /]\s*,/.test(subLine))
|
|
131
|
+
break;
|
|
132
|
+
const fieldMatch = subLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
133
|
+
if (fieldMatch) {
|
|
134
|
+
for (const fm of fieldMatch) {
|
|
135
|
+
const name = (_d = fm.match(/(\w+)\s*:/)) === null || _d === void 0 ? void 0 : _d[1];
|
|
136
|
+
if (name && !payloadParams.includes(name))
|
|
137
|
+
payloadParams.push(name);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
98
141
|
}
|
|
99
142
|
}
|
|
100
143
|
if (queryParams.length > 0 || payloadParams.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vueDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/vueDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAO/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"vueDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/vueDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAO/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAoEpF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAkBzF"}
|
|
@@ -29,7 +29,20 @@ function detectAxiosBaseUrl(sourceText, filePath) {
|
|
|
29
29
|
function detectVuexActions(sourceText, filePath) {
|
|
30
30
|
const calls = [];
|
|
31
31
|
const lines = sourceText.split('\n');
|
|
32
|
+
// Pass 1: Build a map of constant name → string value.
|
|
33
|
+
// Matches patterns like:
|
|
34
|
+
// const FETCH_ARTICLES = 'fetchArticles'
|
|
35
|
+
// export const FETCH_ARTICLES = "fetchArticles"
|
|
36
|
+
const constantMap = new Map();
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
const constMatch = line.match(/(?:export\s+)?const\s+([A-Z_][A-Z0-9_]*)\s*=\s*['"]([^'"]+)['"]/);
|
|
39
|
+
if (constMatch) {
|
|
40
|
+
constantMap.set(constMatch[1], constMatch[2]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Pass 2: Detect action definitions and their API calls.
|
|
32
44
|
let currentAction = '';
|
|
45
|
+
let currentResolvedName;
|
|
33
46
|
for (let i = 0; i < lines.length; i++) {
|
|
34
47
|
const line = lines[i];
|
|
35
48
|
// Vuex action definition: [ACTION_NAME]({ commit }, payload) {
|
|
@@ -37,6 +50,13 @@ function detectVuexActions(sourceText, filePath) {
|
|
|
37
50
|
const actionMatch = line.match(/(?:\[(\w+)\]|(\w+))\s*\(\s*\{\s*(?:commit|dispatch|state|getters|rootState)/);
|
|
38
51
|
if (actionMatch) {
|
|
39
52
|
currentAction = actionMatch[1] || actionMatch[2] || '';
|
|
53
|
+
// If using bracket syntax with a constant, resolve to its string value
|
|
54
|
+
if (actionMatch[1] && constantMap.has(actionMatch[1])) {
|
|
55
|
+
currentResolvedName = constantMap.get(actionMatch[1]);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
currentResolvedName = undefined;
|
|
59
|
+
}
|
|
40
60
|
continue;
|
|
41
61
|
}
|
|
42
62
|
// ApiService.get/post/put/delete('resource')
|
|
@@ -44,6 +64,7 @@ function detectVuexActions(sourceText, filePath) {
|
|
|
44
64
|
if (apiServiceMatch) {
|
|
45
65
|
calls.push({
|
|
46
66
|
actionName: currentAction || undefined,
|
|
67
|
+
resolvedName: currentResolvedName,
|
|
47
68
|
method: apiServiceMatch[1].toUpperCase(),
|
|
48
69
|
urlPattern: apiServiceMatch[2],
|
|
49
70
|
sourceFile: filePath,
|
|
@@ -56,6 +77,7 @@ function detectVuexActions(sourceText, filePath) {
|
|
|
56
77
|
if (axiosMatch) {
|
|
57
78
|
calls.push({
|
|
58
79
|
actionName: currentAction || undefined,
|
|
80
|
+
resolvedName: currentResolvedName,
|
|
59
81
|
method: axiosMatch[1].toUpperCase(),
|
|
60
82
|
urlPattern: axiosMatch[2],
|
|
61
83
|
sourceFile: filePath,
|
|
@@ -10,7 +10,7 @@ export declare class PythonAnalyzer implements LanguageAnalyzer {
|
|
|
10
10
|
extractHttpInteractions(model: SemanticModel, _ctx: AnalysisContext): ResolvedHttpInteraction[];
|
|
11
11
|
extractAssertions(model: SemanticModel): SemanticAssertion[];
|
|
12
12
|
extractBusinessRuleRefs(model: SemanticModel): BusinessRuleRef[];
|
|
13
|
-
extractFlowRefs(
|
|
13
|
+
extractFlowRefs(model: SemanticModel): FlowRef[];
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Classify JWT/auth security from decorator information.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/languages/python/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EAIb,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,eAAe,EAEf,aAAa,EAEb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/languages/python/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EAIb,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,eAAe,EAEf,aAAa,EAEb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAmC5B,qBAAa,cAAe,YAAW,gBAAgB;IACrD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAY;IAEhD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAc1D,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,GAAG,aAAa;IA4CtF,uBAAuB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,GAAG,uBAAuB,EAAE;IAwB/F,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,EAAE;IAG5D,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE;IAGhE,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,EAAE;CAqBjD;AA2WD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,sBAAsB,GAAG,SAAS,CAarG"}
|
|
@@ -8,6 +8,7 @@ exports.classifyFlaskSecurity = classifyFlaskSecurity;
|
|
|
8
8
|
const parserRegistry_1 = require("../../ast/parserRegistry");
|
|
9
9
|
const treeSitterUtils_1 = require("../shared/treeSitterUtils");
|
|
10
10
|
const resolvePaths_1 = require("../../coverage/deep-analysis/resolvePaths");
|
|
11
|
+
const testPatternDetector_1 = require("./testPatternDetector");
|
|
11
12
|
// ─── Parser ───────────────────────────────────────────────────────────────────
|
|
12
13
|
let cachedParser = undefined;
|
|
13
14
|
let parserLoaded = false;
|
|
@@ -45,7 +46,7 @@ class PythonAnalyzer {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
buildSemanticModel(parsed, _context) {
|
|
48
|
-
var _a, _b;
|
|
49
|
+
var _a, _b, _c, _d;
|
|
49
50
|
const root = (_b = (_a = parsed.ast) === null || _a === void 0 ? void 0 : _a.rootNode) !== null && _b !== void 0 ? _b : parsed.ast;
|
|
50
51
|
if (!root)
|
|
51
52
|
return emptyModel(parsed.filePath);
|
|
@@ -56,6 +57,20 @@ class PythonAnalyzer {
|
|
|
56
57
|
// Feature 27: Flask/FastAPI pattern detection
|
|
57
58
|
const decoratorStacks = extractFlaskDecoratorStacks(root, parsed.filePath);
|
|
58
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
|
+
}
|
|
59
74
|
return {
|
|
60
75
|
filePath: parsed.filePath,
|
|
61
76
|
language: 'python',
|
|
@@ -103,8 +118,23 @@ class PythonAnalyzer {
|
|
|
103
118
|
extractBusinessRuleRefs(model) {
|
|
104
119
|
return model.businessRuleRefs;
|
|
105
120
|
}
|
|
106
|
-
extractFlowRefs(
|
|
107
|
-
|
|
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;
|
|
108
138
|
}
|
|
109
139
|
}
|
|
110
140
|
exports.PythonAnalyzer = PythonAnalyzer;
|
|
@@ -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
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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"}
|
|
@@ -53,6 +53,14 @@ const graphBuilder_1 = require("./graphBuilder");
|
|
|
53
53
|
const fileClassifier_1 = require("../../../discovery/fileClassifier");
|
|
54
54
|
const projectDiscovery_1 = require("../../../discovery/projectDiscovery");
|
|
55
55
|
const parserRegistry_1 = require("../../../ast/parserRegistry");
|
|
56
|
+
const rulesEnforcer_1 = require("./rulesEnforcer");
|
|
57
|
+
const optionalAuthUnifier_1 = require("./optionalAuthUnifier");
|
|
58
|
+
const expressRouterResolver_1 = require("./resolvers/expressRouterResolver");
|
|
59
|
+
const flaskBlueprintResolver_1 = require("./resolvers/flaskBlueprintResolver");
|
|
60
|
+
const angularInjectionResolver_1 = require("./resolvers/angularInjectionResolver");
|
|
61
|
+
const vuexActionResolver_1 = require("./resolvers/vuexActionResolver");
|
|
62
|
+
const mybatisResolver_1 = require("./resolvers/mybatisResolver");
|
|
63
|
+
const dddLayerResolver_1 = require("./resolvers/dddLayerResolver");
|
|
56
64
|
const fs = __importStar(require("fs"));
|
|
57
65
|
const path = __importStar(require("path"));
|
|
58
66
|
class AstStage {
|
|
@@ -71,7 +79,8 @@ class AstStage {
|
|
|
71
79
|
const scaOutput = context.stageOutputs.get('sca');
|
|
72
80
|
// Build analysis context
|
|
73
81
|
const analysisContext = (0, astAnalysisOrchestrator_1.buildAnalysisContext)(context.config.astConfig);
|
|
74
|
-
// Discover all files
|
|
82
|
+
// Discover all files — XML, GraphQL, and PHP files are included in serviceFiles
|
|
83
|
+
// via SERVICE_CODE_EXTS in fileClassifier.ts (RULE-SA05, SA06)
|
|
75
84
|
const artifacts = (0, projectDiscovery_1.discoverProject)({ rootDir: context.projectRoot });
|
|
76
85
|
const allSourceFiles = [
|
|
77
86
|
...artifacts.testFiles,
|
|
@@ -131,8 +140,33 @@ class AstStage {
|
|
|
131
140
|
}
|
|
132
141
|
// Phase 2: Build cross-file symbol table
|
|
133
142
|
const crossFileTable = (0, crossFileResolver_1.buildCrossFileSymbolTable)(models, context.projectRoot);
|
|
134
|
-
// Phase 2b:
|
|
143
|
+
// Phase 2b: Register and run cross-file resolvers (Feature 27)
|
|
144
|
+
// Clear any stale registrations from prior runs, then register all 6 resolvers
|
|
145
|
+
(0, crossFileResolutionPass_1.clearCrossFileResolvers)();
|
|
146
|
+
(0, crossFileResolutionPass_1.registerCrossFileResolver)(new expressRouterResolver_1.ExpressRouterResolver());
|
|
147
|
+
(0, crossFileResolutionPass_1.registerCrossFileResolver)(new flaskBlueprintResolver_1.FlaskBlueprintResolver());
|
|
148
|
+
(0, crossFileResolutionPass_1.registerCrossFileResolver)(new angularInjectionResolver_1.AngularInjectionResolver());
|
|
149
|
+
(0, crossFileResolutionPass_1.registerCrossFileResolver)(new vuexActionResolver_1.VuexActionResolver());
|
|
150
|
+
(0, crossFileResolutionPass_1.registerCrossFileResolver)(new mybatisResolver_1.MyBatisResolver());
|
|
151
|
+
(0, crossFileResolutionPass_1.registerCrossFileResolver)(new dddLayerResolver_1.DddLayerResolver());
|
|
135
152
|
const crossFileResolutionResult = (0, crossFileResolutionPass_1.runCrossFileResolution)(crossFileTable, context.projectRoot, artifacts.apiFrameworks, allSourceFiles);
|
|
153
|
+
// Phase 2c: Detect auth coverage gaps (Feature 27)
|
|
154
|
+
// Collect all endpoints with their security classifications for gap analysis
|
|
155
|
+
const endpointsForAuthGaps = [];
|
|
156
|
+
for (const [filePath, model] of crossFileTable.models) {
|
|
157
|
+
if (!model.routeRegistrations)
|
|
158
|
+
continue;
|
|
159
|
+
for (const reg of model.routeRegistrations) {
|
|
160
|
+
endpointsForAuthGaps.push({
|
|
161
|
+
path: reg.path,
|
|
162
|
+
security: reg.security,
|
|
163
|
+
sourceFile: filePath,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const authCoverageGaps = (0, optionalAuthUnifier_1.detectAuthCoverageGaps)(endpointsForAuthGaps, new Set());
|
|
168
|
+
// Phase 2d: Enforce structure-agnostic rules (Feature 27)
|
|
169
|
+
const rulesResult = (0, rulesEnforcer_1.enforceStructureAgnosticRules)(crossFileTable, context.projectRoot);
|
|
136
170
|
// Phase 3: Run abstract layer traversal for test files
|
|
137
171
|
const traversalResults = new Map();
|
|
138
172
|
const depthCap = context.config.traversalDepthCap;
|
|
@@ -204,6 +238,10 @@ class AstStage {
|
|
|
204
238
|
graphEdgesAdded: edges.length,
|
|
205
239
|
crossFileResolversRun: crossFileResolutionResult.resolverResults.length,
|
|
206
240
|
crossFileEntriesAdded: crossFileResolutionResult.totalEntriesAdded,
|
|
241
|
+
rulesChecked: rulesResult.rulesChecked,
|
|
242
|
+
rulesPassed: rulesResult.rulesPassed,
|
|
243
|
+
ruleViolations: rulesResult.violations.length,
|
|
244
|
+
authCoverageGaps: authCoverageGaps.length,
|
|
207
245
|
},
|
|
208
246
|
};
|
|
209
247
|
context.diagnostics.set('ast', diagnostics);
|
|
@@ -238,7 +276,13 @@ function detectLanguage(filePath, scaOutput) {
|
|
|
238
276
|
'.kts': 'kotlin',
|
|
239
277
|
'.py': 'python',
|
|
240
278
|
'.rb': 'ruby',
|
|
279
|
+
'.php': 'php',
|
|
241
280
|
'.feature': 'cucumber',
|
|
242
281
|
};
|
|
282
|
+
// Handle GraphQL schema files and XML as special-case languages
|
|
283
|
+
if (ext === '.graphqls' || ext === '.graphql')
|
|
284
|
+
return 'graphql';
|
|
285
|
+
if (ext === '.xml')
|
|
286
|
+
return 'xml';
|
|
243
287
|
return extensionMap[ext];
|
|
244
288
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseUrlComposer.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/baseUrlComposer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,QAAQ,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"baseUrlComposer.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/baseUrlComposer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,QAAQ,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,CAyBtE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBlD;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IACL,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,GACA,MAAM,CAUR"}
|
|
@@ -24,10 +24,24 @@ exports.composeFrameworkUrl = composeFrameworkUrl;
|
|
|
24
24
|
*/
|
|
25
25
|
function composeUrl(...segments) {
|
|
26
26
|
const parts = segments
|
|
27
|
-
.filter((s) => typeof s === 'string' && s.length > 0)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
.filter((s) => typeof s === 'string' && s.length > 0);
|
|
28
|
+
if (parts.length === 0)
|
|
29
|
+
return '/';
|
|
30
|
+
// Detect protocol in the first segment (e.g. http://, https://)
|
|
31
|
+
const protocolMatch = parts[0].match(/^(\w+:\/\/)/);
|
|
32
|
+
let protocol = '';
|
|
33
|
+
if (protocolMatch) {
|
|
34
|
+
protocol = protocolMatch[1];
|
|
35
|
+
parts[0] = parts[0].slice(protocol.length);
|
|
36
|
+
}
|
|
37
|
+
const stripped = parts.map((s) => s.replace(/^\/+|\/+$/g, ''));
|
|
38
|
+
const joined = stripped.join('/');
|
|
39
|
+
const deduped = joined.replace(/\/+/g, '/');
|
|
40
|
+
if (protocol) {
|
|
41
|
+
const normalized = protocol + deduped;
|
|
42
|
+
return normalized.replace(/\/+$/, '') || protocol;
|
|
43
|
+
}
|
|
44
|
+
const normalized = '/' + deduped;
|
|
31
45
|
return normalized === '/' ? '/' : normalized.replace(/\/+$/, '');
|
|
32
46
|
}
|
|
33
47
|
/**
|
|
@@ -107,12 +107,31 @@ function resolveSymbolCrossFile(symbolName, fromFile, table) {
|
|
|
107
107
|
}
|
|
108
108
|
return undefined;
|
|
109
109
|
}
|
|
110
|
+
/** Built-in objects that should never be treated as import module names */
|
|
111
|
+
const BUILTIN_OBJECTS = new Set([
|
|
112
|
+
'console', 'math', 'json', 'object', 'array', 'promise', 'date',
|
|
113
|
+
'string', 'number', 'boolean', 'symbol', 'regexp', 'error', 'map',
|
|
114
|
+
'set', 'weakmap', 'weakset', 'proxy', 'reflect', 'intl',
|
|
115
|
+
'arraybuffer', 'sharedarraybuffer', 'atomics', 'dataview',
|
|
116
|
+
'this', 'self', 'super', 'window', 'document', 'process',
|
|
117
|
+
'global', 'globalthis', 'module', 'exports', 'require',
|
|
118
|
+
'__dirname', '__filename',
|
|
119
|
+
]);
|
|
110
120
|
/**
|
|
111
121
|
* Extract import declarations from a semantic model.
|
|
112
122
|
* This is a heuristic extraction — real import extraction would come from the AST.
|
|
113
123
|
*/
|
|
114
124
|
function extractImportsFromModel(filePath, model, projectRoot) {
|
|
115
125
|
const imports = [];
|
|
126
|
+
// Build a set of known identifiers from the model (constants and local variables)
|
|
127
|
+
// These are the names that could plausibly be import aliases
|
|
128
|
+
const knownIdentifiers = new Set();
|
|
129
|
+
for (const [name] of model.constants) {
|
|
130
|
+
knownIdentifiers.add(name);
|
|
131
|
+
}
|
|
132
|
+
for (const [name] of model.localVariables) {
|
|
133
|
+
knownIdentifiers.add(name);
|
|
134
|
+
}
|
|
116
135
|
// Use functions' calledFunctions to infer cross-file references
|
|
117
136
|
for (const [, func] of model.functions) {
|
|
118
137
|
for (const calledName of func.calledFunctions) {
|
|
@@ -120,6 +139,16 @@ function extractImportsFromModel(filePath, model, projectRoot) {
|
|
|
120
139
|
const dotIndex = calledName.indexOf('.');
|
|
121
140
|
if (dotIndex > 0) {
|
|
122
141
|
const modulePart = calledName.substring(0, dotIndex);
|
|
142
|
+
// Skip common built-in objects that are never import sources
|
|
143
|
+
if (BUILTIN_OBJECTS.has(modulePart.toLowerCase())) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
// Only treat as an import if the prefix matches a known identifier
|
|
147
|
+
// (constant or local variable) in this file, which is how import
|
|
148
|
+
// aliases typically appear in the semantic model
|
|
149
|
+
if (!knownIdentifiers.has(modulePart)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
123
152
|
const resolvedPath = (0, importResolver_1.resolveImportPath)(`./${modulePart}`, filePath, projectRoot, model.language);
|
|
124
153
|
if (resolvedPath) {
|
|
125
154
|
imports.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/graphBuilder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoB,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"graphBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/graphBuilder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoB,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAiB,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAIrE;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAClC,cAAc,EAAE,oBAAoB,EACpC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,GACnD;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CA4S5C"}
|