@securecode-ai/mcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/api/client.d.ts +4 -0
- package/dist/api/client.js +49 -0
- package/dist/api/client.js.map +1 -1
- package/dist/api/types.d.ts +40 -0
- package/dist/index.js +64 -7
- package/dist/index.js.map +1 -1
- package/dist/poc/httpPocExecutor.d.ts +20 -0
- package/dist/poc/httpPocExecutor.js +247 -0
- package/dist/poc/httpPocExecutor.js.map +1 -0
- package/dist/poc/lightpandaExecutor.d.ts +41 -0
- package/dist/poc/lightpandaExecutor.js +263 -0
- package/dist/poc/lightpandaExecutor.js.map +1 -0
- package/dist/poc/pocExecutor.d.ts +46 -0
- package/dist/poc/pocExecutor.js +74 -0
- package/dist/poc/pocExecutor.js.map +1 -0
- package/dist/poc/pocRouter.d.ts +20 -0
- package/dist/poc/pocRouter.js +40 -0
- package/dist/poc/pocRouter.js.map +1 -0
- package/dist/poc/pocTypes.d.ts +54 -0
- package/dist/poc/pocTypes.js +16 -0
- package/dist/poc/pocTypes.js.map +1 -0
- package/dist/project-map/fixTester.d.ts +55 -0
- package/dist/project-map/fixTester.js +338 -0
- package/dist/project-map/fixTester.js.map +1 -0
- package/dist/project-map/guardEvaluator.d.ts +40 -0
- package/dist/project-map/guardEvaluator.js +216 -0
- package/dist/project-map/guardEvaluator.js.map +1 -0
- package/dist/project-map/guardPatterns.d.ts +43 -0
- package/dist/project-map/guardPatterns.js +173 -0
- package/dist/project-map/guardPatterns.js.map +1 -0
- package/dist/project-map/sanitizers.d.ts +34 -0
- package/dist/project-map/sanitizers.js +76 -0
- package/dist/project-map/sanitizers.js.map +1 -0
- package/dist/project-map/sinkFinder.d.ts +48 -0
- package/dist/project-map/sinkFinder.js +340 -0
- package/dist/project-map/sinkFinder.js.map +1 -0
- package/dist/project-map/sinkRegistry.d.ts +60 -0
- package/dist/project-map/sinkRegistry.js +246 -0
- package/dist/project-map/sinkRegistry.js.map +1 -0
- package/dist/project-map/taintSources.d.ts +30 -0
- package/dist/project-map/taintSources.js +77 -0
- package/dist/project-map/taintSources.js.map +1 -0
- package/dist/project-map/taintTracker.d.ts +42 -0
- package/dist/project-map/taintTracker.js +571 -0
- package/dist/project-map/taintTracker.js.map +1 -0
- package/dist/tools/scan.js +44 -0
- package/dist/tools/scan.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase H — Fix Regression Tester.
|
|
4
|
+
*
|
|
5
|
+
* Verifies that a fix doesn't break existing behavior or introduce new
|
|
6
|
+
* vulnerabilities by running a differential analysis on the original and
|
|
7
|
+
* fixed code.
|
|
8
|
+
*
|
|
9
|
+
* Algorithm:
|
|
10
|
+
* 1. Syntax check: parse the fixed code — if it doesn't parse, syntaxValid = false
|
|
11
|
+
* 2. Differential analysis: run the AST Sink Finder (Phase B) + Taint Tracker
|
|
12
|
+
* (Phase C) on both original and fixed code
|
|
13
|
+
* - Original had a tainted sink + fixed doesn't → fix is effective
|
|
14
|
+
* - Fixed code has a new tainted sink → newVulnerabilities
|
|
15
|
+
* - Fixed code has a new sink not in original → newVulnerabilities
|
|
16
|
+
* 3. Functional regression: check if the fix changed the code structure
|
|
17
|
+
* in ways that could break behavior (removed functions, changed signatures)
|
|
18
|
+
*
|
|
19
|
+
* No runtime execution needed — the POC Executor (Phase F) handles that.
|
|
20
|
+
* The fix tester is purely static: it compares the AST analysis results.
|
|
21
|
+
*/
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.testFix = testFix;
|
|
57
|
+
const parserLoader_1 = require("./parserLoader");
|
|
58
|
+
const sinkFinder_1 = require("./sinkFinder");
|
|
59
|
+
const taintTracker_1 = require("./taintTracker");
|
|
60
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
61
|
+
/** Extract function names from source code via AST walk. */
|
|
62
|
+
async function extractFunctionNames(source, language) {
|
|
63
|
+
const parsed = await (0, parserLoader_1.parseSource)(source, language);
|
|
64
|
+
if (!parsed)
|
|
65
|
+
return new Set();
|
|
66
|
+
const { root } = parsed;
|
|
67
|
+
const names = new Set();
|
|
68
|
+
const { walk } = await Promise.resolve().then(() => __importStar(require('./astHelpers')));
|
|
69
|
+
const FUNCTION_TYPES = new Set([
|
|
70
|
+
'function_declaration', 'function_definition', 'method_definition',
|
|
71
|
+
'async_function_declaration', 'async_function_definition',
|
|
72
|
+
]);
|
|
73
|
+
for (const node of walk(root)) {
|
|
74
|
+
if (FUNCTION_TYPES.has(node.type)) {
|
|
75
|
+
const id = node.namedChildren.find(c => c.type === 'identifier');
|
|
76
|
+
if (id) {
|
|
77
|
+
const name = parsed.root.startIndex !== undefined
|
|
78
|
+
? source.slice(id.startIndex, id.endIndex)
|
|
79
|
+
: '';
|
|
80
|
+
if (name)
|
|
81
|
+
names.add(name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return names;
|
|
86
|
+
}
|
|
87
|
+
/** Compare sink findings between original and fixed code. */
|
|
88
|
+
function compareSinks(originalSinks, fixedSinks, vulnType, vulnLine) {
|
|
89
|
+
const removed = [];
|
|
90
|
+
const added = [];
|
|
91
|
+
const newVulns = [];
|
|
92
|
+
// Sinks in original but not in fixed → removed (fix worked)
|
|
93
|
+
for (const orig of originalSinks) {
|
|
94
|
+
const stillExists = fixedSinks.some(f => f.line === orig.line && f.canonicalType === orig.canonicalType);
|
|
95
|
+
if (!stillExists) {
|
|
96
|
+
removed.push(orig);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Sinks in fixed but not in original → new vulnerabilities
|
|
100
|
+
for (const fixed of fixedSinks) {
|
|
101
|
+
const existedBefore = originalSinks.some(o => o.line === fixed.line && o.canonicalType === fixed.canonicalType);
|
|
102
|
+
if (!existedBefore) {
|
|
103
|
+
added.push(fixed);
|
|
104
|
+
newVulns.push(`${fixed.canonicalType} at line ${fixed.line} (${fixed.sink})`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { removed, added, newVulns };
|
|
108
|
+
}
|
|
109
|
+
/** Compare taint results between original and fixed code. */
|
|
110
|
+
function compareTaint(originalTaint, fixedTaint, vulnType, vulnLine) {
|
|
111
|
+
const fixedFlows = [];
|
|
112
|
+
const newFlows = [];
|
|
113
|
+
// Taint flows in original but not in fixed → fixed
|
|
114
|
+
for (const orig of originalTaint) {
|
|
115
|
+
const stillTainted = fixedTaint.some(f => f.source === orig.source && f.sink === orig.sink && f.sinkLine === orig.sinkLine);
|
|
116
|
+
if (!stillTainted) {
|
|
117
|
+
fixedFlows.push(orig);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Taint flows in fixed but not in original → new vulns
|
|
121
|
+
for (const fixed of fixedTaint) {
|
|
122
|
+
const existedBefore = originalTaint.some(o => o.source === fixed.source && o.sink === fixed.sink && o.sinkLine === fixed.sinkLine);
|
|
123
|
+
if (!existedBefore) {
|
|
124
|
+
newFlows.push(fixed);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { fixedFlows, newFlows };
|
|
128
|
+
}
|
|
129
|
+
// ── Main entry point ───────────────────────────────────────────────────────
|
|
130
|
+
/**
|
|
131
|
+
* Test a fix by comparing the original and fixed code statically.
|
|
132
|
+
*
|
|
133
|
+
* @param originalCode the original source code
|
|
134
|
+
* @param fixedCode the fixed source code
|
|
135
|
+
* @param language the source language
|
|
136
|
+
* @param options optional test options (vulnType, vulnLine)
|
|
137
|
+
* @returns the test result
|
|
138
|
+
*/
|
|
139
|
+
async function testFix(originalCode, fixedCode, language, options) {
|
|
140
|
+
const tests = [];
|
|
141
|
+
const newVulnerabilities = [];
|
|
142
|
+
const regressions = [];
|
|
143
|
+
// ── 1. Syntax check ─────────────────────────────────────────────────
|
|
144
|
+
// Tree-sitter is error-recovery: it produces a parse tree even for
|
|
145
|
+
// invalid syntax, inserting ERROR/MISSING nodes. We walk the tree to
|
|
146
|
+
// detect these — parseSource returning non-null does NOT mean the
|
|
147
|
+
// syntax is valid.
|
|
148
|
+
let syntaxValid = true;
|
|
149
|
+
try {
|
|
150
|
+
const parsed = await (0, parserLoader_1.parseSource)(fixedCode, language);
|
|
151
|
+
if (!parsed) {
|
|
152
|
+
syntaxValid = false;
|
|
153
|
+
tests.push({
|
|
154
|
+
name: 'Syntax check',
|
|
155
|
+
passed: false,
|
|
156
|
+
error: 'Fixed code failed to parse (invalid syntax or unsupported language)',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Walk the tree looking for ERROR/MISSING nodes, and check the
|
|
161
|
+
// root's hasError flag (tree-sitter sets this when error
|
|
162
|
+
// recovery inserted ERROR/MISSING nodes during parsing).
|
|
163
|
+
const { walk } = await Promise.resolve().then(() => __importStar(require('./astHelpers')));
|
|
164
|
+
let hasError = parsed.root.hasError === true;
|
|
165
|
+
if (!hasError) {
|
|
166
|
+
for (const node of walk(parsed.root)) {
|
|
167
|
+
if (node.type === 'ERROR' || node.type === 'error'
|
|
168
|
+
|| node.type === 'MISSING' || node.type === 'missing') {
|
|
169
|
+
hasError = true;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (hasError) {
|
|
175
|
+
syntaxValid = false;
|
|
176
|
+
tests.push({
|
|
177
|
+
name: 'Syntax check',
|
|
178
|
+
passed: false,
|
|
179
|
+
error: 'Fixed code contains syntax errors (detected ERROR nodes in AST)',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
tests.push({ name: 'Syntax check', passed: true });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
syntaxValid = false;
|
|
189
|
+
tests.push({
|
|
190
|
+
name: 'Syntax check',
|
|
191
|
+
passed: false,
|
|
192
|
+
error: err.message,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
// If syntax is invalid, we can't do further analysis
|
|
196
|
+
if (!syntaxValid) {
|
|
197
|
+
return {
|
|
198
|
+
passes: false,
|
|
199
|
+
syntaxValid: false,
|
|
200
|
+
fixEffective: false,
|
|
201
|
+
newVulnerabilities: [],
|
|
202
|
+
regressions: [],
|
|
203
|
+
tests,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// ── 2. Differential sink analysis ────────────────────────────────────
|
|
207
|
+
let originalSinks = [];
|
|
208
|
+
let fixedSinks = [];
|
|
209
|
+
try {
|
|
210
|
+
[originalSinks, fixedSinks] = await Promise.all([
|
|
211
|
+
(0, sinkFinder_1.findSinks)(originalCode, language),
|
|
212
|
+
(0, sinkFinder_1.findSinks)(fixedCode, language),
|
|
213
|
+
]);
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
tests.push({
|
|
217
|
+
name: 'Differential sink analysis',
|
|
218
|
+
passed: false,
|
|
219
|
+
error: `Sink finder failed: ${err.message}`,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (originalSinks.length > 0 || fixedSinks.length > 0) {
|
|
223
|
+
const { removed, added, newVulns } = compareSinks(originalSinks, fixedSinks, options?.vulnType, options?.vulnLine);
|
|
224
|
+
if (newVulns.length > 0) {
|
|
225
|
+
newVulnerabilities.push(...newVulns);
|
|
226
|
+
tests.push({
|
|
227
|
+
name: 'No new sinks introduced',
|
|
228
|
+
passed: false,
|
|
229
|
+
error: `Fix introduced new sinks: ${newVulns.join(', ')}`,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
tests.push({ name: 'No new sinks introduced', passed: true });
|
|
234
|
+
}
|
|
235
|
+
if (removed.length > 0) {
|
|
236
|
+
tests.push({
|
|
237
|
+
name: 'Original sink removed',
|
|
238
|
+
passed: true,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// ── 3. Differential taint analysis ────────────────────────────────────
|
|
243
|
+
let originalTaint = [];
|
|
244
|
+
let fixedTaint = [];
|
|
245
|
+
try {
|
|
246
|
+
[originalTaint, fixedTaint] = await Promise.all([
|
|
247
|
+
(0, taintTracker_1.trackTaint)(originalCode, language),
|
|
248
|
+
(0, taintTracker_1.trackTaint)(fixedCode, language),
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
tests.push({
|
|
253
|
+
name: 'Differential taint analysis',
|
|
254
|
+
passed: false,
|
|
255
|
+
error: `Taint tracker failed: ${err.message}`,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
if (originalTaint.length > 0 || fixedTaint.length > 0) {
|
|
259
|
+
const { fixedFlows, newFlows } = compareTaint(originalTaint, fixedTaint, options?.vulnType, options?.vulnLine);
|
|
260
|
+
if (newFlows.length > 0) {
|
|
261
|
+
newVulnerabilities.push(...newFlows.map(f => `${f.canonicalType}: ${f.source} → ${f.sink} (line ${f.sinkLine})`));
|
|
262
|
+
tests.push({
|
|
263
|
+
name: 'No new taint flows introduced',
|
|
264
|
+
passed: false,
|
|
265
|
+
error: `Fix introduced new taint flows: ${newFlows.length}`,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
tests.push({ name: 'No new taint flows introduced', passed: true });
|
|
270
|
+
}
|
|
271
|
+
if (fixedFlows.length > 0) {
|
|
272
|
+
tests.push({
|
|
273
|
+
name: 'Original taint flow eliminated',
|
|
274
|
+
passed: true,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// ── 4. Function preservation check ──────────────────────────────────
|
|
279
|
+
try {
|
|
280
|
+
const [origFns, fixedFns] = await Promise.all([
|
|
281
|
+
extractFunctionNames(originalCode, language),
|
|
282
|
+
extractFunctionNames(fixedCode, language),
|
|
283
|
+
]);
|
|
284
|
+
const removedFns = [...origFns].filter(fn => !fixedFns.has(fn));
|
|
285
|
+
if (removedFns.length > 0) {
|
|
286
|
+
regressions.push(`Functions removed: ${removedFns.join(', ')}`);
|
|
287
|
+
tests.push({
|
|
288
|
+
name: 'No functions removed',
|
|
289
|
+
passed: false,
|
|
290
|
+
error: `Removed: ${removedFns.join(', ')}`,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
tests.push({ name: 'No functions removed', passed: true });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
// Best-effort: skip if function extraction fails
|
|
299
|
+
}
|
|
300
|
+
// ── 5. Determine if the fix is effective ─────────────────────────────
|
|
301
|
+
let fixEffective = false;
|
|
302
|
+
if (options?.vulnType) {
|
|
303
|
+
// Check if the specific vulnerability was removed
|
|
304
|
+
const hadVuln = originalSinks.some(s => s.canonicalType === options.vulnType)
|
|
305
|
+
|| originalTaint.some(t => t.canonicalType === options.vulnType);
|
|
306
|
+
const stillHasVuln = fixedSinks.some(s => s.canonicalType === options.vulnType)
|
|
307
|
+
|| fixedTaint.some(t => t.canonicalType === options.vulnType);
|
|
308
|
+
fixEffective = hadVuln && !stillHasVuln;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
// No specific type: fix is effective if any sink/taint was removed
|
|
312
|
+
fixEffective = originalSinks.length > fixedSinks.length
|
|
313
|
+
|| originalTaint.length > fixedTaint.length;
|
|
314
|
+
}
|
|
315
|
+
if (fixEffective) {
|
|
316
|
+
tests.push({ name: 'Fix effective (vulnerability removed)', passed: true });
|
|
317
|
+
}
|
|
318
|
+
else if (options?.vulnType) {
|
|
319
|
+
tests.push({
|
|
320
|
+
name: 'Fix effective (vulnerability removed)',
|
|
321
|
+
passed: false,
|
|
322
|
+
error: `Original ${options.vulnType} vulnerability still present in fixed code`,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
// ── Final verdict ────────────────────────────────────────────────────
|
|
326
|
+
const passes = syntaxValid
|
|
327
|
+
&& newVulnerabilities.length === 0
|
|
328
|
+
&& (fixEffective || !options?.vulnType);
|
|
329
|
+
return {
|
|
330
|
+
passes,
|
|
331
|
+
syntaxValid,
|
|
332
|
+
fixEffective,
|
|
333
|
+
newVulnerabilities,
|
|
334
|
+
regressions,
|
|
335
|
+
tests,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=fixTester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixTester.js","sourceRoot":"","sources":["../../src/project-map/fixTester.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase E — guard effectiveness evaluator.
|
|
3
|
+
*
|
|
4
|
+
* Takes a guard (middleware function, sanitizer call, or validator) and
|
|
5
|
+
* an attack type, and returns whether the guard actually stops the attack.
|
|
6
|
+
*
|
|
7
|
+
* The evaluator reuses:
|
|
8
|
+
* - `detectAuthInFunction` from `detectors.ts` to identify auth guards
|
|
9
|
+
* - The sanitizer registry from `sanitizers.ts` to identify sanitizer guards
|
|
10
|
+
* - The pattern library in `guardPatterns.ts` to evaluate effectiveness
|
|
11
|
+
*
|
|
12
|
+
* The Juror sees these deterministic guard evaluations and doesn't have to
|
|
13
|
+
* guess whether `parseInt()` stops XSS (it doesn't) or whether `escape()`
|
|
14
|
+
* stops SQLi (it doesn't).
|
|
15
|
+
*/
|
|
16
|
+
import { GuardEvaluation, AttackType } from './guardPatterns';
|
|
17
|
+
import type { SinkLanguage } from './sinkRegistry';
|
|
18
|
+
export type { GuardEvaluation, AttackType };
|
|
19
|
+
/**
|
|
20
|
+
* Evaluate a guard against a specific attack type.
|
|
21
|
+
*
|
|
22
|
+
* @param guardSource the source text of the guard function
|
|
23
|
+
* @param guardName the guard's display name (middleware name)
|
|
24
|
+
* @param attackType the attack to evaluate against
|
|
25
|
+
* @param language the source language
|
|
26
|
+
* @returns the evaluation result
|
|
27
|
+
*/
|
|
28
|
+
export declare function evaluateGuard(guardSource: string, guardName: string, attackType: AttackType, language: SinkLanguage): Promise<GuardEvaluation>;
|
|
29
|
+
/**
|
|
30
|
+
* Evaluate multiple guards against multiple attack types.
|
|
31
|
+
*
|
|
32
|
+
* @param guards array of { source, name } pairs
|
|
33
|
+
* @param attacks array of attack types to evaluate each guard against
|
|
34
|
+
* @param language the source language
|
|
35
|
+
* @returns flat array of evaluations (one per guard×attack pair)
|
|
36
|
+
*/
|
|
37
|
+
export declare function evaluateGuards(guards: Array<{
|
|
38
|
+
source: string;
|
|
39
|
+
name: string;
|
|
40
|
+
}>, attacks: AttackType[], language: SinkLanguage): Promise<GuardEvaluation[]>;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase E — guard effectiveness evaluator.
|
|
4
|
+
*
|
|
5
|
+
* Takes a guard (middleware function, sanitizer call, or validator) and
|
|
6
|
+
* an attack type, and returns whether the guard actually stops the attack.
|
|
7
|
+
*
|
|
8
|
+
* The evaluator reuses:
|
|
9
|
+
* - `detectAuthInFunction` from `detectors.ts` to identify auth guards
|
|
10
|
+
* - The sanitizer registry from `sanitizers.ts` to identify sanitizer guards
|
|
11
|
+
* - The pattern library in `guardPatterns.ts` to evaluate effectiveness
|
|
12
|
+
*
|
|
13
|
+
* The Juror sees these deterministic guard evaluations and doesn't have to
|
|
14
|
+
* guess whether `parseInt()` stops XSS (it doesn't) or whether `escape()`
|
|
15
|
+
* stops SQLi (it doesn't).
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.evaluateGuard = evaluateGuard;
|
|
19
|
+
exports.evaluateGuards = evaluateGuards;
|
|
20
|
+
const parserLoader_1 = require("./parserLoader");
|
|
21
|
+
const astHelpers_1 = require("./astHelpers");
|
|
22
|
+
const detectors_1 = require("./detectors");
|
|
23
|
+
const sanitizers_1 = require("./sanitizers");
|
|
24
|
+
const guardPatterns_1 = require("./guardPatterns");
|
|
25
|
+
// ── Guard identification ────────────────────────────────────────────────────
|
|
26
|
+
/**
|
|
27
|
+
* Identify what type of guard a function body implements.
|
|
28
|
+
* Returns the guard type, or 'unknown' if not recognized.
|
|
29
|
+
*/
|
|
30
|
+
function identifyGuardType(fnRoot, source, language) {
|
|
31
|
+
const text = source.slice(fnRoot.startIndex, fnRoot.endIndex);
|
|
32
|
+
// ── 1. Auth guards ──────────────────────────────────────────────────────
|
|
33
|
+
const auth = (0, detectors_1.detectAuthInFunction)(fnRoot, source);
|
|
34
|
+
if (auth !== 'none') {
|
|
35
|
+
if (auth === 'jwt') {
|
|
36
|
+
// Check if algorithm is pinned
|
|
37
|
+
const hasAlgorithmPin = /algorithms?\s*:/i.test(text)
|
|
38
|
+
|| /verifyOptions\.algorithms/i.test(text);
|
|
39
|
+
return hasAlgorithmPin ? 'auth-jwt-verify' : 'auth-jwt-verify-noalg';
|
|
40
|
+
}
|
|
41
|
+
if (auth === 'session')
|
|
42
|
+
return 'auth-session';
|
|
43
|
+
if (auth === 'api-key')
|
|
44
|
+
return 'auth-api-key';
|
|
45
|
+
}
|
|
46
|
+
// ── 2. Sanitizer calls ──────────────────────────────────────────────────
|
|
47
|
+
for (const node of (0, astHelpers_1.walk)(fnRoot)) {
|
|
48
|
+
if (node.type !== 'call_expression' && node.type !== 'call')
|
|
49
|
+
continue;
|
|
50
|
+
const p = (0, astHelpers_1.callParts)(node, source);
|
|
51
|
+
if (!p)
|
|
52
|
+
continue;
|
|
53
|
+
const sanitizer = (0, sanitizers_1.matchSanitizer)(p.method, p.receiver, language);
|
|
54
|
+
if (!sanitizer)
|
|
55
|
+
continue;
|
|
56
|
+
// Classify by sanitizer name
|
|
57
|
+
if (['parseInt', 'parseFloat', 'Number', 'int', 'float', 'Boolean', 'bool'].includes(sanitizer)) {
|
|
58
|
+
return 'sanitizer-numeric';
|
|
59
|
+
}
|
|
60
|
+
if (['escape', 'escapeHtml', 'sanitize', 'sanitizeHtml', 'clean', 'xss'].includes(sanitizer)
|
|
61
|
+
|| sanitizer.includes('DOMPurify') || sanitizer.includes('bleach')
|
|
62
|
+
|| sanitizer.includes('markupsafe') || sanitizer.includes('nh3')) {
|
|
63
|
+
return 'sanitizer-html';
|
|
64
|
+
}
|
|
65
|
+
if (['encodeURIComponent', 'encodeURI'].includes(sanitizer)) {
|
|
66
|
+
return 'sanitizer-url';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ── 3. Parameterized query ────────────────────────────────────────────
|
|
70
|
+
// A parameterized query passes the query string (with ? placeholders)
|
|
71
|
+
// and the params as separate arguments — no string concatenation.
|
|
72
|
+
// Pattern: db.query("SELECT ... WHERE id = ?", [params]) — two args,
|
|
73
|
+
// first is a string literal with ?, second is an array/expression.
|
|
74
|
+
for (const node of (0, astHelpers_1.walk)(fnRoot)) {
|
|
75
|
+
if (node.type !== 'call_expression' && node.type !== 'call')
|
|
76
|
+
continue;
|
|
77
|
+
const p = (0, astHelpers_1.callParts)(node, source);
|
|
78
|
+
if (!p || !p.receiver)
|
|
79
|
+
continue;
|
|
80
|
+
if (['query', 'execute', 'raw', '$queryRaw'].includes(p.method)) {
|
|
81
|
+
// Check for parameterized pattern: 2+ args, first is a string
|
|
82
|
+
// literal containing ? or a named param, and no concatenation.
|
|
83
|
+
if (p.args.length >= 2) {
|
|
84
|
+
const firstArg = p.args[0];
|
|
85
|
+
const argText = source.slice(firstArg.startIndex, firstArg.endIndex);
|
|
86
|
+
const isStringLike = firstArg.type === 'string'
|
|
87
|
+
|| firstArg.type === 'template_string'
|
|
88
|
+
|| firstArg.type === 'template_literal';
|
|
89
|
+
const hasPlaceholder = argText.includes('?') || /:\w+/.test(argText);
|
|
90
|
+
const hasConcat = argText.includes('+');
|
|
91
|
+
if (isStringLike && hasPlaceholder && !hasConcat) {
|
|
92
|
+
return 'parameterized-query';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// ── 4. Allowlist ────────────────────────────────────────────────────────
|
|
98
|
+
// A literal allowlist: if (['a', 'b', 'c'].includes(value))
|
|
99
|
+
if (/if\s*\(\s*\[[^\]]*\]\s*\.(includes|indexOf|has)\s*\(/.test(text)
|
|
100
|
+
|| /if\s*\(\s*\w+\s+in\s+\[/.test(text)) {
|
|
101
|
+
// Check if the array is a literal (not a variable)
|
|
102
|
+
const hasLiteralArray = /\[\s*['"`][^\]]*\]\s*\.(includes|indexOf)/.test(text);
|
|
103
|
+
return hasLiteralArray ? 'allowlist-literal' : 'allowlist-dynamic';
|
|
104
|
+
}
|
|
105
|
+
// ── 5. Rate limiting ──────────────────────────────────────────────────
|
|
106
|
+
if (/rateLimit|rate_limit|RateLimit|express-rate-limit|slowDown/i.test(text)) {
|
|
107
|
+
return 'rate-limit';
|
|
108
|
+
}
|
|
109
|
+
// ── 6. Helmet ──────────────────────────────────────────────────────────
|
|
110
|
+
if (/helmet\s*\(|require\s*\(\s*['"]helmet['"]/.test(text)) {
|
|
111
|
+
return 'helmet';
|
|
112
|
+
}
|
|
113
|
+
// ── 7. CORS ────────────────────────────────────────────────────────────
|
|
114
|
+
if (/cors\s*\(|require\s*\(\s*['"]cors['"]/.test(text)) {
|
|
115
|
+
// Check if origin is '*'
|
|
116
|
+
if (/origin\s*:\s*['"]\*['"]/.test(text)) {
|
|
117
|
+
return 'cors-permissive';
|
|
118
|
+
}
|
|
119
|
+
return 'cors-strict';
|
|
120
|
+
}
|
|
121
|
+
// ── 8. Validator (zod/joi/pydantic) ────────────────────────────────────
|
|
122
|
+
if (/\.parse\s*\(|\.validate\s*\(|\.safeParse\s*\(/.test(text)
|
|
123
|
+
&& /(zod|joi|yup|pydantic|schema)/i.test(text)) {
|
|
124
|
+
return 'validator';
|
|
125
|
+
}
|
|
126
|
+
return 'unknown';
|
|
127
|
+
}
|
|
128
|
+
// ── Evaluation ──────────────────────────────────────────────────────────────
|
|
129
|
+
/**
|
|
130
|
+
* Evaluate a guard against a specific attack type.
|
|
131
|
+
*
|
|
132
|
+
* @param guardSource the source text of the guard function
|
|
133
|
+
* @param guardName the guard's display name (middleware name)
|
|
134
|
+
* @param attackType the attack to evaluate against
|
|
135
|
+
* @param language the source language
|
|
136
|
+
* @returns the evaluation result
|
|
137
|
+
*/
|
|
138
|
+
async function evaluateGuard(guardSource, guardName, attackType, language) {
|
|
139
|
+
const parsed = await (0, parserLoader_1.parseSource)(guardSource, language);
|
|
140
|
+
let guardType = 'unknown';
|
|
141
|
+
if (parsed) {
|
|
142
|
+
// Try to find a function body in the parsed source. The guard source
|
|
143
|
+
// may be a full function declaration or just a function body.
|
|
144
|
+
const { root } = parsed;
|
|
145
|
+
// Look for a function declaration/definition
|
|
146
|
+
let fnNode = null;
|
|
147
|
+
for (const n of (0, astHelpers_1.walk)(root)) {
|
|
148
|
+
if (['function_declaration', 'function_definition', 'method_definition',
|
|
149
|
+
'async_function_declaration', 'async_function_definition',
|
|
150
|
+
'arrow_function', 'function_expression'].includes(n.type)) {
|
|
151
|
+
fnNode = n;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (fnNode) {
|
|
156
|
+
guardType = identifyGuardType(fnNode, guardSource, language);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
// Parse as a bare expression/statement block
|
|
160
|
+
guardType = identifyGuardType(root, guardSource, language);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Look up effectiveness
|
|
164
|
+
const effective = guardPatterns_1.GUARD_EFFECTIVENESS[guardType]?.[attackType];
|
|
165
|
+
const isEffective = effective === true;
|
|
166
|
+
const isKnown = effective !== undefined;
|
|
167
|
+
// Build reason
|
|
168
|
+
let reason;
|
|
169
|
+
let bypassExample;
|
|
170
|
+
if (isEffective) {
|
|
171
|
+
reason = guardPatterns_1.GUARD_EFFECTIVE_REASONS[guardType]?.[attackType]
|
|
172
|
+
?? `Guard type '${guardType}' is effective against ${attackType}`;
|
|
173
|
+
}
|
|
174
|
+
else if (isKnown) {
|
|
175
|
+
// Not effective
|
|
176
|
+
reason = `Guard type '${guardType}' is NOT effective against ${attackType}`;
|
|
177
|
+
bypassExample = guardPatterns_1.GUARD_BYPASS_EXAMPLES[guardType]?.[attackType];
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Unknown — guard detected but we can't determine effectiveness
|
|
181
|
+
reason = `Guard detected as '${guardType}' but effectiveness against ${attackType} is unknown`;
|
|
182
|
+
bypassExample = undefined;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
guardName,
|
|
186
|
+
guardType,
|
|
187
|
+
attackType,
|
|
188
|
+
effective: isEffective,
|
|
189
|
+
reason,
|
|
190
|
+
...(bypassExample && { bypassExample }),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Evaluate multiple guards against multiple attack types.
|
|
195
|
+
*
|
|
196
|
+
* @param guards array of { source, name } pairs
|
|
197
|
+
* @param attacks array of attack types to evaluate each guard against
|
|
198
|
+
* @param language the source language
|
|
199
|
+
* @returns flat array of evaluations (one per guard×attack pair)
|
|
200
|
+
*/
|
|
201
|
+
async function evaluateGuards(guards, attacks, language) {
|
|
202
|
+
const results = [];
|
|
203
|
+
for (const guard of guards) {
|
|
204
|
+
for (const attack of attacks) {
|
|
205
|
+
try {
|
|
206
|
+
const eval_ = await evaluateGuard(guard.source, guard.name, attack, language);
|
|
207
|
+
results.push(eval_);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Best-effort: skip guards that fail to parse
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return results;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=guardEvaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardEvaluator.js","sourceRoot":"","sources":["../../src/project-map/guardEvaluator.ts"],"names":[],"mappings":"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"}
|