@securecode-ai/mcp 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +107 -13
  2. package/dist/api/client.d.ts +1 -1
  3. package/dist/api/client.js +2 -1
  4. package/dist/api/client.js.map +1 -1
  5. package/dist/attack/agentScanExecutor.js +159 -5
  6. package/dist/attack/agentScanExecutor.js.map +1 -1
  7. package/dist/attack/agentScanLoop.js +157 -3
  8. package/dist/attack/agentScanLoop.js.map +1 -1
  9. package/dist/attack/agentScanProtocol.d.ts +47 -5
  10. package/dist/attack/agentScanProtocol.js +3 -3
  11. package/dist/index.js +218 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp/server.js +62 -0
  14. package/dist/mcp/server.js.map +1 -1
  15. package/dist/mcp/tools.js +80 -3
  16. package/dist/mcp/tools.js.map +1 -1
  17. package/dist/project-map/agentMemory.d.ts +75 -0
  18. package/dist/project-map/agentMemory.js +230 -0
  19. package/dist/project-map/agentMemory.js.map +1 -0
  20. package/dist/project-map/crossFileTaintTracker.d.ts +51 -0
  21. package/dist/project-map/crossFileTaintTracker.js +429 -0
  22. package/dist/project-map/crossFileTaintTracker.js.map +1 -0
  23. package/dist/project-map/endpointDiscovery.d.ts +24 -0
  24. package/dist/project-map/endpointDiscovery.js +101 -0
  25. package/dist/project-map/endpointDiscovery.js.map +1 -0
  26. package/dist/project-map/guardEvaluator.js +37 -0
  27. package/dist/project-map/guardEvaluator.js.map +1 -1
  28. package/dist/project-map/guardPatterns.js +16 -5
  29. package/dist/project-map/guardPatterns.js.map +1 -1
  30. package/dist/project-map/sanitizers.js +10 -0
  31. package/dist/project-map/sanitizers.js.map +1 -1
  32. package/dist/project-map/scanCache.d.ts +67 -0
  33. package/dist/project-map/scanCache.js +169 -0
  34. package/dist/project-map/scanCache.js.map +1 -0
  35. package/dist/project-map/sinkRegistry.js +165 -0
  36. package/dist/project-map/sinkRegistry.js.map +1 -1
  37. package/dist/project-map/taintSources.d.ts +19 -1
  38. package/dist/project-map/taintSources.js +69 -2
  39. package/dist/project-map/taintSources.js.map +1 -1
  40. package/dist/project-map/taintTracker.d.ts +4 -1
  41. package/dist/project-map/taintTracker.js +188 -5
  42. package/dist/project-map/taintTracker.js.map +1 -1
  43. package/dist/tools/agentMemoryTools.d.ts +18 -0
  44. package/dist/tools/agentMemoryTools.js +107 -0
  45. package/dist/tools/agentMemoryTools.js.map +1 -0
  46. package/dist/tools/agentScan.js +206 -33
  47. package/dist/tools/agentScan.js.map +1 -1
  48. package/dist/utils/files.js +19 -2
  49. package/dist/utils/files.js.map +1 -1
  50. package/dist/utils/listFiles.d.ts +17 -0
  51. package/dist/utils/listFiles.js +126 -0
  52. package/dist/utils/listFiles.js.map +1 -0
  53. package/dist/utils/listImports.d.ts +15 -0
  54. package/dist/utils/listImports.js +164 -0
  55. package/dist/utils/listImports.js.map +1 -0
  56. package/package.json +1 -1
@@ -0,0 +1,429 @@
1
+ "use strict";
2
+ /**
3
+ * Cross-file taint tracker — follows taint across file boundaries.
4
+ *
5
+ * The single-file `trackTaint` engine finds source→sink flows within one
6
+ * file. This module extends that to multi-file flows by:
7
+ *
8
+ * 1. Parsing the entry file's imports (collectImports)
9
+ * 2. Resolving each import to a file on disk (resolveModulePath)
10
+ * 3. Indexing each imported function: does it return tainted data?
11
+ * Does it propagate taint through a specific parameter?
12
+ * 4. Running taint tracking on the entry file with the cross-file
13
+ * function index, so calls to imported functions that propagate
14
+ * taint are recognised.
15
+ *
16
+ * Limits prevent runaway analysis on large codebases:
17
+ * maxDepth (3) — how many import hops to follow
18
+ * maxFiles (10) — total files parsed per trace
19
+ * maxFunctionBodySize (5000 chars) — skip oversized functions
20
+ */
21
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.trackTaintCrossFile = trackTaintCrossFile;
56
+ const fs = __importStar(require("fs"));
57
+ const path = __importStar(require("path"));
58
+ const parserLoader_1 = require("./parserLoader");
59
+ const astHelpers_1 = require("./astHelpers");
60
+ const taintTracker_1 = require("./taintTracker");
61
+ // ── Constants ───────────────────────────────────────────────────────────────
62
+ const DEFAULT_MAX_DEPTH = 3;
63
+ const DEFAULT_MAX_FILES = 10;
64
+ const MAX_FUNCTION_BODY_SIZE = 5000;
65
+ const FUNCTION_NODE_TYPES = new Set([
66
+ 'function_declaration', 'function_definition', 'method_definition',
67
+ 'async_function_declaration', 'async_function_definition',
68
+ 'arrow_function', 'function_expression',
69
+ 'generator_function_declaration', 'generator_declaration',
70
+ ]);
71
+ /**
72
+ * Build a map of `functionName → FunctionAnalysis` for all exported
73
+ * functions in the file. Each function is analyzed to determine whether
74
+ * it returns tainted data and which parameters propagate taint.
75
+ */
76
+ async function indexFileFunctions(source, language, file) {
77
+ const out = new Map();
78
+ const parsed = await (0, parserLoader_1.parseSource)(source, language);
79
+ if (!parsed)
80
+ return out;
81
+ const { root } = parsed;
82
+ for (const node of (0, astHelpers_1.walk)(root)) {
83
+ if (!FUNCTION_NODE_TYPES.has(node.type))
84
+ continue;
85
+ const name = functionName(node, source);
86
+ if (!name)
87
+ continue;
88
+ // Skip functions that are too large
89
+ const bodyText = source.slice(node.startIndex, node.endIndex);
90
+ if (bodyText.length > MAX_FUNCTION_BODY_SIZE)
91
+ continue;
92
+ const params = extractParams(node, source);
93
+ const line = node.startPosition.row + 1;
94
+ // Analyze: seed each parameter as tainted, check if the function
95
+ // returns tainted data from any param
96
+ const taintThroughParams = new Set();
97
+ let returnsTainted = false;
98
+ for (let i = 0; i < params.length; i++) {
99
+ const seedName = params[i];
100
+ if (!seedName)
101
+ continue;
102
+ // Run taint tracking on just this function body with the param
103
+ // seeded as a source. If any result mentions this param as the
104
+ // source, the function propagates taint through it.
105
+ const flows = await trackTaintWithSeed(source, language, seedName, node);
106
+ if (flows.length > 0) {
107
+ taintThroughParams.add(i);
108
+ returnsTainted = true; // if it reaches a sink it could also be returned
109
+ }
110
+ }
111
+ // Also check if the function returns tainted data directly
112
+ // (e.g. `return req.body` without going through a sink)
113
+ if (!returnsTainted) {
114
+ returnsTainted = checkReturnsTainted(node, source, language);
115
+ }
116
+ out.set(name, { name, file, line, params, returnsTainted, taintThroughParams });
117
+ }
118
+ return out;
119
+ }
120
+ /**
121
+ * Check if a function returns tainted data by examining return statements.
122
+ * Looks for returns of taint sources (req.body, etc.) or tainted variables.
123
+ */
124
+ function checkReturnsTainted(fnNode, source, language) {
125
+ let found = false;
126
+ for (const child of (0, astHelpers_1.walk)(fnNode)) {
127
+ if (child.type !== 'return_statement' && child.type !== 'return')
128
+ continue;
129
+ // Skip the function's own return type annotation
130
+ if (child.parent && FUNCTION_NODE_TYPES.has(child.parent.type) && child === child.parent.namedChildren[0])
131
+ continue;
132
+ const expr = child.namedChildren.find(c => c.type !== 'return' && c.type !== 'return_statement');
133
+ if (!expr)
134
+ continue;
135
+ const text = source.slice(expr.startIndex, expr.endIndex);
136
+ // Simple heuristic: if the return expression contains a known source pattern
137
+ // like req.body, request.GET, etc.
138
+ if (isTaintSourceText(text, language)) {
139
+ found = true;
140
+ break;
141
+ }
142
+ }
143
+ return found;
144
+ }
145
+ /**
146
+ * Check if text looks like a taint source (req.body, request.GET, etc.)
147
+ * without needing a full AST parse.
148
+ */
149
+ function isTaintSourceText(text, language) {
150
+ const patterns = [];
151
+ if (language === 'python') {
152
+ patterns.push('request.GET', 'request.POST', 'request.args', 'request.form', 'request.json', 'request.body', 'request.headers', 'request.cookies', 'request.values', 'request.data', 'request.META', 'request.FILES', 'sys.argv', 'os.environ', 'os.getenv');
153
+ }
154
+ else {
155
+ patterns.push('req.body', 'req.query', 'req.params', 'req.headers', 'req.cookies', 'req.files', 'request.body', 'request.query', 'request.params', 'request.headers', 'request.cookies', 'ctx.body', 'ctx.query', 'ctx.params', 'ctx.headers', 'event.body', 'req.json', 'request.json');
156
+ }
157
+ return patterns.some(p => text.includes(p));
158
+ }
159
+ /**
160
+ * Run taint tracking on a file with a parameter seeded as a taint source.
161
+ * Uses the seedParams option of trackTaint to pre-taint the parameter
162
+ * at module scope, so it propagates into the function's scope.
163
+ */
164
+ async function trackTaintWithSeed(source, language, seedParam, fnNode) {
165
+ const results = await (0, taintTracker_1.trackTaint)(source, language, [seedParam]);
166
+ // Filter: only keep results where the source involves our seed
167
+ return results.filter(r => r.source === `<param:${seedParam}>` ||
168
+ r.propagationPath.some(p => p.variable === seedParam && p.operation === 'source'));
169
+ }
170
+ /** Extract parameter names from a function node. */
171
+ function extractParams(fnNode, source) {
172
+ const params = [];
173
+ for (const child of fnNode.namedChildren) {
174
+ if (child.type === 'parameters' || child.type === 'formal_parameters' || child.type === 'parameter_list') {
175
+ for (const param of child.namedChildren) {
176
+ if ((0, astHelpers_1.isIdentifier)(param)) {
177
+ // Plain identifier: `name` (JS/Python)
178
+ params.push(source.slice(param.startIndex, param.endIndex));
179
+ }
180
+ else if (param.type === 'required_parameter') {
181
+ // TypeScript required parameter: `name: string` — take the identifier
182
+ const id = param.namedChildren.find(c => (0, astHelpers_1.isIdentifier)(c));
183
+ if (id)
184
+ params.push(source.slice(id.startIndex, id.endIndex));
185
+ }
186
+ else if (param.type === 'shorthand_property_identifier_pattern' || param.type === 'shorthand_property_identifier') {
187
+ params.push(source.slice(param.startIndex, param.endIndex));
188
+ }
189
+ else if (param.type === 'assignment_pattern') {
190
+ // default param: `x = 'default'` — take the left side
191
+ const left = param.namedChildren[0];
192
+ if (left && (0, astHelpers_1.isIdentifier)(left)) {
193
+ params.push(source.slice(left.startIndex, left.endIndex));
194
+ }
195
+ }
196
+ else if (param.type === 'identifier' || param.type === 'typed_parameter') {
197
+ params.push(source.slice(param.startIndex, param.endIndex));
198
+ }
199
+ else if (param.type === 'typed_parameter') {
200
+ // Python typed param: `name: str`
201
+ const id = param.namedChildren.find(c => (0, astHelpers_1.isIdentifier)(c));
202
+ if (id)
203
+ params.push(source.slice(id.startIndex, id.endIndex));
204
+ }
205
+ // Skip rest params, destructuring patterns — too complex for now
206
+ }
207
+ break;
208
+ }
209
+ }
210
+ return params;
211
+ }
212
+ /** Get the name of a function node (mirrors taintTracker's functionName). */
213
+ function functionName(fn, source) {
214
+ const id = fn.namedChildren.find(c => (0, astHelpers_1.isIdentifier)(c));
215
+ if (id)
216
+ return source.slice(id.startIndex, id.endIndex);
217
+ const parent = fn.parent;
218
+ if (parent && (parent.type === 'lexical_declaration' || parent.type === 'variable_declaration' || parent.type === 'assignment')) {
219
+ const name = parent.child(0);
220
+ if (name && (0, astHelpers_1.isIdentifier)(name))
221
+ return source.slice(name.startIndex, name.endIndex);
222
+ }
223
+ return null;
224
+ }
225
+ // ── Language detection ──────────────────────────────────────────────────────
226
+ function toSinkLanguage(filePath) {
227
+ const ext = path.extname(filePath).toLowerCase();
228
+ const map = {
229
+ '.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
230
+ '.ts': 'typescript', '.tsx': 'tsx',
231
+ '.py': 'python',
232
+ };
233
+ return map[ext] || null;
234
+ }
235
+ // ── Main entry point ───────────────────────────────────────────────────────
236
+ /**
237
+ * Track taint propagation across file boundaries.
238
+ *
239
+ * Parses the entry file, resolves its imports, indexes imported functions
240
+ * for taint behavior, then runs taint tracking with cross-file knowledge.
241
+ *
242
+ * Returns CrossFileTaintResult[] with full file:line paths.
243
+ */
244
+ async function trackTaintCrossFile(opts) {
245
+ const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
246
+ const maxFiles = opts.maxFiles ?? DEFAULT_MAX_FILES;
247
+ const entryAbs = path.resolve(opts.workspaceRoot, opts.filePath);
248
+ const language = toSinkLanguage(opts.filePath);
249
+ if (!language)
250
+ return [];
251
+ const entrySource = fs.readFileSync(entryAbs, 'utf8');
252
+ const entryParsed = await (0, parserLoader_1.parseSource)(entrySource, language);
253
+ if (!entryParsed)
254
+ return [];
255
+ // 1. Collect imports from the entry file
256
+ const imports = (0, astHelpers_1.collectImports)(entryParsed.root, entrySource);
257
+ // imports = Map { localName -> moduleSpec }
258
+ // 2. Build a function index from imported files
259
+ const functionIndex = new Map();
260
+ const visitedFiles = new Set([opts.filePath]);
261
+ const filesToIndex = [];
262
+ for (const [localName, moduleSpec] of imports) {
263
+ filesToIndex.push({ spec: moduleSpec, fromFile: opts.filePath, depth: 0 });
264
+ }
265
+ let filesParsed = 1;
266
+ while (filesToIndex.length > 0 && filesParsed < maxFiles) {
267
+ const { spec, fromFile, depth } = filesToIndex.shift();
268
+ if (depth >= maxDepth)
269
+ continue;
270
+ const resolvedFile = (0, astHelpers_1.resolveModulePath)(spec, path.resolve(opts.workspaceRoot, fromFile), opts.workspaceRoot);
271
+ if (!resolvedFile)
272
+ continue; // node_modules or unresolvable
273
+ if (visitedFiles.has(resolvedFile))
274
+ continue; // cycle guard
275
+ visitedFiles.add(resolvedFile);
276
+ const resolvedAbs = path.resolve(opts.workspaceRoot, resolvedFile);
277
+ let source;
278
+ try {
279
+ source = fs.readFileSync(resolvedAbs, 'utf8');
280
+ }
281
+ catch {
282
+ continue;
283
+ }
284
+ const fileLang = toSinkLanguage(resolvedFile);
285
+ if (!fileLang)
286
+ continue;
287
+ const index = await indexFileFunctions(source, fileLang, resolvedFile);
288
+ for (const [name, analysis] of index) {
289
+ functionIndex.set(name, analysis);
290
+ }
291
+ filesParsed++;
292
+ // Queue deeper imports (depth+1) if we haven't hit the limit
293
+ if (depth + 1 < maxDepth) {
294
+ const parsed = await (0, parserLoader_1.parseSource)(source, fileLang);
295
+ if (parsed) {
296
+ const deeperImports = (0, astHelpers_1.collectImports)(parsed.root, source);
297
+ for (const [_, deeperSpec] of deeperImports) {
298
+ filesToIndex.push({ spec: deeperSpec, fromFile: resolvedFile, depth: depth + 1 });
299
+ }
300
+ }
301
+ }
302
+ }
303
+ // 3. Run single-file taint tracking on the entry file
304
+ const entryFlows = await (0, taintTracker_1.trackTaint)(entrySource, language);
305
+ if (entryFlows.length === 0) {
306
+ // No flows in the entry file — check if any imported functions
307
+ // have sinks that could be reached through tainted parameters
308
+ return checkImportedFunctionSinks(functionIndex, imports, opts.workspaceRoot, language);
309
+ }
310
+ // 4. Convert single-file results to cross-file results
311
+ // For now: if the entry file has flows, report them with the entry file
312
+ // as both source and sink file. If the flow involves a call to an
313
+ // indexed function, add the cross-file step.
314
+ const results = [];
315
+ for (const flow of entryFlows) {
316
+ const steps = [];
317
+ let sourceFile = opts.filePath;
318
+ let sinkFile = opts.filePath;
319
+ for (const step of flow.propagationPath) {
320
+ // Check if this step involves a call to an indexed function
321
+ const callMatch = step.description.match(/^([\w$]+)\(\)/);
322
+ if (callMatch && (step.operation === 'call-pass' || step.operation === 'sink-arg')) {
323
+ const fnName = callMatch[1];
324
+ const analysis = functionIndex.get(fnName);
325
+ if (analysis && analysis.file !== opts.filePath) {
326
+ // This step crosses a file boundary
327
+ steps.push({
328
+ file: opts.filePath,
329
+ line: step.line,
330
+ variable: step.variable,
331
+ operation: 'call-cross-file',
332
+ description: `${fnName}() in ${analysis.file}:${analysis.line} ${analysis.returnsTainted ? 'returns tainted data' : 'propagates taint'}`,
333
+ });
334
+ sourceFile = analysis.file;
335
+ continue;
336
+ }
337
+ }
338
+ steps.push({
339
+ file: opts.filePath,
340
+ line: step.line,
341
+ variable: step.variable,
342
+ operation: step.operation === 'source' ? 'source' : 'sink-arg',
343
+ description: step.description,
344
+ });
345
+ }
346
+ results.push({
347
+ source: flow.source,
348
+ sourceLine: flow.sourceLine,
349
+ sourceFile,
350
+ sink: flow.sink,
351
+ sinkLine: flow.sinkLine,
352
+ sinkFile,
353
+ canonicalType: flow.canonicalType,
354
+ crossFileSteps: steps,
355
+ });
356
+ }
357
+ // 5. Also check imported functions for sinks reachable through params
358
+ const importedSinks = await checkImportedFunctionSinks(functionIndex, imports, opts.workspaceRoot, language);
359
+ results.push(...importedSinks);
360
+ // Dedup by (source, sink, sinkFile, sinkLine)
361
+ const seen = new Set();
362
+ const deduped = [];
363
+ for (const r of results) {
364
+ const key = `${r.source}:${r.sink}:${r.sinkFile}:${r.sinkLine}`;
365
+ if (!seen.has(key)) {
366
+ seen.add(key);
367
+ deduped.push(r);
368
+ }
369
+ }
370
+ return deduped;
371
+ }
372
+ /**
373
+ * Check imported functions for sinks reachable through tainted parameters.
374
+ * If an imported function has a sink in its body and the caller passes
375
+ * tainted data to the relevant parameter, that's a cross-file flow.
376
+ */
377
+ async function checkImportedFunctionSinks(functionIndex, imports, workspaceRoot, language) {
378
+ const results = [];
379
+ for (const [name, analysis] of functionIndex) {
380
+ if (analysis.taintThroughParams.size === 0)
381
+ continue;
382
+ // This function has sinks reachable through params.
383
+ // Read its source and run trackTaint with all params seeded to find sinks.
384
+ const fileAbs = path.resolve(workspaceRoot, analysis.file);
385
+ let source;
386
+ try {
387
+ source = fs.readFileSync(fileAbs, 'utf8');
388
+ }
389
+ catch {
390
+ continue;
391
+ }
392
+ // Seed all params of this function as taint sources
393
+ const flows = await (0, taintTracker_1.trackTaint)(source, language, analysis.params);
394
+ for (const flow of flows) {
395
+ // Only include flows where the source is a seeded parameter
396
+ const isParamSource = analysis.params.includes(flow.source.replace('<param:', '').replace('>', '')) ||
397
+ flow.propagationPath.some(p => p.operation === 'source' && analysis.params.includes(p.variable));
398
+ if (!isParamSource)
399
+ continue;
400
+ results.push({
401
+ source: `${name}() param: ${flow.source}`,
402
+ sourceLine: analysis.line,
403
+ sourceFile: analysis.file,
404
+ sink: flow.sink,
405
+ sinkLine: flow.sinkLine,
406
+ sinkFile: analysis.file,
407
+ canonicalType: flow.canonicalType,
408
+ crossFileSteps: [
409
+ {
410
+ file: analysis.file,
411
+ line: analysis.line,
412
+ variable: name,
413
+ operation: 'call-cross-file',
414
+ description: `${name}() called with tainted data — param "${flow.source}" reaches sink`,
415
+ },
416
+ {
417
+ file: analysis.file,
418
+ line: flow.sinkLine,
419
+ variable: flow.sink,
420
+ operation: 'sink-arg',
421
+ description: `${flow.sink}(${flow.propagationPath[flow.propagationPath.length - 1]?.variable || ''})`,
422
+ },
423
+ ],
424
+ });
425
+ }
426
+ }
427
+ return results;
428
+ }
429
+ //# sourceMappingURL=crossFileTaintTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crossFileTaintTracker.js","sourceRoot":"","sources":["../../src/project-map/crossFileTaintTracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Endpoint discovery — scans the workspace for HTTP route handler definitions
3
+ * across all supported frameworks.
4
+ *
5
+ * Uses ripgrep (via searchCode) for speed. Detects:
6
+ * - Next.js App Router: export async function GET/POST/PATCH/DELETE/PUT
7
+ * - Express: app.get/post/patch/delete/put('path', handler)
8
+ * router.get/post/...('path', handler)
9
+ * - FastAPI: @app.get/post/...("/path")
10
+ * @router.get/post/...("/path")
11
+ * - Flask: @app.route("/path") @app.get/post/...("/path")
12
+ * - Django: path('url', views.handler) re_path(...)
13
+ *
14
+ * Returns a compact list of { method, path, file, line, framework } entries.
15
+ */
16
+ export interface DiscoveredEndpoint {
17
+ method: string;
18
+ path: string;
19
+ file: string;
20
+ line: number;
21
+ framework: string;
22
+ }
23
+ export declare function discoverEndpoints(workspaceRoot: string, glob?: string): Promise<DiscoveredEndpoint[]>;
24
+ export declare function formatEndpoints(endpoints: DiscoveredEndpoint[]): string;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ /**
3
+ * Endpoint discovery — scans the workspace for HTTP route handler definitions
4
+ * across all supported frameworks.
5
+ *
6
+ * Uses ripgrep (via searchCode) for speed. Detects:
7
+ * - Next.js App Router: export async function GET/POST/PATCH/DELETE/PUT
8
+ * - Express: app.get/post/patch/delete/put('path', handler)
9
+ * router.get/post/...('path', handler)
10
+ * - FastAPI: @app.get/post/...("/path")
11
+ * @router.get/post/...("/path")
12
+ * - Flask: @app.route("/path") @app.get/post/...("/path")
13
+ * - Django: path('url', views.handler) re_path(...)
14
+ *
15
+ * Returns a compact list of { method, path, file, line, framework } entries.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.discoverEndpoints = discoverEndpoints;
19
+ exports.formatEndpoints = formatEndpoints;
20
+ const searchCode_1 = require("../utils/searchCode");
21
+ const HTTP_METHODS = ['GET', 'POST', 'PATCH', 'DELETE', 'PUT'];
22
+ // Patterns to search for. Each entry has a regex and a framework label.
23
+ const PATTERNS = [
24
+ // Next.js App Router: export async function GET / POST / etc.
25
+ { regex: 'export\\s+async\\s+function\\s+(GET|POST|PATCH|DELETE|PUT)\\b', framework: 'nextjs' },
26
+ // Express: app.METHOD( or router.METHOD(
27
+ { regex: '(app|router)\\.(get|post|patch|delete|put)\\s*\\(', framework: 'express' },
28
+ // Express: app.all(
29
+ { regex: '(app|router)\\.all\\s*\\(', framework: 'express' },
30
+ // FastAPI: @app.get( / @router.get(
31
+ { regex: '@(app|router)\\.(get|post|patch|delete|put)\\s*\\(', framework: 'fastapi' },
32
+ // Flask: @app.route(
33
+ { regex: '@app\\.route\\s*\\(', framework: 'flask' },
34
+ // Flask 2.0: @app.get( / @app.post( etc
35
+ { regex: '@app\\.(get|post|patch|delete|put)\\s*\\(', framework: 'flask' },
36
+ // Django: path('url', views.handler)
37
+ { regex: '(re_)?path\\s*\\(\\s*[\'"]', framework: 'django' },
38
+ ];
39
+ function extractMethod(text, framework) {
40
+ const upper = text.toUpperCase();
41
+ for (const m of HTTP_METHODS) {
42
+ if (upper.includes(m))
43
+ return m;
44
+ }
45
+ if (framework === 'flask' && text.includes('.route('))
46
+ return '*';
47
+ if (framework === 'django')
48
+ return '*';
49
+ if (upper.includes('.ALL('))
50
+ return '*';
51
+ return '?';
52
+ }
53
+ function extractPath(text) {
54
+ // Try to find a string literal (single or double quoted) in the line
55
+ const match = text.match(/['"`]([^'"`]+)['"`]/);
56
+ return match ? match[1] : '';
57
+ }
58
+ async function discoverEndpoints(workspaceRoot, glob) {
59
+ const allEndpoints = [];
60
+ const seen = new Set(); // dedup by file:line
61
+ for (const { regex, framework } of PATTERNS) {
62
+ try {
63
+ const result = await (0, searchCode_1.searchCode)(workspaceRoot, regex, glob);
64
+ for (const hit of result.hits) {
65
+ const key = `${hit.path}:${hit.line}`;
66
+ if (seen.has(key))
67
+ continue;
68
+ seen.add(key);
69
+ allEndpoints.push({
70
+ method: extractMethod(hit.text, framework),
71
+ path: extractPath(hit.text),
72
+ file: hit.path,
73
+ line: hit.line,
74
+ framework,
75
+ });
76
+ }
77
+ }
78
+ catch {
79
+ // ripgrep not available — skip this pattern
80
+ }
81
+ }
82
+ // Sort by file, then line
83
+ allEndpoints.sort((a, b) => {
84
+ if (a.file !== b.file)
85
+ return a.file.localeCompare(b.file);
86
+ return a.line - b.line;
87
+ });
88
+ return allEndpoints;
89
+ }
90
+ function formatEndpoints(endpoints) {
91
+ if (endpoints.length === 0) {
92
+ return 'No HTTP route handlers found in the workspace.';
93
+ }
94
+ const lines = [`Found ${endpoints.length} endpoint(s):`];
95
+ for (const ep of endpoints) {
96
+ const pathStr = ep.path ? ` ${ep.path}` : '';
97
+ lines.push(` ${ep.method.padEnd(6)} ${ep.file}:${ep.line} [${ep.framework}]${pathStr}`);
98
+ }
99
+ return lines.join('\n');
100
+ }
101
+ //# sourceMappingURL=endpointDiscovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpointDiscovery.js","sourceRoot":"","sources":["../../src/project-map/endpointDiscovery.ts"],"names":[],"mappings":"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"}
@@ -123,6 +123,43 @@ function identifyGuardType(fnRoot, source, language) {
123
123
  && /(zod|joi|yup|pydantic|schema)/i.test(text)) {
124
124
  return 'validator';
125
125
  }
126
+ // ── 9. Python-specific guards ─────────────────────────────────────────
127
+ if (language === 'python') {
128
+ // Python allowlist: if x in {'a', 'b'} or if x in ('a', 'b') or if x in ['a', 'b']
129
+ if (/if\s+\w+\s+in\s*[\(\{\[]\s*['"][^\])}]*[\])}]/.test(text)) {
130
+ const hasLiteralSet = /in\s*[\(\{\[]\s*['"][^)\]}]*['"]\s*[,)\]}]/.test(text);
131
+ return hasLiteralSet ? 'allowlist-literal' : 'allowlist-dynamic';
132
+ }
133
+ // Python parameterized query: cursor.execute("...%s...", [params]) or
134
+ // cursor.execute("...:name...", {...}) or session.execute(text("...:name..."), {...})
135
+ for (const node of (0, astHelpers_1.walk)(fnRoot)) {
136
+ if (node.type !== 'call' && node.type !== 'call_expression')
137
+ continue;
138
+ const p = (0, astHelpers_1.callParts)(node, source);
139
+ if (!p || !p.receiver)
140
+ continue;
141
+ if (['execute', 'executemany', 'execute_many'].includes(p.method)) {
142
+ if (p.args.length >= 2) {
143
+ const firstArg = p.args[0];
144
+ const argText = source.slice(firstArg.startIndex, firstArg.endIndex);
145
+ const isStringLike = firstArg.type === 'string';
146
+ const hasPlaceholder = argText.includes('%s') || /:\w+/.test(argText);
147
+ const hasConcat = argText.includes(' + ') || argText.includes('% ') || argText.includes('.format(');
148
+ if (isStringLike && hasPlaceholder && !hasConcat) {
149
+ return 'parameterized-query';
150
+ }
151
+ }
152
+ }
153
+ }
154
+ // Python constant-time comparison: secrets.compare_digest or hmac.compare_digest
155
+ if (/compare_digest|secrets\.compare|hmac\.compare/.test(text)) {
156
+ return 'auth-api-key'; // constant-time comparison = auth guard
157
+ }
158
+ // Python rate limiting: flask_limiter, slowapi, ratelimit decorator
159
+ if (/flask_limiter|Limiter|slowapi|ratelimit|@limiter|@rate_limit/i.test(text)) {
160
+ return 'rate-limit';
161
+ }
162
+ }
126
163
  return 'unknown';
127
164
  }
128
165
  // ── Evaluation ──────────────────────────────────────────────────────────────