@securecode-ai/mcp 0.1.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/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/api/client.d.ts +22 -0
- package/dist/api/client.js +141 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/types.d.ts +129 -0
- package/dist/api/types.js +3 -0
- package/dist/api/types.js.map +1 -0
- package/dist/approval/auditLog.d.ts +3 -0
- package/dist/approval/auditLog.js +67 -0
- package/dist/approval/auditLog.js.map +1 -0
- package/dist/approval/broker.d.ts +11 -0
- package/dist/approval/broker.js +252 -0
- package/dist/approval/broker.js.map +1 -0
- package/dist/approval/types.d.ts +26 -0
- package/dist/approval/types.js +58 -0
- package/dist/approval/types.js.map +1 -0
- package/dist/attack/agentLoop.d.ts +21 -0
- package/dist/attack/agentLoop.js +148 -0
- package/dist/attack/agentLoop.js.map +1 -0
- package/dist/attack/executor.d.ts +17 -0
- package/dist/attack/executor.js +162 -0
- package/dist/attack/executor.js.map +1 -0
- package/dist/attack/policy.d.ts +26 -0
- package/dist/attack/policy.js +73 -0
- package/dist/attack/policy.js.map +1 -0
- package/dist/attack/protocol.d.ts +64 -0
- package/dist/attack/protocol.js +3 -0
- package/dist/attack/protocol.js.map +1 -0
- package/dist/attack/report.d.ts +16 -0
- package/dist/attack/report.js +54 -0
- package/dist/attack/report.js.map +1 -0
- package/dist/auth/credentialStore.d.ts +20 -0
- package/dist/auth/credentialStore.js +148 -0
- package/dist/auth/credentialStore.js.map +1 -0
- package/dist/auth/keychain.d.ts +14 -0
- package/dist/auth/keychain.js +267 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +170 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +183 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.js +120 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/types.d.ts +26 -0
- package/dist/mcp/types.js +3 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/project-map/astHelpers.d.ts +89 -0
- package/dist/project-map/astHelpers.js +468 -0
- package/dist/project-map/astHelpers.js.map +1 -0
- package/dist/project-map/cache.d.ts +11 -0
- package/dist/project-map/cache.js +86 -0
- package/dist/project-map/cache.js.map +1 -0
- package/dist/project-map/confidence.d.ts +50 -0
- package/dist/project-map/confidence.js +87 -0
- package/dist/project-map/confidence.js.map +1 -0
- package/dist/project-map/crossFile.d.ts +73 -0
- package/dist/project-map/crossFile.js +33 -0
- package/dist/project-map/crossFile.js.map +1 -0
- package/dist/project-map/detectors.d.ts +83 -0
- package/dist/project-map/detectors.js +376 -0
- package/dist/project-map/detectors.js.map +1 -0
- package/dist/project-map/layer1.d.ts +74 -0
- package/dist/project-map/layer1.js +928 -0
- package/dist/project-map/layer1.js.map +1 -0
- package/dist/project-map/layer2.d.ts +30 -0
- package/dist/project-map/layer2.js +249 -0
- package/dist/project-map/layer2.js.map +1 -0
- package/dist/project-map/mapBuilder.d.ts +17 -0
- package/dist/project-map/mapBuilder.js +164 -0
- package/dist/project-map/mapBuilder.js.map +1 -0
- package/dist/project-map/parserLoader.d.ts +61 -0
- package/dist/project-map/parserLoader.js +175 -0
- package/dist/project-map/parserLoader.js.map +1 -0
- package/dist/project-map/schemas.d.ts +28 -0
- package/dist/project-map/schemas.js +131 -0
- package/dist/project-map/schemas.js.map +1 -0
- package/dist/project-map/types.d.ts +232 -0
- package/dist/project-map/types.js +61 -0
- package/dist/project-map/types.js.map +1 -0
- package/dist/tools/attack.d.ts +2 -0
- package/dist/tools/attack.js +80 -0
- package/dist/tools/attack.js.map +1 -0
- package/dist/tools/fix.d.ts +2 -0
- package/dist/tools/fix.js +60 -0
- package/dist/tools/fix.js.map +1 -0
- package/dist/tools/map.d.ts +2 -0
- package/dist/tools/map.js +46 -0
- package/dist/tools/map.js.map +1 -0
- package/dist/tools/scan.d.ts +2 -0
- package/dist/tools/scan.js +55 -0
- package/dist/tools/scan.js.map +1 -0
- package/dist/utils/files.d.ts +8 -0
- package/dist/utils/files.js +79 -0
- package/dist/utils/files.js.map +1 -0
- package/package.json +53 -0
- package/resources/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-python.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-typescript.wasm +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 4 — web-tree-sitter loader + WASM grammar resolver.
|
|
4
|
+
*
|
|
5
|
+
* web-tree-sitter ships a Node ESM/CJS entry plus language WASM blobs.
|
|
6
|
+
* In a VS Code extension the compiled JS runs from dist/, so the WASM
|
|
7
|
+
* files MUST be co-located somewhere we can resolve at runtime. We bundle
|
|
8
|
+
* the grammars under `resources/tree-sitter/` (see
|
|
9
|
+
* `resources/tree-sitter/README.md` for the vendoring recipe) and resolve
|
|
10
|
+
* them with an absolute path computed from the extension install dir.
|
|
11
|
+
*
|
|
12
|
+
* Loading is lazy and memoized: grammars are ~1-3 MB each and we only
|
|
13
|
+
* need the one matching the file being parsed.
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.loadLanguage = loadLanguage;
|
|
50
|
+
exports.parseSource = parseSource;
|
|
51
|
+
exports.nodeText = nodeText;
|
|
52
|
+
exports.grammarForFile = grammarForFile;
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
let _tsPromise = null;
|
|
56
|
+
const _languageCache = new Map();
|
|
57
|
+
let _parserCache = null;
|
|
58
|
+
/**
|
|
59
|
+
* Locate the directory holding the bundled WASM grammars.
|
|
60
|
+
*
|
|
61
|
+
* Standalone MCP: resolve from the package root (dist/project-map -> ../../resources/tree-sitter).
|
|
62
|
+
* Override with SECURECODE_GRAMMAR_DIR for custom installs.
|
|
63
|
+
*/
|
|
64
|
+
function grammarDir() {
|
|
65
|
+
if (process.env.SECURECODE_GRAMMAR_DIR) {
|
|
66
|
+
return process.env.SECURECODE_GRAMMAR_DIR;
|
|
67
|
+
}
|
|
68
|
+
// __dirname when compiled is <pkg>/dist/project-map. Two levels up.
|
|
69
|
+
const here = __dirname;
|
|
70
|
+
const pkgRoot = path.resolve(here, '..', '..');
|
|
71
|
+
return path.join(pkgRoot, 'resources', 'tree-sitter');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Lazy-load and initialise web-tree-sitter (Node CJS entry).
|
|
75
|
+
*
|
|
76
|
+
* The WASM runtime MUST be initialised before any Language.load() call. The
|
|
77
|
+
* initialiser moved between versions: >= 0.26 exposes a static `Parser.init()`,
|
|
78
|
+
* while <= 0.25 exposed a module-level `init()`. We try both, in that order.
|
|
79
|
+
* Getting this wrong is silent — Language.load() rejects and the Project Map
|
|
80
|
+
* degrades to producing nothing — so both shapes are handled explicitly.
|
|
81
|
+
*/
|
|
82
|
+
function loadTreeSitter() {
|
|
83
|
+
if (_tsPromise)
|
|
84
|
+
return _tsPromise;
|
|
85
|
+
_tsPromise = (async () => {
|
|
86
|
+
// web-tree-sitter's package "main" is the CJS bundle that works in Node.
|
|
87
|
+
const mod = require('web-tree-sitter');
|
|
88
|
+
if (typeof mod.Parser?.init === 'function') {
|
|
89
|
+
await mod.Parser.init();
|
|
90
|
+
}
|
|
91
|
+
else if (typeof mod.init === 'function') {
|
|
92
|
+
await mod.init();
|
|
93
|
+
}
|
|
94
|
+
return mod;
|
|
95
|
+
})();
|
|
96
|
+
return _tsPromise;
|
|
97
|
+
}
|
|
98
|
+
/** Resolve the Language namespace across both export shapes. */
|
|
99
|
+
function languageNamespace(ts) {
|
|
100
|
+
const ns = ts.Language ?? ts.Parser?.Language;
|
|
101
|
+
if (!ns || typeof ns.load !== 'function') {
|
|
102
|
+
throw new Error('SecureCode: web-tree-sitter exposes no Language.load()');
|
|
103
|
+
}
|
|
104
|
+
return ns;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Load a language grammar by short name. Memoized per language.
|
|
108
|
+
* Throws if the WASM file is missing — callers should catch and degrade
|
|
109
|
+
* gracefully (the Project Map is best-effort; a missing grammar must never
|
|
110
|
+
* break a scan).
|
|
111
|
+
*/
|
|
112
|
+
function loadLanguage(name) {
|
|
113
|
+
if (_languageCache.has(name))
|
|
114
|
+
return _languageCache.get(name);
|
|
115
|
+
const p = (async () => {
|
|
116
|
+
const ts = await loadTreeSitter();
|
|
117
|
+
const file = path.join(grammarDir(), `tree-sitter-${name}.wasm`);
|
|
118
|
+
if (!fs.existsSync(file)) {
|
|
119
|
+
throw new Error(`SecureCode: tree-sitter grammar missing: ${file}`);
|
|
120
|
+
}
|
|
121
|
+
return languageNamespace(ts).load(file);
|
|
122
|
+
})();
|
|
123
|
+
_languageCache.set(name, p);
|
|
124
|
+
return p;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parse source code with a grammar. Returns the root node or null when the
|
|
128
|
+
* grammar is unavailable (best-effort: callers must handle null).
|
|
129
|
+
*/
|
|
130
|
+
async function parseSource(source, language) {
|
|
131
|
+
try {
|
|
132
|
+
const ts = await loadTreeSitter();
|
|
133
|
+
const lang = await loadLanguage(language);
|
|
134
|
+
if (!_parserCache) {
|
|
135
|
+
_parserCache = new ts.Parser();
|
|
136
|
+
}
|
|
137
|
+
const parser = _parserCache;
|
|
138
|
+
parser.setLanguage(lang);
|
|
139
|
+
const tree = parser.parse(source);
|
|
140
|
+
// >= 0.26 exposes `rootNode` as a property; <= 0.25 had getRootNode().
|
|
141
|
+
const root = tree.rootNode ?? tree.getRootNode?.();
|
|
142
|
+
if (!root) {
|
|
143
|
+
throw new Error('SecureCode: tree-sitter returned no root node');
|
|
144
|
+
}
|
|
145
|
+
return { tree, root };
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
// Best-effort: never let a parser failure kill the scan.
|
|
149
|
+
console.warn('SecureCode: tree-sitter parse failed:', err);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Convenience: extract the source text for a node using the original source
|
|
155
|
+
* string. Tree-sitter nodes don't always carry .text (depends on version).
|
|
156
|
+
*/
|
|
157
|
+
function nodeText(node, source) {
|
|
158
|
+
if (node.text !== undefined)
|
|
159
|
+
return node.text;
|
|
160
|
+
return source.slice(node.startIndex, node.endIndex);
|
|
161
|
+
}
|
|
162
|
+
/** Pick a grammar for a file path. */
|
|
163
|
+
function grammarForFile(filePath) {
|
|
164
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
165
|
+
if (ext === '.ts')
|
|
166
|
+
return 'typescript';
|
|
167
|
+
if (ext === '.tsx')
|
|
168
|
+
return 'tsx';
|
|
169
|
+
if (ext === '.py')
|
|
170
|
+
return 'python';
|
|
171
|
+
if (ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs')
|
|
172
|
+
return 'javascript';
|
|
173
|
+
return 'unknown';
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=parserLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parserLoader.js","sourceRoot":"","sources":["../../src/project-map/parserLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 4 — validator schema collection.
|
|
3
|
+
*
|
|
4
|
+
* Where a request body is declared by a schema (zod, Joi, yup, pydantic), the
|
|
5
|
+
* schema's fields ARE the endpoint's parameters: they are the inputs that
|
|
6
|
+
* actually reach the handler, and they are frequently the only place the field
|
|
7
|
+
* names appear at all. `CreateUserSchema.parse(req.body)` never mentions
|
|
8
|
+
* `email` or `name`, so a params pass that only looks for `req.body.<name>`
|
|
9
|
+
* reads reports an endpoint with no inputs.
|
|
10
|
+
*/
|
|
11
|
+
import { TreeSitterNode } from './parserLoader';
|
|
12
|
+
import { ValidatorLibrary } from './types';
|
|
13
|
+
/** A validation schema declared in a file, keyed by its local variable name. */
|
|
14
|
+
export interface SchemaDef {
|
|
15
|
+
/** Field names the schema declares. */
|
|
16
|
+
fields: string[];
|
|
17
|
+
library: ValidatorLibrary;
|
|
18
|
+
}
|
|
19
|
+
/** Methods that run a schema against a value: `Schema.parse(req.body)`. */
|
|
20
|
+
export declare const SCHEMA_VALIDATION_METHODS: Set<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Collect every validation schema declared in a file.
|
|
23
|
+
*
|
|
24
|
+
* JS/TS: `const S = z.object({...})` / `Joi.object({...})` / `yup.object({...})`.
|
|
25
|
+
* Python: `class S(BaseModel): ...` — pydantic models, including subclasses of
|
|
26
|
+
* a model already seen in this file.
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectSchemas(root: TreeSitterNode, source: string, imports: Map<string, string>): Map<string, SchemaDef>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 4 — validator schema collection.
|
|
4
|
+
*
|
|
5
|
+
* Where a request body is declared by a schema (zod, Joi, yup, pydantic), the
|
|
6
|
+
* schema's fields ARE the endpoint's parameters: they are the inputs that
|
|
7
|
+
* actually reach the handler, and they are frequently the only place the field
|
|
8
|
+
* names appear at all. `CreateUserSchema.parse(req.body)` never mentions
|
|
9
|
+
* `email` or `name`, so a params pass that only looks for `req.body.<name>`
|
|
10
|
+
* reads reports an endpoint with no inputs.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.SCHEMA_VALIDATION_METHODS = void 0;
|
|
14
|
+
exports.collectSchemas = collectSchemas;
|
|
15
|
+
const astHelpers_1 = require("./astHelpers");
|
|
16
|
+
/** Builder receivers that produce an object schema, mapped to their library. */
|
|
17
|
+
const SCHEMA_BUILDERS = [
|
|
18
|
+
{ library: 'zod', receivers: /^z$|^zod$/, methods: new Set(['object', 'strictObject']) },
|
|
19
|
+
{ library: 'joi', receivers: /^joi$/i, methods: new Set(['object']) },
|
|
20
|
+
{ library: 'yup', receivers: /^yup$/, methods: new Set(['object', 'shape']) },
|
|
21
|
+
];
|
|
22
|
+
/** Methods that run a schema against a value: `Schema.parse(req.body)`. */
|
|
23
|
+
exports.SCHEMA_VALIDATION_METHODS = new Set([
|
|
24
|
+
'parse', 'safeParse', 'parseAsync', 'safeParseAsync',
|
|
25
|
+
'validate', 'validateAsync', 'validateSync',
|
|
26
|
+
'cast', 'load', 'model_validate', 'parse_obj',
|
|
27
|
+
]);
|
|
28
|
+
/** Field names declared by a JS object literal: `{ email: ..., name: ... }`. */
|
|
29
|
+
function objectLiteralFields(node, source) {
|
|
30
|
+
const fields = [];
|
|
31
|
+
for (const child of node.namedChildren) {
|
|
32
|
+
if (child.type !== 'pair')
|
|
33
|
+
continue;
|
|
34
|
+
const key = child.child(0);
|
|
35
|
+
if (!key)
|
|
36
|
+
continue;
|
|
37
|
+
const text = source.slice(key.startIndex, key.endIndex);
|
|
38
|
+
fields.push((0, astHelpers_1.isStringLiteral)(key) ? (0, astHelpers_1.stringLiteralValue)(key, source) : text);
|
|
39
|
+
}
|
|
40
|
+
return fields;
|
|
41
|
+
}
|
|
42
|
+
/** Field names declared by a pydantic model body: `email: EmailStr`. */
|
|
43
|
+
function pydanticModelFields(classNode, source) {
|
|
44
|
+
const body = classNode.namedChildren.find(c => c.type === 'block');
|
|
45
|
+
if (!body)
|
|
46
|
+
return [];
|
|
47
|
+
const fields = [];
|
|
48
|
+
for (const stmt of body.namedChildren) {
|
|
49
|
+
for (const n of stmt.type === 'expression_statement' ? stmt.namedChildren : [stmt]) {
|
|
50
|
+
if (n.type !== 'assignment')
|
|
51
|
+
continue;
|
|
52
|
+
const target = n.child(0);
|
|
53
|
+
if (target && (0, astHelpers_1.isIdentifier)(target)) {
|
|
54
|
+
fields.push(source.slice(target.startIndex, target.endIndex));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return fields;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Collect every validation schema declared in a file.
|
|
62
|
+
*
|
|
63
|
+
* JS/TS: `const S = z.object({...})` / `Joi.object({...})` / `yup.object({...})`.
|
|
64
|
+
* Python: `class S(BaseModel): ...` — pydantic models, including subclasses of
|
|
65
|
+
* a model already seen in this file.
|
|
66
|
+
*/
|
|
67
|
+
function collectSchemas(root, source, imports) {
|
|
68
|
+
const out = new Map();
|
|
69
|
+
for (const n of (0, astHelpers_1.walk)(root)) {
|
|
70
|
+
// --- JS/TS builder calls -------------------------------------------
|
|
71
|
+
if (n.type === 'variable_declarator') {
|
|
72
|
+
const name = n.child(0);
|
|
73
|
+
const value = n.child(n.childCount - 1);
|
|
74
|
+
if (!name || !value || !(0, astHelpers_1.isIdentifier)(name))
|
|
75
|
+
continue;
|
|
76
|
+
if (value.type !== 'call_expression')
|
|
77
|
+
continue;
|
|
78
|
+
const def = schemaFromBuilderCall(value, source, imports);
|
|
79
|
+
if (def)
|
|
80
|
+
out.set(source.slice(name.startIndex, name.endIndex), def);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// --- Python pydantic models ----------------------------------------
|
|
84
|
+
if (n.type === 'class_definition') {
|
|
85
|
+
const nameNode = n.namedChildren.find(c => (0, astHelpers_1.isIdentifier)(c));
|
|
86
|
+
const bases = n.namedChildren.find(c => c.type === 'argument_list');
|
|
87
|
+
if (!nameNode || !bases)
|
|
88
|
+
continue;
|
|
89
|
+
const baseText = source.slice(bases.startIndex, bases.endIndex);
|
|
90
|
+
const inheritsModel = /\bBaseModel\b/.test(baseText)
|
|
91
|
+
|| [...out.keys()].some(k => new RegExp(`\\b${k}\\b`).test(baseText));
|
|
92
|
+
if (!inheritsModel)
|
|
93
|
+
continue;
|
|
94
|
+
const fields = pydanticModelFields(n, source);
|
|
95
|
+
if (fields.length === 0)
|
|
96
|
+
continue;
|
|
97
|
+
out.set(source.slice(nameNode.startIndex, nameNode.endIndex), {
|
|
98
|
+
fields,
|
|
99
|
+
library: 'pydantic',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/** Recognise `z.object({...})` / `Joi.object({...})` and read its fields. */
|
|
106
|
+
function schemaFromBuilderCall(call, source, imports) {
|
|
107
|
+
const fn = call.child(0);
|
|
108
|
+
if (!fn || (fn.type !== 'member_expression' && fn.type !== 'attribute'))
|
|
109
|
+
return null;
|
|
110
|
+
const obj = fn.child(0);
|
|
111
|
+
const prop = fn.child(fn.childCount - 1);
|
|
112
|
+
if (!obj || !prop)
|
|
113
|
+
return null;
|
|
114
|
+
const receiver = (0, astHelpers_1.baseIdentifier)(obj, source);
|
|
115
|
+
if (!receiver)
|
|
116
|
+
return null;
|
|
117
|
+
const method = source.slice(prop.startIndex, prop.endIndex);
|
|
118
|
+
const spec = imports.get(receiver) ?? '';
|
|
119
|
+
const builder = SCHEMA_BUILDERS.find(b => b.methods.has(method) && (b.receivers.test(receiver) || b.receivers.test(spec)));
|
|
120
|
+
if (!builder)
|
|
121
|
+
return null;
|
|
122
|
+
const args = call.child(call.childCount - 1);
|
|
123
|
+
const literal = args?.namedChildren.find(c => c.type === 'object');
|
|
124
|
+
if (!literal)
|
|
125
|
+
return null;
|
|
126
|
+
const fields = objectLiteralFields(literal, source);
|
|
127
|
+
if (fields.length === 0)
|
|
128
|
+
return null;
|
|
129
|
+
return { fields, library: builder.library };
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/project-map/schemas.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAgEH,wCAsCC;AAnGD,6CAAuG;AAUvG,gFAAgF;AAChF,MAAM,eAAe,GAA6E;IAC9F,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,EAAE;IACxF,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE;IACrE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE;CAChF,CAAC;AAEF,2EAA2E;AAC9D,QAAA,yBAAyB,GAAG,IAAI,GAAG,CAAC;IAC7C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB;IACpD,UAAU,EAAE,eAAe,EAAE,cAAc;IAC3C,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,WAAW;CAChD,CAAC,CAAC;AAEH,gFAAgF;AAChF,SAAS,mBAAmB,CAAC,IAAoB,EAAE,MAAc;IAC7D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QACpC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,IAAA,4BAAe,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAA,+BAAkB,EAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,wEAAwE;AACxE,SAAS,mBAAmB,CAAC,SAAyB,EAAE,MAAc;IAClE,MAAM,IAAI,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IACnE,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACjF,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;gBAAE,SAAS;YACtC,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,MAAM,IAAI,IAAA,yBAAY,EAAC,MAAM,CAAC,EAAE,CAAC;gBACjC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClE,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAC1B,IAAoB,EACpB,MAAc,EACd,OAA4B;IAE5B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAqB,CAAC;IAEzC,KAAK,MAAM,CAAC,IAAI,IAAA,iBAAI,EAAC,IAAI,CAAC,EAAE,CAAC;QACzB,sEAAsE;QACtE,IAAI,CAAC,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;YACxC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAA,yBAAY,EAAC,IAAI,CAAC;gBAAE,SAAS;YACrD,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB;gBAAE,SAAS;YAC/C,MAAM,GAAG,GAAG,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAC1D,IAAI,GAAG;gBAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;YACpE,SAAS;QACb,CAAC;QAED,sEAAsE;QACtE,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAA,yBAAY,EAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,KAAK,GAAG,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC;YACpE,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK;gBAAE,SAAS;YAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;YAChE,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;mBAC7C,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC1E,IAAI,CAAC,aAAa;gBAAE,SAAS;YAC7B,MAAM,MAAM,GAAG,mBAAmB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAC9C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAClC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE;gBAC1D,MAAM;gBACN,OAAO,EAAE,UAAU;aACtB,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC;AACf,CAAC;AAED,6EAA6E;AAC7E,SAAS,qBAAqB,CAC1B,IAAoB,EACpB,MAAc,EACd,OAA4B;IAE5B,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,mBAAmB,IAAI,EAAE,CAAC,IAAI,KAAK,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IACrF,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAA,2BAAc,EAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE5D,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CACrC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrF,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IACnE,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 4 Project Map — shared types.
|
|
3
|
+
*
|
|
4
|
+
* The Project Map is a local, Tree-sitter-derived model of every HTTP
|
|
5
|
+
* endpoint in the workspace plus the cross-file context that protects it
|
|
6
|
+
* (middleware chain, validators, auth, ORM/data layer). It is what lets the
|
|
7
|
+
* Juror reason about whether a route is actually defended, and what lets
|
|
8
|
+
* the Attacker build endpoint-specific scenarios instead of generic ones.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here ever leaves the machine: only the derived `EndpointContext`
|
|
11
|
+
* for the file under scan is attached to the scan payload, and that is the
|
|
12
|
+
* ONLY new field the API contract gains (optional; ignored by today's API).
|
|
13
|
+
*/
|
|
14
|
+
/** HTTP method, uppercased. */
|
|
15
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'ALL';
|
|
16
|
+
/** Where a parameter lives on the request. */
|
|
17
|
+
export type ParamSource = 'body' | 'query' | 'path' | 'header' | 'cookie' | 'unknown';
|
|
18
|
+
/** Auth scheme detected for an endpoint. */
|
|
19
|
+
export type AuthScheme = 'jwt' | 'session' | 'api-key' | 'oauth' | 'basic' | 'none' | 'unknown';
|
|
20
|
+
/** ORM / data layer detected for an endpoint. */
|
|
21
|
+
export type DataLayer = 'prisma' | 'typeorm' | 'sequelize' | 'knex' | 'mongoose' | 'raw-sql' | 'supabase' | 'sqlalchemy' | 'django-orm' | 'none' | 'unknown';
|
|
22
|
+
/** Validator library detected for a parameter or handler. */
|
|
23
|
+
export type ValidatorLibrary = 'zod' | 'joi' | 'express-validator' | 'yup' | 'ajv' | 'pydantic' | 'marshmallow' | 'none' | 'unknown';
|
|
24
|
+
/** Response shape detected for an endpoint. */
|
|
25
|
+
export type ResponseShape = 'json' | 'html' | 'redirect' | 'stream' | 'file' | 'text' | 'unknown';
|
|
26
|
+
/**
|
|
27
|
+
* Confidence scores per the README Phase 4 spec.
|
|
28
|
+
*
|
|
29
|
+
* 1.0 — static literal (string-literal path, directly-registered middleware)
|
|
30
|
+
* 0.8 — runtime-confirmed (trace hook observed the relationship)
|
|
31
|
+
* 0.6 — intra-file dataflow (resolved from a same-file const/variable)
|
|
32
|
+
* 0.2 — unresolved (could not statically resolve; not yet traced)
|
|
33
|
+
* 0.15 — meta-programming (new Function / vm.runIn* / dynamic require)
|
|
34
|
+
* 0.1 — fully unresolved, no usable evidence
|
|
35
|
+
* 0.0 — dynamic import pattern (D1-D10), flagged but un-confirmed
|
|
36
|
+
*/
|
|
37
|
+
export declare const Confidence: {
|
|
38
|
+
readonly STATIC_LITERAL: 1;
|
|
39
|
+
readonly RUNTIME_CONFIRMED: 0.8;
|
|
40
|
+
readonly INTRA_FILE_DATAFLOW: 0.6;
|
|
41
|
+
readonly UNRESOLVED: 0.2;
|
|
42
|
+
readonly META_PROGRAMMING: 0.15;
|
|
43
|
+
readonly FULLY_UNRESOLVED: 0.1;
|
|
44
|
+
readonly DYNAMIC_PATTERN: 0;
|
|
45
|
+
};
|
|
46
|
+
/** A single parameter on an endpoint. */
|
|
47
|
+
export interface EndpointParam {
|
|
48
|
+
name: string;
|
|
49
|
+
/** Best-effort type annotation as a string ('string', 'number', 'User', 'unknown'). */
|
|
50
|
+
type: string;
|
|
51
|
+
source: ParamSource;
|
|
52
|
+
/** True iff a validator call covers this param. */
|
|
53
|
+
validated: boolean;
|
|
54
|
+
/** Which validator library was detected, when validated. */
|
|
55
|
+
validator?: ValidatorLibrary;
|
|
56
|
+
/** Confidence that this param actually exists at this source. */
|
|
57
|
+
confidence: number;
|
|
58
|
+
}
|
|
59
|
+
/** A middleware entry in an endpoint's chain (registration order). */
|
|
60
|
+
export interface MiddlewareEntry {
|
|
61
|
+
/** Local name of the middleware function or import. */
|
|
62
|
+
name: string;
|
|
63
|
+
/** File where the middleware is defined or imported (workspace-relative). */
|
|
64
|
+
sourceFile: string;
|
|
65
|
+
/** Line where it was registered (app.use(...) or app.get('/', mw, handler)). */
|
|
66
|
+
registrationLine: number;
|
|
67
|
+
/** Confidence that this middleware actually applies to this endpoint. */
|
|
68
|
+
confidence: number;
|
|
69
|
+
}
|
|
70
|
+
/** One node in the handler's call graph (a function the handler invokes). */
|
|
71
|
+
export interface CallGraphNode {
|
|
72
|
+
/** Local name of the called function. */
|
|
73
|
+
name: string;
|
|
74
|
+
/** File where the called function is defined, when resolvable. */
|
|
75
|
+
calleeFile?: string;
|
|
76
|
+
/** Line of the call site. */
|
|
77
|
+
callLine: number;
|
|
78
|
+
/** Confidence that this call actually happens. */
|
|
79
|
+
confidence: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The full per-endpoint record — one of these is produced for every
|
|
83
|
+
* `app.get('/x', handler)` / `@app.get('/x')` / `@router.get('/x')` /
|
|
84
|
+
* `@app.route('/x')` / Fastify `.get('/x', handler)` / Koa `router.get(...)`
|
|
85
|
+
* / FastAPI `@app.get('/x')` / Django `path('x', view)` site discovered.
|
|
86
|
+
*/
|
|
87
|
+
export interface EndpointRecord {
|
|
88
|
+
/** Stable id: `${file}:${line}:${method}:${path}`. */
|
|
89
|
+
id: string;
|
|
90
|
+
method: HttpMethod;
|
|
91
|
+
/**
|
|
92
|
+
* Route path AS REGISTERED at the route site (string literal when
|
|
93
|
+
* available; '?' when dynamic). For a router mounted under a prefix in
|
|
94
|
+
* another file this stays router-local — see `mountedPath`.
|
|
95
|
+
*/
|
|
96
|
+
path: string;
|
|
97
|
+
/**
|
|
98
|
+
* Full externally-visible path, when the router carrying this route is
|
|
99
|
+
* mounted under a prefix somewhere else (`/api/users` + `/:id`).
|
|
100
|
+
*
|
|
101
|
+
* Kept alongside `path` rather than replacing it: consumers key off the
|
|
102
|
+
* as-registered path to line up with the source, while an attacker or a
|
|
103
|
+
* trace needs the URL that is actually reachable. Absent when the router
|
|
104
|
+
* is not mounted anywhere we can see.
|
|
105
|
+
*/
|
|
106
|
+
mountedPath?: string;
|
|
107
|
+
/** Handler function local name (or '<anonymous>'). */
|
|
108
|
+
handlerName: string;
|
|
109
|
+
/** Workspace-relative source file of the handler. */
|
|
110
|
+
sourceFile: string;
|
|
111
|
+
/** 1-indexed line of the handler registration / decorator. */
|
|
112
|
+
line: number;
|
|
113
|
+
/** Middleware chain in registration order (global app.use first, then route mw). */
|
|
114
|
+
middleware: MiddlewareEntry[];
|
|
115
|
+
/** Parameters extracted from the handler signature + validators. */
|
|
116
|
+
params: EndpointParam[];
|
|
117
|
+
authScheme: AuthScheme;
|
|
118
|
+
dataLayer: DataLayer;
|
|
119
|
+
validatorLibrary: ValidatorLibrary;
|
|
120
|
+
callGraph: CallGraphNode[];
|
|
121
|
+
responseShape: ResponseShape;
|
|
122
|
+
/**
|
|
123
|
+
* Confidence that `path` is the path this endpoint is really registered
|
|
124
|
+
* at: 1.0 for a string literal or a filesystem-derived route, 0.6 when it
|
|
125
|
+
* came from a same-file constant, UNRESOLVED when it is '?'.
|
|
126
|
+
*
|
|
127
|
+
* Kept separate from `confidence` because it is the one piece of evidence
|
|
128
|
+
* that exists even for an endpoint with no middleware, params or calls —
|
|
129
|
+
* without it such a record would be indistinguishable from one where
|
|
130
|
+
* nothing at all could be extracted.
|
|
131
|
+
*/
|
|
132
|
+
pathConfidence?: number;
|
|
133
|
+
/** Aggregate confidence for the whole record (min of relationship confidences). */
|
|
134
|
+
confidence: number;
|
|
135
|
+
/** True iff a runtime trace confirmed this endpoint exists and was hit. */
|
|
136
|
+
runtimeConfirmed: boolean;
|
|
137
|
+
}
|
|
138
|
+
/** Layer 2 dynamic import pattern type (D1-D10). */
|
|
139
|
+
export type DynamicPatternType = 'D1' | 'D2' | 'D3' | 'D4' | 'D5' | 'D6' | 'D7' | 'D8' | 'D9' | 'D10';
|
|
140
|
+
/** A single dynamic import / meta-programming detection. */
|
|
141
|
+
export interface DynamicPattern {
|
|
142
|
+
type: DynamicPatternType;
|
|
143
|
+
file: string;
|
|
144
|
+
line: number;
|
|
145
|
+
/** Short code snippet for surfacing in the panel. */
|
|
146
|
+
snippet: string;
|
|
147
|
+
/** Initial confidence — always 0.0 per spec. */
|
|
148
|
+
confidence: number;
|
|
149
|
+
}
|
|
150
|
+
/** Per-file extraction output. */
|
|
151
|
+
export interface FileExtraction {
|
|
152
|
+
file: string;
|
|
153
|
+
language: 'javascript' | 'typescript' | 'tsx' | 'python' | 'unknown';
|
|
154
|
+
endpoints: EndpointRecord[];
|
|
155
|
+
dynamicPatterns: DynamicPattern[];
|
|
156
|
+
/** Imports resolved for cross-file use: name -> sourceFile (workspace-relative). */
|
|
157
|
+
imports: Record<string, string>;
|
|
158
|
+
/** mtimes/hash used by the cache. */
|
|
159
|
+
mtime: number;
|
|
160
|
+
hash: string;
|
|
161
|
+
}
|
|
162
|
+
/** The whole map. */
|
|
163
|
+
export interface ProjectMap {
|
|
164
|
+
/** Workspace-relative file -> FileExtraction. */
|
|
165
|
+
files: Record<string, FileExtraction>;
|
|
166
|
+
/** Flat endpoint list (denormalized for the panel). */
|
|
167
|
+
endpoints: EndpointRecord[];
|
|
168
|
+
/** Flat dynamic-pattern list. */
|
|
169
|
+
dynamicPatterns: DynamicPattern[];
|
|
170
|
+
/** Schema version, bumped when the on-disk format changes. */
|
|
171
|
+
version: number;
|
|
172
|
+
/** Last full rebuild timestamp (ms). */
|
|
173
|
+
builtAt: number;
|
|
174
|
+
}
|
|
175
|
+
/** Schema version for the on-disk cache. Bump to invalidate old caches. */
|
|
176
|
+
export declare const PROJECT_MAP_SCHEMA_VERSION = 1;
|
|
177
|
+
/**
|
|
178
|
+
* EndpointContext — the slice of the Project Map that travels with a
|
|
179
|
+
* scan. This is the ONLY new field added to the scan payload, and the API
|
|
180
|
+
* contract is unchanged (today's API ignores it; Phase 5 consumes it).
|
|
181
|
+
*
|
|
182
|
+
* It captures everything the Juror needs to reason about whether the
|
|
183
|
+
* middleware on this route actually defends the sink being scanned:
|
|
184
|
+
* the middleware chain (with source files), the parameters and their
|
|
185
|
+
* validation status, the auth scheme, the ORM/data layer, the handler
|
|
186
|
+
* call graph, and the confidence per relationship.
|
|
187
|
+
*/
|
|
188
|
+
export interface EndpointContext {
|
|
189
|
+
/** Stable id: `${sourceFile}:${line}:${method}:${path}`. */
|
|
190
|
+
id?: string;
|
|
191
|
+
method: HttpMethod;
|
|
192
|
+
path: string;
|
|
193
|
+
/** Full externally-visible path when the router is mounted under a prefix. */
|
|
194
|
+
mountedPath?: string;
|
|
195
|
+
handlerName: string;
|
|
196
|
+
sourceFile: string;
|
|
197
|
+
line: number;
|
|
198
|
+
middleware: MiddlewareEntry[];
|
|
199
|
+
params: EndpointParam[];
|
|
200
|
+
authScheme: AuthScheme;
|
|
201
|
+
dataLayer: DataLayer;
|
|
202
|
+
validatorLibrary: ValidatorLibrary;
|
|
203
|
+
callGraph: CallGraphNode[];
|
|
204
|
+
responseShape: ResponseShape;
|
|
205
|
+
confidence: number;
|
|
206
|
+
runtimeConfirmed: boolean;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* How a related file relates to the file being scanned.
|
|
210
|
+
*
|
|
211
|
+
* Mirrors `FileRelationship` in the API's `src/types/scanTypes.ts` exactly.
|
|
212
|
+
* The API renders the label verbatim into the Scout/Juror/Fixer prompts, so a
|
|
213
|
+
* value outside this union is not a type error on the wire — it is a prompt
|
|
214
|
+
* that says something the model has never been told how to read.
|
|
215
|
+
*/
|
|
216
|
+
export type FileRelationship = 'imports' | 'imported_by' | 'route_handler' | 'shared_type' | 'config' | 'middleware';
|
|
217
|
+
/**
|
|
218
|
+
* A neighbouring source file shipped alongside the scanned file so the model
|
|
219
|
+
* can see what the file under scan depends on.
|
|
220
|
+
*
|
|
221
|
+
* Travels as `workspaceHints.relatedFiles`, which the API already consumes:
|
|
222
|
+
* `formatRelatedFilesContext` (api/src/services/contextService.ts) folds it
|
|
223
|
+
* into the Scout, Juror and Fixer prompts.
|
|
224
|
+
*/
|
|
225
|
+
export interface RelatedFile {
|
|
226
|
+
/** Workspace-relative, POSIX separators — the label the model sees. */
|
|
227
|
+
filePath: string;
|
|
228
|
+
content: string;
|
|
229
|
+
relationship: FileRelationship;
|
|
230
|
+
}
|
|
231
|
+
/** Convert an EndpointRecord into the serializable EndpointContext. */
|
|
232
|
+
export declare function toEndpointContext(r: EndpointRecord): EndpointContext;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 4 Project Map — shared types.
|
|
4
|
+
*
|
|
5
|
+
* The Project Map is a local, Tree-sitter-derived model of every HTTP
|
|
6
|
+
* endpoint in the workspace plus the cross-file context that protects it
|
|
7
|
+
* (middleware chain, validators, auth, ORM/data layer). It is what lets the
|
|
8
|
+
* Juror reason about whether a route is actually defended, and what lets
|
|
9
|
+
* the Attacker build endpoint-specific scenarios instead of generic ones.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here ever leaves the machine: only the derived `EndpointContext`
|
|
12
|
+
* for the file under scan is attached to the scan payload, and that is the
|
|
13
|
+
* ONLY new field the API contract gains (optional; ignored by today's API).
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.PROJECT_MAP_SCHEMA_VERSION = exports.Confidence = void 0;
|
|
17
|
+
exports.toEndpointContext = toEndpointContext;
|
|
18
|
+
/**
|
|
19
|
+
* Confidence scores per the README Phase 4 spec.
|
|
20
|
+
*
|
|
21
|
+
* 1.0 — static literal (string-literal path, directly-registered middleware)
|
|
22
|
+
* 0.8 — runtime-confirmed (trace hook observed the relationship)
|
|
23
|
+
* 0.6 — intra-file dataflow (resolved from a same-file const/variable)
|
|
24
|
+
* 0.2 — unresolved (could not statically resolve; not yet traced)
|
|
25
|
+
* 0.15 — meta-programming (new Function / vm.runIn* / dynamic require)
|
|
26
|
+
* 0.1 — fully unresolved, no usable evidence
|
|
27
|
+
* 0.0 — dynamic import pattern (D1-D10), flagged but un-confirmed
|
|
28
|
+
*/
|
|
29
|
+
exports.Confidence = {
|
|
30
|
+
STATIC_LITERAL: 1.0,
|
|
31
|
+
RUNTIME_CONFIRMED: 0.8,
|
|
32
|
+
INTRA_FILE_DATAFLOW: 0.6,
|
|
33
|
+
UNRESOLVED: 0.2,
|
|
34
|
+
META_PROGRAMMING: 0.15,
|
|
35
|
+
FULLY_UNRESOLVED: 0.1,
|
|
36
|
+
DYNAMIC_PATTERN: 0.0,
|
|
37
|
+
};
|
|
38
|
+
/** Schema version for the on-disk cache. Bump to invalidate old caches. */
|
|
39
|
+
exports.PROJECT_MAP_SCHEMA_VERSION = 1;
|
|
40
|
+
/** Convert an EndpointRecord into the serializable EndpointContext. */
|
|
41
|
+
function toEndpointContext(r) {
|
|
42
|
+
return {
|
|
43
|
+
id: r.id,
|
|
44
|
+
method: r.method,
|
|
45
|
+
path: r.path,
|
|
46
|
+
mountedPath: r.mountedPath,
|
|
47
|
+
handlerName: r.handlerName,
|
|
48
|
+
sourceFile: r.sourceFile,
|
|
49
|
+
line: r.line,
|
|
50
|
+
middleware: r.middleware,
|
|
51
|
+
params: r.params,
|
|
52
|
+
authScheme: r.authScheme,
|
|
53
|
+
dataLayer: r.dataLayer,
|
|
54
|
+
validatorLibrary: r.validatorLibrary,
|
|
55
|
+
callGraph: r.callGraph,
|
|
56
|
+
responseShape: r.responseShape,
|
|
57
|
+
confidence: r.confidence,
|
|
58
|
+
runtimeConfirmed: r.runtimeConfirmed,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/project-map/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AA0RH,8CAmBC;AA9PD;;;;;;;;;;GAUG;AACU,QAAA,UAAU,GAAG;IACtB,cAAc,EAAE,GAAG;IACnB,iBAAiB,EAAE,GAAG;IACtB,mBAAmB,EAAE,GAAG;IACxB,UAAU,EAAE,GAAG;IACf,gBAAgB,EAAE,IAAI;IACtB,gBAAgB,EAAE,GAAG;IACrB,eAAe,EAAE,GAAG;CACd,CAAC;AAqJX,2EAA2E;AAC9D,QAAA,0BAA0B,GAAG,CAAC,CAAC;AAiE5C,uEAAuE;AACvE,SAAgB,iBAAiB,CAAC,CAAiB;IAC/C,OAAO;QACH,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,gBAAgB,EAAE,CAAC,CAAC,gBAAgB;QACpC,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,aAAa,EAAE,CAAC,CAAC,aAAa;QAC9B,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,gBAAgB,EAAE,CAAC,CAAC,gBAAgB;KACvC,CAAC;AACN,CAAC"}
|