api-tests-coverage 1.0.13 → 1.0.15
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/src/pipeline/confidence.d.ts +70 -0
- package/dist/src/pipeline/confidence.d.ts.map +1 -0
- package/dist/src/pipeline/confidence.js +198 -0
- package/dist/src/pipeline/graph.d.ts +58 -0
- package/dist/src/pipeline/graph.d.ts.map +1 -0
- package/dist/src/pipeline/graph.js +199 -0
- package/dist/src/pipeline/index.d.ts +24 -0
- package/dist/src/pipeline/index.d.ts.map +1 -0
- package/dist/src/pipeline/index.js +41 -0
- package/dist/src/pipeline/orchestrator.d.ts +42 -0
- package/dist/src/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/src/pipeline/orchestrator.js +115 -0
- package/dist/src/pipeline/stageInterface.d.ts +45 -0
- package/dist/src/pipeline/stageInterface.d.ts.map +1 -0
- package/dist/src/pipeline/stageInterface.js +17 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts +38 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.js +203 -0
- package/dist/src/pipeline/stages/ast/astStage.d.ts +19 -0
- package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/astStage.js +238 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts +23 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.js +183 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts +15 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.js +268 -0
- package/dist/src/pipeline/stages/ast/importResolver.d.ts +22 -0
- package/dist/src/pipeline/stages/ast/importResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/importResolver.js +186 -0
- package/dist/src/pipeline/stages/ast/types.d.ts +85 -0
- package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/types.js +5 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts +25 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.js +90 -0
- package/dist/src/pipeline/stages/dast/dastStage.d.ts +17 -0
- package/dist/src/pipeline/stages/dast/dastStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/dastStage.js +203 -0
- package/dist/src/pipeline/stages/dast/types.d.ts +49 -0
- package/dist/src/pipeline/stages/dast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/types.js +9 -0
- package/dist/src/pipeline/stages/iast/iastStage.d.ts +17 -0
- package/dist/src/pipeline/stages/iast/iastStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/iast/iastStage.js +191 -0
- package/dist/src/pipeline/stages/iast/types.d.ts +48 -0
- package/dist/src/pipeline/stages/iast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/iast/types.js +8 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +17 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.js +60 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts +15 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +141 -0
- package/dist/src/pipeline/stages/merge/mergeRules.d.ts +39 -0
- package/dist/src/pipeline/stages/merge/mergeRules.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/mergeRules.js +90 -0
- package/dist/src/pipeline/stages/merge/mergeStage.d.ts +20 -0
- package/dist/src/pipeline/stages/merge/mergeStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/mergeStage.js +145 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.d.ts +11 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.js +46 -0
- package/dist/src/pipeline/stages/sca/ciDetector.d.ts +15 -0
- package/dist/src/pipeline/stages/sca/ciDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/ciDetector.js +87 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts +31 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.js +296 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts +25 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.js +416 -0
- package/dist/src/pipeline/stages/sca/scaStage.d.ts +21 -0
- package/dist/src/pipeline/stages/sca/scaStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/scaStage.js +208 -0
- package/dist/src/pipeline/stages/sca/types.d.ts +61 -0
- package/dist/src/pipeline/stages/sca/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/types.js +9 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts +19 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +118 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts +20 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +238 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts +22 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.js +134 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts +16 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.js +191 -0
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts +20 -0
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/tiaStage.js +215 -0
- package/dist/src/pipeline/stages/tia/types.d.ts +52 -0
- package/dist/src/pipeline/stages/tia/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/types.js +5 -0
- package/dist/src/pipeline/types.d.ts +128 -0
- package/dist/src/pipeline/types.d.ts.map +1 -0
- package/dist/src/pipeline/types.js +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Graph builder — converts SemanticModels, cross-file results, and traversal
|
|
4
|
+
* results into GraphNode[] and GraphEdge[] for the Coverage Knowledge Graph.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.buildAstGraph = buildAstGraph;
|
|
41
|
+
const fileClassifier_1 = require("../../../discovery/fileClassifier");
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* Build graph nodes and edges from parsed semantic models and analysis results.
|
|
45
|
+
*/
|
|
46
|
+
function buildAstGraph(models, crossFileTable, traversalResults, interactions) {
|
|
47
|
+
const nodes = [];
|
|
48
|
+
const edges = [];
|
|
49
|
+
const addedNodeIds = new Set();
|
|
50
|
+
const addedEdgeIds = new Set();
|
|
51
|
+
function addNode(node) {
|
|
52
|
+
if (addedNodeIds.has(node.id))
|
|
53
|
+
return;
|
|
54
|
+
addedNodeIds.add(node.id);
|
|
55
|
+
nodes.push(node);
|
|
56
|
+
}
|
|
57
|
+
function addEdge(edge) {
|
|
58
|
+
if (addedEdgeIds.has(edge.id))
|
|
59
|
+
return;
|
|
60
|
+
addedEdgeIds.add(edge.id);
|
|
61
|
+
edges.push(edge);
|
|
62
|
+
}
|
|
63
|
+
// 1. Create file nodes for every parsed file
|
|
64
|
+
for (const [filePath, model] of models) {
|
|
65
|
+
const basename = path.basename(filePath);
|
|
66
|
+
const isTest = (0, fileClassifier_1.isTestFile)(basename);
|
|
67
|
+
const fileNodeType = isTest ? 'test-file' : 'file';
|
|
68
|
+
addNode({
|
|
69
|
+
id: `file:${filePath}`,
|
|
70
|
+
type: fileNodeType,
|
|
71
|
+
label: basename,
|
|
72
|
+
sourceStage: 'ast',
|
|
73
|
+
filePath,
|
|
74
|
+
metadata: { language: model.language, isTestFile: isTest },
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// 2. Create endpoint nodes from HTTP interactions
|
|
78
|
+
for (const [filePath, fileInteractions] of interactions) {
|
|
79
|
+
for (const interaction of fileInteractions) {
|
|
80
|
+
const endpointId = `endpoint:${interaction.method}:${interaction.path}`;
|
|
81
|
+
addNode({
|
|
82
|
+
id: endpointId,
|
|
83
|
+
type: 'endpoint',
|
|
84
|
+
label: `${interaction.method} ${interaction.path}`,
|
|
85
|
+
sourceStage: 'ast',
|
|
86
|
+
filePath: interaction.sourceFile,
|
|
87
|
+
metadata: {
|
|
88
|
+
method: interaction.method,
|
|
89
|
+
path: interaction.path,
|
|
90
|
+
normalizedPath: interaction.normalizedPath,
|
|
91
|
+
resolutionType: interaction.resolutionType,
|
|
92
|
+
confidence: interaction.confidence,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
// Create 'tests' edge from test file to endpoint
|
|
96
|
+
const basename = path.basename(filePath);
|
|
97
|
+
if ((0, fileClassifier_1.isTestFile)(basename)) {
|
|
98
|
+
addEdge({
|
|
99
|
+
id: `file:${filePath}->tests->${endpointId}`,
|
|
100
|
+
type: 'tests',
|
|
101
|
+
sourceNodeId: `file:${filePath}`,
|
|
102
|
+
targetNodeId: endpointId,
|
|
103
|
+
sourceStage: 'ast',
|
|
104
|
+
metadata: {
|
|
105
|
+
resolutionType: interaction.resolutionType,
|
|
106
|
+
confidence: interaction.confidence,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// Create 'asserts' edge if assertion-linked
|
|
111
|
+
if (interaction.assertionLinked && interaction.assertionType && interaction.assertionType !== 'none') {
|
|
112
|
+
addEdge({
|
|
113
|
+
id: `file:${filePath}->asserts->${endpointId}`,
|
|
114
|
+
type: 'asserts',
|
|
115
|
+
sourceNodeId: `file:${filePath}`,
|
|
116
|
+
targetNodeId: endpointId,
|
|
117
|
+
sourceStage: 'ast',
|
|
118
|
+
metadata: { assertionType: interaction.assertionType },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// 3. Create function/class nodes from semantic models
|
|
124
|
+
for (const [filePath, model] of models) {
|
|
125
|
+
for (const [funcName, func] of model.functions) {
|
|
126
|
+
const funcNodeId = `function:${filePath}:${funcName}`;
|
|
127
|
+
addNode({
|
|
128
|
+
id: funcNodeId,
|
|
129
|
+
type: 'function',
|
|
130
|
+
label: funcName,
|
|
131
|
+
sourceStage: 'ast',
|
|
132
|
+
filePath,
|
|
133
|
+
metadata: {
|
|
134
|
+
parameters: func.parameters,
|
|
135
|
+
annotations: func.annotations,
|
|
136
|
+
httpCallCount: func.bodyHttpCalls.length,
|
|
137
|
+
calledFunctions: func.calledFunctions,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
// Create 'calls' edges to other functions
|
|
141
|
+
for (const calledFunc of func.calledFunctions) {
|
|
142
|
+
// Try to find the called function in the same file or imported files
|
|
143
|
+
const targetFile = findFunctionFile(calledFunc, filePath, crossFileTable);
|
|
144
|
+
if (targetFile) {
|
|
145
|
+
const targetId = `function:${targetFile}:${calledFunc}`;
|
|
146
|
+
addEdge({
|
|
147
|
+
id: `${funcNodeId}->calls->${targetId}`,
|
|
148
|
+
type: 'calls',
|
|
149
|
+
sourceNodeId: funcNodeId,
|
|
150
|
+
targetNodeId: targetId,
|
|
151
|
+
sourceStage: 'ast',
|
|
152
|
+
metadata: {},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Create edges from function to endpoints it defines (for controller methods)
|
|
157
|
+
for (const httpCall of func.bodyHttpCalls) {
|
|
158
|
+
if (httpCall.resolvedPath) {
|
|
159
|
+
const endpointId = `endpoint:${httpCall.method}:${httpCall.resolvedPath}`;
|
|
160
|
+
if (addedNodeIds.has(endpointId)) {
|
|
161
|
+
addEdge({
|
|
162
|
+
id: `${funcNodeId}->defines->${endpointId}`,
|
|
163
|
+
type: 'defines',
|
|
164
|
+
sourceNodeId: funcNodeId,
|
|
165
|
+
targetNodeId: endpointId,
|
|
166
|
+
sourceStage: 'ast',
|
|
167
|
+
metadata: {},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// 4. Create class nodes from cross-file table
|
|
175
|
+
for (const [className, classDecl] of crossFileTable.classes) {
|
|
176
|
+
const classNodeId = `class:${classDecl.filePath}:${className}`;
|
|
177
|
+
addNode({
|
|
178
|
+
id: classNodeId,
|
|
179
|
+
type: 'class',
|
|
180
|
+
label: className,
|
|
181
|
+
sourceStage: 'ast',
|
|
182
|
+
filePath: classDecl.filePath,
|
|
183
|
+
line: classDecl.line,
|
|
184
|
+
metadata: {
|
|
185
|
+
methods: classDecl.methods,
|
|
186
|
+
extendsClass: classDecl.extendsClass,
|
|
187
|
+
implementsInterfaces: classDecl.implementsInterfaces,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
// Create 'extends' edge if there's a base class
|
|
191
|
+
if (classDecl.extendsClass) {
|
|
192
|
+
const baseDecl = crossFileTable.classes.get(classDecl.extendsClass);
|
|
193
|
+
if (baseDecl) {
|
|
194
|
+
const baseNodeId = `class:${baseDecl.filePath}:${classDecl.extendsClass}`;
|
|
195
|
+
addEdge({
|
|
196
|
+
id: `${classNodeId}->extends->${baseNodeId}`,
|
|
197
|
+
type: 'extends',
|
|
198
|
+
sourceNodeId: classNodeId,
|
|
199
|
+
targetNodeId: baseNodeId,
|
|
200
|
+
sourceStage: 'ast',
|
|
201
|
+
metadata: {},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// 5. Create assertion nodes from traversal results
|
|
207
|
+
for (const [filePath, result] of traversalResults) {
|
|
208
|
+
if (result.resolvedAssertions.length > 0) {
|
|
209
|
+
const assertionNodeId = `assertion:${filePath}`;
|
|
210
|
+
addNode({
|
|
211
|
+
id: assertionNodeId,
|
|
212
|
+
type: 'assertion',
|
|
213
|
+
label: `Assertions (${result.assertionSource})`,
|
|
214
|
+
sourceStage: 'ast',
|
|
215
|
+
filePath,
|
|
216
|
+
metadata: {
|
|
217
|
+
assertionSource: result.assertionSource,
|
|
218
|
+
assertionCount: result.resolvedAssertions.length,
|
|
219
|
+
traversalDepth: result.traversalDepth,
|
|
220
|
+
resolution: result.resolution,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// 6. Create import edges from the import graph
|
|
226
|
+
for (const [filePath, importedFiles] of crossFileTable.importGraph) {
|
|
227
|
+
const sourceId = `file:${filePath}`;
|
|
228
|
+
if (!addedNodeIds.has(sourceId))
|
|
229
|
+
continue;
|
|
230
|
+
for (const importedFile of importedFiles) {
|
|
231
|
+
const targetId = `file:${importedFile}`;
|
|
232
|
+
if (!addedNodeIds.has(targetId))
|
|
233
|
+
continue;
|
|
234
|
+
addEdge({
|
|
235
|
+
id: `${sourceId}->imports-helper->${targetId}`,
|
|
236
|
+
type: 'imports-helper',
|
|
237
|
+
sourceNodeId: sourceId,
|
|
238
|
+
targetNodeId: targetId,
|
|
239
|
+
sourceStage: 'ast',
|
|
240
|
+
metadata: {},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { nodes, edges };
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Find the file containing a function definition.
|
|
248
|
+
*/
|
|
249
|
+
function findFunctionFile(funcName, fromFile, table) {
|
|
250
|
+
var _a;
|
|
251
|
+
// Check current file first
|
|
252
|
+
const currentModel = table.models.get(fromFile);
|
|
253
|
+
if (currentModel === null || currentModel === void 0 ? void 0 : currentModel.functions.has(funcName))
|
|
254
|
+
return fromFile;
|
|
255
|
+
// Check imported files
|
|
256
|
+
const importedFiles = (_a = table.importGraph.get(fromFile)) !== null && _a !== void 0 ? _a : [];
|
|
257
|
+
for (const importedFile of importedFiles) {
|
|
258
|
+
const model = table.models.get(importedFile);
|
|
259
|
+
if (model === null || model === void 0 ? void 0 : model.functions.has(funcName))
|
|
260
|
+
return importedFile;
|
|
261
|
+
}
|
|
262
|
+
// Search all models as fallback
|
|
263
|
+
for (const [filePath, model] of table.models) {
|
|
264
|
+
if (model.functions.has(funcName))
|
|
265
|
+
return filePath;
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import resolver — resolves import paths to absolute file paths.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - TypeScript/JavaScript: relative paths, index files, .ts/.js extension resolution
|
|
6
|
+
* - Java: package-based imports to file paths
|
|
7
|
+
* - Kotlin: same as Java
|
|
8
|
+
* - Python: relative and absolute module imports
|
|
9
|
+
* - Ruby: require/require_relative
|
|
10
|
+
*/
|
|
11
|
+
import type { SupportedLanguage } from '../../../ast/astTypes';
|
|
12
|
+
/**
|
|
13
|
+
* Resolve an import source to an absolute file path.
|
|
14
|
+
*
|
|
15
|
+
* @param importSource - The import specifier (e.g. './utils', 'express', 'com.example.Service')
|
|
16
|
+
* @param fromFile - The absolute path of the file containing the import
|
|
17
|
+
* @param projectRoot - The project root directory
|
|
18
|
+
* @param language - The language of the importing file
|
|
19
|
+
* @returns The resolved absolute file path, or undefined if unresolvable
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveImportPath(importSource: string, fromFile: string, projectRoot: string, language: SupportedLanguage): string | undefined;
|
|
22
|
+
//# sourceMappingURL=importResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importResolver.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/importResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,iBAAiB,GAC1B,MAAM,GAAG,SAAS,CAepB"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Import resolver — resolves import paths to absolute file paths.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - TypeScript/JavaScript: relative paths, index files, .ts/.js extension resolution
|
|
7
|
+
* - Java: package-based imports to file paths
|
|
8
|
+
* - Kotlin: same as Java
|
|
9
|
+
* - Python: relative and absolute module imports
|
|
10
|
+
* - Ruby: require/require_relative
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.resolveImportPath = resolveImportPath;
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
/**
|
|
50
|
+
* Resolve an import source to an absolute file path.
|
|
51
|
+
*
|
|
52
|
+
* @param importSource - The import specifier (e.g. './utils', 'express', 'com.example.Service')
|
|
53
|
+
* @param fromFile - The absolute path of the file containing the import
|
|
54
|
+
* @param projectRoot - The project root directory
|
|
55
|
+
* @param language - The language of the importing file
|
|
56
|
+
* @returns The resolved absolute file path, or undefined if unresolvable
|
|
57
|
+
*/
|
|
58
|
+
function resolveImportPath(importSource, fromFile, projectRoot, language) {
|
|
59
|
+
switch (language) {
|
|
60
|
+
case 'typescript':
|
|
61
|
+
case 'javascript':
|
|
62
|
+
return resolveJsTsImport(importSource, fromFile, projectRoot);
|
|
63
|
+
case 'java':
|
|
64
|
+
case 'kotlin':
|
|
65
|
+
return resolveJavaKotlinImport(importSource, fromFile, projectRoot, language);
|
|
66
|
+
case 'python':
|
|
67
|
+
return resolvePythonImport(importSource, fromFile, projectRoot);
|
|
68
|
+
case 'ruby':
|
|
69
|
+
return resolveRubyImport(importSource, fromFile, projectRoot);
|
|
70
|
+
default:
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ─── JS/TS Import Resolution ─────────────────────────────────────────────────
|
|
75
|
+
const JS_TS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
76
|
+
function resolveJsTsImport(importSource, fromFile, projectRoot) {
|
|
77
|
+
// Skip bare module specifiers (node_modules packages)
|
|
78
|
+
if (!importSource.startsWith('.') && !importSource.startsWith('/')) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const fromDir = path.dirname(fromFile);
|
|
82
|
+
const basePath = path.resolve(fromDir, importSource);
|
|
83
|
+
// Try exact match first
|
|
84
|
+
if (existsAsFile(basePath))
|
|
85
|
+
return basePath;
|
|
86
|
+
// Try with extensions
|
|
87
|
+
for (const ext of JS_TS_EXTENSIONS) {
|
|
88
|
+
const withExt = basePath + ext;
|
|
89
|
+
if (existsAsFile(withExt))
|
|
90
|
+
return withExt;
|
|
91
|
+
}
|
|
92
|
+
// Try as directory with index file
|
|
93
|
+
for (const ext of JS_TS_EXTENSIONS) {
|
|
94
|
+
const indexFile = path.join(basePath, `index${ext}`);
|
|
95
|
+
if (existsAsFile(indexFile))
|
|
96
|
+
return indexFile;
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
// ─── Java/Kotlin Import Resolution ───────────────────────────────────────────
|
|
101
|
+
function resolveJavaKotlinImport(importSource, _fromFile, projectRoot, language) {
|
|
102
|
+
// Java import: com.example.service.UserService → src/main/java/com/example/service/UserService.java
|
|
103
|
+
// Handle wildcard imports: com.example.service.* → directory
|
|
104
|
+
const cleanImport = importSource.replace(/\.\*$/, '');
|
|
105
|
+
const pathParts = cleanImport.split('.');
|
|
106
|
+
const extensions = language === 'kotlin' ? ['.kt', '.kts', '.java'] : ['.java', '.kt'];
|
|
107
|
+
const sourceDirs = ['src/main/java', 'src/main/kotlin', 'src/test/java', 'src/test/kotlin', 'src'];
|
|
108
|
+
for (const sourceDir of sourceDirs) {
|
|
109
|
+
for (const ext of extensions) {
|
|
110
|
+
const filePath = path.join(projectRoot, sourceDir, ...pathParts) + ext;
|
|
111
|
+
if (existsAsFile(filePath))
|
|
112
|
+
return filePath;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
// ─── Python Import Resolution ────────────────────────────────────────────────
|
|
118
|
+
function resolvePythonImport(importSource, fromFile, projectRoot) {
|
|
119
|
+
// Relative import: .utils or ..models
|
|
120
|
+
const dotMatch = /^(\.+)(.*)$/.exec(importSource);
|
|
121
|
+
if (dotMatch) {
|
|
122
|
+
const dots = dotMatch[1].length;
|
|
123
|
+
const rest = dotMatch[2];
|
|
124
|
+
let baseDir = path.dirname(fromFile);
|
|
125
|
+
for (let i = 1; i < dots; i++) {
|
|
126
|
+
baseDir = path.dirname(baseDir);
|
|
127
|
+
}
|
|
128
|
+
const parts = rest ? rest.split('.') : [];
|
|
129
|
+
return resolvePythonPath(path.join(baseDir, ...parts));
|
|
130
|
+
}
|
|
131
|
+
// Absolute import: package.module
|
|
132
|
+
const parts = importSource.split('.');
|
|
133
|
+
// Try from project root and common source directories
|
|
134
|
+
const searchDirs = [projectRoot, path.join(projectRoot, 'src'), path.join(projectRoot, 'tests')];
|
|
135
|
+
for (const dir of searchDirs) {
|
|
136
|
+
const result = resolvePythonPath(path.join(dir, ...parts));
|
|
137
|
+
if (result)
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
function resolvePythonPath(basePath) {
|
|
143
|
+
// Try as .py file
|
|
144
|
+
const asFile = basePath + '.py';
|
|
145
|
+
if (existsAsFile(asFile))
|
|
146
|
+
return asFile;
|
|
147
|
+
// Try as package (__init__.py)
|
|
148
|
+
const asPackage = path.join(basePath, '__init__.py');
|
|
149
|
+
if (existsAsFile(asPackage))
|
|
150
|
+
return asPackage;
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
// ─── Ruby Import Resolution ─────────────────────────────────────────────────
|
|
154
|
+
function resolveRubyImport(importSource, fromFile, projectRoot) {
|
|
155
|
+
// require_relative './utils' → relative to current file
|
|
156
|
+
// require 'app/models/user' → relative to project root / lib
|
|
157
|
+
if (importSource.startsWith('.')) {
|
|
158
|
+
const fromDir = path.dirname(fromFile);
|
|
159
|
+
const basePath = path.resolve(fromDir, importSource);
|
|
160
|
+
return tryRubyExtensions(basePath);
|
|
161
|
+
}
|
|
162
|
+
const searchDirs = [projectRoot, path.join(projectRoot, 'lib'), path.join(projectRoot, 'app')];
|
|
163
|
+
for (const dir of searchDirs) {
|
|
164
|
+
const result = tryRubyExtensions(path.join(dir, importSource));
|
|
165
|
+
if (result)
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
function tryRubyExtensions(basePath) {
|
|
171
|
+
if (existsAsFile(basePath))
|
|
172
|
+
return basePath;
|
|
173
|
+
const withRb = basePath + '.rb';
|
|
174
|
+
if (existsAsFile(withRb))
|
|
175
|
+
return withRb;
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
179
|
+
function existsAsFile(filePath) {
|
|
180
|
+
try {
|
|
181
|
+
return fs.statSync(filePath).isFile();
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST stage types — cross-file analysis, import resolution, and abstract layer traversal.
|
|
3
|
+
*/
|
|
4
|
+
import type { SemanticModel, SemanticHttpCall, SemanticAssertion } from '../../../ast/astTypes';
|
|
5
|
+
import type { AssertionSource } from '../../types';
|
|
6
|
+
/**
|
|
7
|
+
* An import declaration extracted from a source file.
|
|
8
|
+
*/
|
|
9
|
+
export interface ImportDeclaration {
|
|
10
|
+
/** The import source specifier (e.g. './utils', 'express', '@nestjs/common') */
|
|
11
|
+
source: string;
|
|
12
|
+
/** The resolved absolute file path (undefined if unresolvable) */
|
|
13
|
+
resolvedPath?: string;
|
|
14
|
+
/** Names imported (e.g. ['Router', 'Response'] or ['default']) */
|
|
15
|
+
importedNames: string[];
|
|
16
|
+
/** Whether this is a default import */
|
|
17
|
+
isDefault: boolean;
|
|
18
|
+
/** Line number in source file */
|
|
19
|
+
line?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A class declaration extracted from a source file.
|
|
23
|
+
*/
|
|
24
|
+
export interface ClassDeclaration {
|
|
25
|
+
name: string;
|
|
26
|
+
extendsClass?: string;
|
|
27
|
+
implementsInterfaces?: string[];
|
|
28
|
+
methods: string[];
|
|
29
|
+
filePath: string;
|
|
30
|
+
line?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Multi-file symbol table aggregating all parsed file models.
|
|
34
|
+
*/
|
|
35
|
+
export interface CrossFileSymbolTable {
|
|
36
|
+
/** All parsed semantic models keyed by file path */
|
|
37
|
+
models: Map<string, SemanticModel>;
|
|
38
|
+
/** Exported symbols: symbolName → { filePath, value } */
|
|
39
|
+
exportedSymbols: Map<string, {
|
|
40
|
+
filePath: string;
|
|
41
|
+
value: string;
|
|
42
|
+
}>;
|
|
43
|
+
/** All classes: className → declaration */
|
|
44
|
+
classes: Map<string, ClassDeclaration>;
|
|
45
|
+
/** Import graph: filePath → array of resolved import file paths */
|
|
46
|
+
importGraph: Map<string, string[]>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Result of abstract layer traversal (inheritance, helpers, fixtures).
|
|
50
|
+
*/
|
|
51
|
+
export interface TraversalResult {
|
|
52
|
+
/** Assertions resolved from base classes, helpers, or fixtures */
|
|
53
|
+
resolvedAssertions: SemanticAssertion[];
|
|
54
|
+
/** HTTP calls resolved from abstract layers */
|
|
55
|
+
resolvedHttpCalls: SemanticHttpCall[];
|
|
56
|
+
/** How the assertion was sourced */
|
|
57
|
+
assertionSource: AssertionSource;
|
|
58
|
+
/** How deep the traversal went */
|
|
59
|
+
traversalDepth: number;
|
|
60
|
+
/** Whether resolution is full or partial */
|
|
61
|
+
resolution: 'full' | 'partial';
|
|
62
|
+
/** Chain of unresolved references (for diagnostics) */
|
|
63
|
+
unresolvedChain?: string[];
|
|
64
|
+
/** Whether a cycle was detected during traversal */
|
|
65
|
+
cycleDetected: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Output of the AST pipeline stage.
|
|
69
|
+
*/
|
|
70
|
+
export interface AstStageOutput {
|
|
71
|
+
/** All parsed semantic models */
|
|
72
|
+
models: Map<string, SemanticModel>;
|
|
73
|
+
/** Cross-file symbol table */
|
|
74
|
+
crossFileTable: CrossFileSymbolTable;
|
|
75
|
+
/** Traversal results per test file */
|
|
76
|
+
traversalResults: Map<string, TraversalResult>;
|
|
77
|
+
/** Files that were successfully analyzed */
|
|
78
|
+
analyzedFiles: string[];
|
|
79
|
+
/** Files that failed or were skipped */
|
|
80
|
+
skippedFiles: Array<{
|
|
81
|
+
file: string;
|
|
82
|
+
reason: string;
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAuC,MAAM,uBAAuB,CAAC;AACrI,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,aAAa,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnC,yDAAyD;IACzD,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,2CAA2C;IAC3C,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACvC,mEAAmE;IACnE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC,+CAA+C;IAC/C,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,oCAAoC;IACpC,eAAe,EAAE,eAAe,CAAC;IACjC,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnC,8BAA8B;IAC9B,cAAc,EAAE,oBAAoB,CAAC;IACrC,sCAAsC;IACtC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,4CAA4C;IAC5C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,wCAAwC;IACxC,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAST conflict emitter — detects conflicts between DAST probe results and
|
|
3
|
+
* AST-declared endpoints.
|
|
4
|
+
*
|
|
5
|
+
* Conflict rules from spec §7:
|
|
6
|
+
*
|
|
7
|
+
* | DAST Result | AST Declared | Conflict Type |
|
|
8
|
+
* |--------------------------|-----------------------|------------------------------------|
|
|
9
|
+
* | Not reachable | Endpoint exists | dast-unreachable |
|
|
10
|
+
* | Reachable, no auth check | @Secured present | security-annotation-not-enforced |
|
|
11
|
+
* | Returns 500 | No error handler | unhandled-server-error |
|
|
12
|
+
* | New endpoint found | Not in AST | undeclared-endpoint |
|
|
13
|
+
*/
|
|
14
|
+
import type { ConflictNode } from '../../types';
|
|
15
|
+
import type { CoverageKnowledgeGraph } from '../../graph';
|
|
16
|
+
import type { DastProbeResult } from './types';
|
|
17
|
+
/**
|
|
18
|
+
* Emit conflicts by comparing DAST results with the AST-declared graph.
|
|
19
|
+
*
|
|
20
|
+
* @param results - DAST probe results
|
|
21
|
+
* @param graph - Current coverage knowledge graph
|
|
22
|
+
* @returns Array of conflict nodes to be added to the graph
|
|
23
|
+
*/
|
|
24
|
+
export declare function emitDastConflicts(results: DastProbeResult[], graph: CoverageKnowledgeGraph): ConflictNode[];
|
|
25
|
+
//# sourceMappingURL=conflictEmitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflictEmitter.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/dast/conflictEmitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAkC,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,eAAe,EAAE,EAC1B,KAAK,EAAE,sBAAsB,GAC5B,YAAY,EAAE,CA0EhB"}
|