@hookwarden/engine 0.0.1 → 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/dist/adapters/django.d.ts +4 -0
- package/dist/adapters/django.d.ts.map +1 -0
- package/dist/adapters/django.js +148 -0
- package/dist/adapters/django.js.map +1 -0
- package/dist/adapters/fastapi.d.ts +4 -0
- package/dist/adapters/fastapi.d.ts.map +1 -0
- package/dist/adapters/fastapi.js +118 -0
- package/dist/adapters/fastapi.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +10 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/nextjs.d.ts +4 -0
- package/dist/adapters/nextjs.d.ts.map +1 -0
- package/dist/adapters/nextjs.js +82 -0
- package/dist/adapters/nextjs.js.map +1 -0
- package/dist/evaluate.d.ts +6 -0
- package/dist/evaluate.d.ts.map +1 -0
- package/dist/evaluate.js +108 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/evaluator/index.d.ts +4 -0
- package/dist/evaluator/index.d.ts.map +1 -0
- package/dist/evaluator/index.js +4 -0
- package/dist/evaluator/index.js.map +1 -0
- package/dist/evaluator/matchers.d.ts +13 -0
- package/dist/evaluator/matchers.d.ts.map +1 -0
- package/dist/evaluator/matchers.js +124 -0
- package/dist/evaluator/matchers.js.map +1 -0
- package/dist/evaluator/parse-error.d.ts +4 -0
- package/dist/evaluator/parse-error.d.ts.map +1 -0
- package/dist/evaluator/parse-error.js +46 -0
- package/dist/evaluator/parse-error.js.map +1 -0
- package/dist/evaluator/path-severity-overrides.d.ts +4 -0
- package/dist/evaluator/path-severity-overrides.d.ts.map +1 -0
- package/dist/evaluator/path-severity-overrides.js +29 -0
- package/dist/evaluator/path-severity-overrides.js.map +1 -0
- package/dist/evaluator/visit.d.ts +16 -0
- package/dist/evaluator/visit.d.ts.map +1 -0
- package/dist/evaluator/visit.js +96 -0
- package/dist/evaluator/visit.js.map +1 -0
- package/dist/findings/fingerprint.d.ts +22 -0
- package/dist/findings/fingerprint.d.ts.map +1 -0
- package/dist/findings/fingerprint.js +39 -0
- package/dist/findings/fingerprint.js.map +1 -0
- package/dist/findings/index.d.ts +3 -0
- package/dist/findings/index.d.ts.map +1 -0
- package/dist/findings/index.js +4 -0
- package/dist/findings/index.js.map +1 -0
- package/dist/findings/webcrypto.d.ts +2 -0
- package/dist/findings/webcrypto.d.ts.map +1 -0
- package/dist/findings/webcrypto.js +15 -0
- package/dist/findings/webcrypto.js.map +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/model/build.d.ts +12 -0
- package/dist/model/build.d.ts.map +1 -0
- package/dist/model/build.js +154 -0
- package/dist/model/build.js.map +1 -0
- package/dist/model/catalog.d.ts +17 -0
- package/dist/model/catalog.d.ts.map +1 -0
- package/dist/model/catalog.js +303 -0
- package/dist/model/catalog.js.map +1 -0
- package/dist/model/evidence.d.ts +18 -0
- package/dist/model/evidence.d.ts.map +1 -0
- package/dist/model/evidence.js +114 -0
- package/dist/model/evidence.js.map +1 -0
- package/dist/model/index.d.ts +6 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +7 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/middleware.d.ts +10 -0
- package/dist/model/middleware.d.ts.map +1 -0
- package/dist/model/middleware.js +140 -0
- package/dist/model/middleware.js.map +1 -0
- package/dist/model/reachability.d.ts +11 -0
- package/dist/model/reachability.d.ts.map +1 -0
- package/dist/model/reachability.js +260 -0
- package/dist/model/reachability.js.map +1 -0
- package/dist/parsers/babel.d.ts +11 -0
- package/dist/parsers/babel.d.ts.map +1 -0
- package/dist/parsers/babel.js +121 -0
- package/dist/parsers/babel.js.map +1 -0
- package/dist/parsers/index.d.ts +6 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +7 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/literals.d.ts +4 -0
- package/dist/parsers/literals.d.ts.map +1 -0
- package/dist/parsers/literals.js +37 -0
- package/dist/parsers/literals.js.map +1 -0
- package/dist/parsers/python-literals.d.ts +5 -0
- package/dist/parsers/python-literals.d.ts.map +1 -0
- package/dist/parsers/python-literals.js +62 -0
- package/dist/parsers/python-literals.js.map +1 -0
- package/dist/parsers/python-loader.d.ts +9 -0
- package/dist/parsers/python-loader.d.ts.map +1 -0
- package/dist/parsers/python-loader.js +16 -0
- package/dist/parsers/python-loader.js.map +1 -0
- package/dist/parsers/python.d.ts +8 -0
- package/dist/parsers/python.d.ts.map +1 -0
- package/dist/parsers/python.js +125 -0
- package/dist/parsers/python.js.map +1 -0
- package/dist/parsers/walk.d.ts +15 -0
- package/dist/parsers/walk.d.ts.map +1 -0
- package/dist/parsers/walk.js +66 -0
- package/dist/parsers/walk.js.map +1 -0
- package/dist/redaction/index.d.ts +3 -0
- package/dist/redaction/index.d.ts.map +1 -0
- package/dist/redaction/index.js +2 -0
- package/dist/redaction/index.js.map +1 -0
- package/dist/redaction/structural.d.ts +14 -0
- package/dist/redaction/structural.d.ts.map +1 -0
- package/dist/redaction/structural.js +37 -0
- package/dist/redaction/structural.js.map +1 -0
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/finding.d.ts +32 -0
- package/dist/types/finding.d.ts.map +1 -0
- package/dist/types/finding.js +12 -0
- package/dist/types/finding.js.map +1 -0
- package/dist/types/handler.d.ts +39 -0
- package/dist/types/handler.d.ts.map +1 -0
- package/dist/types/handler.js +7 -0
- package/dist/types/handler.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/project-model.d.ts +42 -0
- package/dist/types/project-model.d.ts.map +1 -0
- package/dist/types/project-model.js +5 -0
- package/dist/types/project-model.js.map +1 -0
- package/dist/types/rule-set.d.ts +42 -0
- package/dist/types/rule-set.d.ts.map +1 -0
- package/dist/types/rule-set.js +6 -0
- package/dist/types/rule-set.js.map +1 -0
- package/dist/types/scan-result.d.ts +19 -0
- package/dist/types/scan-result.d.ts.map +1 -0
- package/dist/types/scan-result.js +8 -0
- package/dist/types/scan-result.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +12 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// buildProjectModel: orchestrates parsers' output into a ProjectModel that the evaluator (Plan 08)
|
|
2
|
+
// consumes. Async (D-02 — handler ids are WebCrypto sha256). Pure (D-01 — no fs/http).
|
|
3
|
+
//
|
|
4
|
+
// Wires together:
|
|
5
|
+
// - Plan 06a's detectCatalogHandlers + computeEvidence (6 of 7 D-32 signals)
|
|
6
|
+
// - Plan 07's bespoke adapters (Next.js / Django / FastAPI) via the bespokeAdapters hook
|
|
7
|
+
// - This plan's computeReachableSymbols (D-34 cross-file traversal) + extractMiddlewareChain (D-36)
|
|
8
|
+
// - The sdk_verify_call evidence overlay — completes D-32's 7th signal.
|
|
9
|
+
import { computeHandlerId } from "../findings/fingerprint.js";
|
|
10
|
+
import { extractBabelLiterals } from "../parsers/literals.js";
|
|
11
|
+
import { extractPythonLiterals } from "../parsers/python-literals.js";
|
|
12
|
+
import { redactSnippet } from "../redaction/structural.js";
|
|
13
|
+
import { detectCatalogHandlers } from "./catalog.js";
|
|
14
|
+
import { computeEvidence } from "./evidence.js";
|
|
15
|
+
import { extractMiddlewareChain } from "./middleware.js";
|
|
16
|
+
import { computeReachableSymbols } from "./reachability.js";
|
|
17
|
+
export async function buildProjectModel(input) {
|
|
18
|
+
// 1. Aggregate every file's imports → import_graph (engine-internal cross-file index).
|
|
19
|
+
const importGraph = [];
|
|
20
|
+
for (const file of input.parsedFiles) {
|
|
21
|
+
for (const edge of file.imports)
|
|
22
|
+
importGraph.push(edge);
|
|
23
|
+
}
|
|
24
|
+
// 2. Detect candidate handlers per file (catalog + bespoke). Skip parse-error files (D-27).
|
|
25
|
+
const candidates = [];
|
|
26
|
+
const adapters = input.bespokeAdapters ?? [];
|
|
27
|
+
for (const file of input.parsedFiles) {
|
|
28
|
+
if (file.parse_error !== null)
|
|
29
|
+
continue;
|
|
30
|
+
for (const cand of detectCatalogHandlers(file))
|
|
31
|
+
candidates.push({ cand, file });
|
|
32
|
+
for (const adapter of adapters) {
|
|
33
|
+
for (const cand of adapter(file, input.parsedFiles))
|
|
34
|
+
candidates.push({ cand, file });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// 3. For each candidate, compute id + evidence (with sdk_verify_call overlay) + reachability +
|
|
38
|
+
// middleware_chain + redacted snippet.
|
|
39
|
+
const handlers = [];
|
|
40
|
+
for (const { cand, file } of candidates) {
|
|
41
|
+
handlers.push(await assembleHandler(cand, file, input));
|
|
42
|
+
}
|
|
43
|
+
// 4. Aggregate middleware registrations at the project level (engine-internal index — Phase 8
|
|
44
|
+
// rule authors who need cross-handler middleware ordering can query this).
|
|
45
|
+
const middlewareRegistrations = [];
|
|
46
|
+
return {
|
|
47
|
+
parsed_files: input.parsedFiles,
|
|
48
|
+
handlers,
|
|
49
|
+
middleware_registrations: middlewareRegistrations,
|
|
50
|
+
import_graph: importGraph,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function assembleHandler(cand, file, input) {
|
|
54
|
+
const id = await computeHandlerId({
|
|
55
|
+
file_path: cand.file_path,
|
|
56
|
+
route_pattern: cand.route_pattern,
|
|
57
|
+
http_methods: cand.http_methods,
|
|
58
|
+
handler_function_name: cand.handler_function_name,
|
|
59
|
+
});
|
|
60
|
+
const baseEvidence = computeEvidence({
|
|
61
|
+
handler: cand,
|
|
62
|
+
parsedFile: file,
|
|
63
|
+
providerCatalog: input.ruleSet.providers,
|
|
64
|
+
imports: file.imports,
|
|
65
|
+
});
|
|
66
|
+
const reachableSymbols = computeReachableSymbols({
|
|
67
|
+
handler_body_node: cand.handler_body_node,
|
|
68
|
+
handler_file: file,
|
|
69
|
+
all_files: input.parsedFiles,
|
|
70
|
+
imports: file.imports,
|
|
71
|
+
maxDepth: input.config.reachability_max_depth,
|
|
72
|
+
});
|
|
73
|
+
const middlewareChain = extractMiddlewareChain({
|
|
74
|
+
handler: cand,
|
|
75
|
+
parsedFile: file,
|
|
76
|
+
imports: file.imports,
|
|
77
|
+
});
|
|
78
|
+
// sdk_verify_call evidence overlay (issue #7 fix) — completes D-32's 7th signal.
|
|
79
|
+
const sdkVerifyEvidence = collectSdkVerifyCallEvidence(cand, reachableSymbols, input.ruleSet);
|
|
80
|
+
const evidence = [...baseEvidence.evidence, ...sdkVerifyEvidence];
|
|
81
|
+
// Recompute provider attribution since sdk_verify_call evidence may shift the count.
|
|
82
|
+
const provider = recomputeProvider(evidence, baseEvidence.provider);
|
|
83
|
+
const redactedSnippet = renderHandlerSnippet(file, cand);
|
|
84
|
+
return {
|
|
85
|
+
id,
|
|
86
|
+
framework: cand.framework,
|
|
87
|
+
framework_version: cand.framework_version,
|
|
88
|
+
route_pattern: cand.route_pattern,
|
|
89
|
+
http_methods: cand.http_methods,
|
|
90
|
+
file_path: cand.file_path,
|
|
91
|
+
location: cand.location,
|
|
92
|
+
handler_function_name: cand.handler_function_name,
|
|
93
|
+
provider,
|
|
94
|
+
verification_state: "manual-review", // PITFALLS #3 default; Plan 08 evaluator promotes
|
|
95
|
+
evidence,
|
|
96
|
+
middleware_chain: middlewareChain,
|
|
97
|
+
reachable_symbols: reachableSymbols,
|
|
98
|
+
findings_ref: [], // back-populated by Plan 08 evaluator
|
|
99
|
+
redacted_snippet: redactedSnippet,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function collectSdkVerifyCallEvidence(cand, reachableSymbols, ruleSet) {
|
|
103
|
+
const out = [];
|
|
104
|
+
for (const [providerName, entry] of Object.entries(ruleSet.providers)) {
|
|
105
|
+
for (const verifyCall of entry.sdk_verify_calls) {
|
|
106
|
+
const matched = reachableSymbols.some((s) => s.qualified_name === verifyCall || s.qualified_name.endsWith(`.${verifyCall}`));
|
|
107
|
+
if (matched) {
|
|
108
|
+
out.push({
|
|
109
|
+
kind: "sdk_verify_call",
|
|
110
|
+
provider: providerName,
|
|
111
|
+
location: cand.location,
|
|
112
|
+
detail: verifyCall,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
function recomputeProvider(evidence, fallback) {
|
|
120
|
+
const counts = new Map();
|
|
121
|
+
for (const e of evidence) {
|
|
122
|
+
if (e.provider === "unknown")
|
|
123
|
+
continue;
|
|
124
|
+
counts.set(e.provider, (counts.get(e.provider) ?? 0) + 1);
|
|
125
|
+
}
|
|
126
|
+
let topProvider = "unknown";
|
|
127
|
+
let topCount = 0;
|
|
128
|
+
let tied = false;
|
|
129
|
+
for (const [p, c] of counts) {
|
|
130
|
+
if (c > topCount) {
|
|
131
|
+
topProvider = p;
|
|
132
|
+
topCount = c;
|
|
133
|
+
tied = false;
|
|
134
|
+
}
|
|
135
|
+
else if (c === topCount && c > 0) {
|
|
136
|
+
tied = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (topProvider === "unknown")
|
|
140
|
+
return fallback;
|
|
141
|
+
return tied ? "multiple" : topProvider;
|
|
142
|
+
}
|
|
143
|
+
function renderHandlerSnippet(file, cand) {
|
|
144
|
+
const slice = file.source_text.slice(cand.handler_source_start, cand.handler_source_end);
|
|
145
|
+
const offset = cand.handler_source_start;
|
|
146
|
+
const allLiterals = file.dialect === "babel"
|
|
147
|
+
? extractBabelLiterals(file.raw_ast)
|
|
148
|
+
: extractPythonLiterals(file.raw_ast);
|
|
149
|
+
const sliceLiterals = allLiterals
|
|
150
|
+
.filter((l) => l.start >= cand.handler_source_start && l.end <= cand.handler_source_end)
|
|
151
|
+
.map((l) => ({ ...l, start: l.start - offset, end: l.end - offset }));
|
|
152
|
+
return redactSnippet({ source_text: slice, literals: sliceLiterals });
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/model/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SourceLocation } from "../types/finding.js";
|
|
2
|
+
import type { Framework } from "../types/handler.js";
|
|
3
|
+
import type { ParsedFile } from "../types/project-model.js";
|
|
4
|
+
export interface CandidateHandler {
|
|
5
|
+
readonly framework: Framework;
|
|
6
|
+
readonly framework_version: string | null;
|
|
7
|
+
readonly route_pattern: string;
|
|
8
|
+
readonly http_methods: ReadonlyArray<string>;
|
|
9
|
+
readonly file_path: string;
|
|
10
|
+
readonly location: SourceLocation;
|
|
11
|
+
readonly handler_function_name: string | null;
|
|
12
|
+
readonly handler_body_node: unknown;
|
|
13
|
+
readonly handler_source_start: number;
|
|
14
|
+
readonly handler_source_end: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function detectCatalogHandlers(parsedFile: ParsedFile): ReadonlyArray<CandidateHandler>;
|
|
17
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/model/catalog.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9C,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AA6BD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAK7F"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// Catalog-driven framework detection (D-31). Covers the four frameworks the catalog data
|
|
2
|
+
// can express uniformly. Next.js / Django / FastAPI need bespoke adapters in Plan 07.
|
|
3
|
+
//
|
|
4
|
+
// This module produces "candidate handlers" — partial WebhookHandler shapes — that Plan 06b's
|
|
5
|
+
// build.ts enriches with cross-file data (reachable_symbols, middleware_chain) and evidence.
|
|
6
|
+
import { walkBabelAst } from "../parsers/walk.js";
|
|
7
|
+
const BODY_METHODS = new Set(["post", "put", "patch", "delete", "all"]);
|
|
8
|
+
const ALL_METHODS = new Set([
|
|
9
|
+
"get",
|
|
10
|
+
"post",
|
|
11
|
+
"put",
|
|
12
|
+
"patch",
|
|
13
|
+
"delete",
|
|
14
|
+
"head",
|
|
15
|
+
"options",
|
|
16
|
+
"all",
|
|
17
|
+
]);
|
|
18
|
+
export function detectCatalogHandlers(parsedFile) {
|
|
19
|
+
if (parsedFile.parse_error !== null || parsedFile.raw_ast === null)
|
|
20
|
+
return [];
|
|
21
|
+
if (parsedFile.dialect === "babel")
|
|
22
|
+
return detectJsTsCatalog(parsedFile);
|
|
23
|
+
if (parsedFile.dialect === "tree-sitter-python")
|
|
24
|
+
return detectPythonCatalog(parsedFile);
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
// ---- JS/TS: Express, Hono, Fastify ----
|
|
28
|
+
function detectJsTsCatalog(parsedFile) {
|
|
29
|
+
const ast = parsedFile.raw_ast;
|
|
30
|
+
const out = [];
|
|
31
|
+
const imports = parsedFile.imports;
|
|
32
|
+
const hasHono = imports.some((i) => i.to_module === "hono" || i.to_module.startsWith("hono/"));
|
|
33
|
+
const hasFastify = imports.some((i) => i.to_module === "fastify");
|
|
34
|
+
walkBabelAst(ast, (node) => {
|
|
35
|
+
const handler = matchAppMethodCall(node, parsedFile.file_path, hasHono, hasFastify);
|
|
36
|
+
if (handler !== null)
|
|
37
|
+
out.push(handler);
|
|
38
|
+
if (hasFastify) {
|
|
39
|
+
const fastifyRoute = matchFastifyRouteCall(node, parsedFile.file_path);
|
|
40
|
+
if (fastifyRoute !== null)
|
|
41
|
+
out.push(fastifyRoute);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
// Matches `app.METHOD(path, ...handlers)` for body-bearing methods (POST/PUT/PATCH/DELETE/ALL).
|
|
47
|
+
// Returns null when the node shape does not match — keeps the walker visitor side-effect-free.
|
|
48
|
+
function matchAppMethodCall(node, filePath, hasHono, hasFastify) {
|
|
49
|
+
if (node.type !== "ExpressionStatement")
|
|
50
|
+
return null;
|
|
51
|
+
const expr = node.expression;
|
|
52
|
+
if (expr.type !== "CallExpression")
|
|
53
|
+
return null;
|
|
54
|
+
const callee = expr.callee;
|
|
55
|
+
if (callee.type !== "MemberExpression")
|
|
56
|
+
return null;
|
|
57
|
+
const property = callee.property;
|
|
58
|
+
if (property.type !== "Identifier")
|
|
59
|
+
return null;
|
|
60
|
+
const methodName = property.name.toLowerCase();
|
|
61
|
+
if (!ALL_METHODS.has(methodName) || !BODY_METHODS.has(methodName))
|
|
62
|
+
return null;
|
|
63
|
+
const args = expr.arguments;
|
|
64
|
+
const pathArg = args[0];
|
|
65
|
+
if (!pathArg)
|
|
66
|
+
return null;
|
|
67
|
+
const path = extractStringPath(pathArg);
|
|
68
|
+
if (path === null || !isWebhookishPath(path))
|
|
69
|
+
return null;
|
|
70
|
+
const fnNode = args[args.length - 1];
|
|
71
|
+
if (!fnNode || fnNode.type === "SpreadElement" || fnNode.type === "ArgumentPlaceholder") {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const framework = hasHono ? "hono" : hasFastify ? "fastify" : "express";
|
|
75
|
+
const fnSpan = spanOf(fnNode);
|
|
76
|
+
return {
|
|
77
|
+
framework,
|
|
78
|
+
framework_version: null, // D-01 — never inferred in Phase 2 (issue #5).
|
|
79
|
+
route_pattern: path,
|
|
80
|
+
http_methods: [methodName.toUpperCase()],
|
|
81
|
+
file_path: filePath,
|
|
82
|
+
location: locationOf(node),
|
|
83
|
+
handler_function_name: extractFunctionName(fnNode),
|
|
84
|
+
handler_body_node: fnNode,
|
|
85
|
+
handler_source_start: fnSpan.start,
|
|
86
|
+
handler_source_end: fnSpan.end,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Matches `fastify.route({ method, url, handler })` shape.
|
|
90
|
+
function matchFastifyRouteCall(node, filePath) {
|
|
91
|
+
if (node.type !== "ExpressionStatement")
|
|
92
|
+
return null;
|
|
93
|
+
const expr = node.expression;
|
|
94
|
+
if (expr.type !== "CallExpression")
|
|
95
|
+
return null;
|
|
96
|
+
const callee = expr.callee;
|
|
97
|
+
if (callee.type !== "MemberExpression")
|
|
98
|
+
return null;
|
|
99
|
+
const property = callee.property;
|
|
100
|
+
if (property.type !== "Identifier" || property.name !== "route")
|
|
101
|
+
return null;
|
|
102
|
+
const opts = expr.arguments[0];
|
|
103
|
+
if (!opts || opts.type !== "ObjectExpression")
|
|
104
|
+
return null;
|
|
105
|
+
const route = readRouteOptions(opts);
|
|
106
|
+
const method = readMethodField(route.method);
|
|
107
|
+
const url = route.url;
|
|
108
|
+
if (!method || !url || !isWebhookishPath(url))
|
|
109
|
+
return null;
|
|
110
|
+
if (!route.handler)
|
|
111
|
+
return null;
|
|
112
|
+
const fnSpan = spanOf(route.handler);
|
|
113
|
+
return {
|
|
114
|
+
framework: "fastify",
|
|
115
|
+
framework_version: null, // issue #5
|
|
116
|
+
route_pattern: url,
|
|
117
|
+
http_methods: method,
|
|
118
|
+
file_path: filePath,
|
|
119
|
+
location: locationOf(node),
|
|
120
|
+
handler_function_name: extractFunctionName(route.handler),
|
|
121
|
+
handler_body_node: route.handler,
|
|
122
|
+
handler_source_start: fnSpan.start,
|
|
123
|
+
handler_source_end: fnSpan.end,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function readRouteOptions(node) {
|
|
127
|
+
let method = null;
|
|
128
|
+
let url = null;
|
|
129
|
+
let handler = null;
|
|
130
|
+
for (const prop of node.properties) {
|
|
131
|
+
if (prop.type !== "ObjectProperty")
|
|
132
|
+
continue;
|
|
133
|
+
const keyName = propertyKeyName(prop.key);
|
|
134
|
+
if (keyName === null)
|
|
135
|
+
continue;
|
|
136
|
+
const value = prop.value;
|
|
137
|
+
if (keyName === "method") {
|
|
138
|
+
if (value.type === "StringLiteral") {
|
|
139
|
+
method = value.value;
|
|
140
|
+
}
|
|
141
|
+
else if (value.type === "ArrayExpression") {
|
|
142
|
+
method = value.elements
|
|
143
|
+
.filter((e) => e !== null && e.type === "StringLiteral")
|
|
144
|
+
.map((e) => e.value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (keyName === "url") {
|
|
148
|
+
if (value.type === "StringLiteral")
|
|
149
|
+
url = value.value;
|
|
150
|
+
}
|
|
151
|
+
else if (keyName === "handler") {
|
|
152
|
+
// Skip Pattern (destructuring assignment LHS) — only Expression-shaped values can be a handler.
|
|
153
|
+
if (value.type !== "RestElement" &&
|
|
154
|
+
value.type !== "AssignmentPattern" &&
|
|
155
|
+
value.type !== "ArrayPattern" &&
|
|
156
|
+
value.type !== "ObjectPattern") {
|
|
157
|
+
handler = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { method, url, handler };
|
|
162
|
+
}
|
|
163
|
+
function propertyKeyName(key) {
|
|
164
|
+
if (key.type === "Identifier")
|
|
165
|
+
return key.name;
|
|
166
|
+
if (key.type === "StringLiteral")
|
|
167
|
+
return key.value;
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
function extractStringPath(node) {
|
|
171
|
+
if (node.type === "StringLiteral")
|
|
172
|
+
return node.value;
|
|
173
|
+
if (node.type === "TemplateLiteral" &&
|
|
174
|
+
node.expressions.length === 0 &&
|
|
175
|
+
node.quasis.length === 1) {
|
|
176
|
+
return node.quasis[0]?.value.cooked ?? null;
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
function isWebhookishPath(path) {
|
|
181
|
+
// Pre-filter: catch obvious webhook paths and conventional provider paths.
|
|
182
|
+
// The catalog's `conventional_paths` is also used for evidence; the prefilter is just to keep
|
|
183
|
+
// noise low so we don't enumerate every route in the project.
|
|
184
|
+
const lower = path.toLowerCase();
|
|
185
|
+
return lower.includes("webhook") || lower.includes("/hook") || lower.includes("/hooks");
|
|
186
|
+
}
|
|
187
|
+
function extractFunctionName(node) {
|
|
188
|
+
if (node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
|
|
189
|
+
return node.id?.name ?? null;
|
|
190
|
+
}
|
|
191
|
+
if (node.type === "Identifier")
|
|
192
|
+
return node.name;
|
|
193
|
+
return null; // arrow function or anonymous
|
|
194
|
+
}
|
|
195
|
+
function locationOf(node) {
|
|
196
|
+
const loc = node.loc;
|
|
197
|
+
return {
|
|
198
|
+
line: loc?.start.line ?? 1,
|
|
199
|
+
col: (loc?.start.column ?? 0) + 1,
|
|
200
|
+
end_line: loc?.end.line ?? 1,
|
|
201
|
+
end_col: (loc?.end.column ?? 0) + 1,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function spanOf(node) {
|
|
205
|
+
return { start: node.start ?? 0, end: node.end ?? 0 };
|
|
206
|
+
}
|
|
207
|
+
function readMethodField(value) {
|
|
208
|
+
if (value === null)
|
|
209
|
+
return null;
|
|
210
|
+
if (typeof value === "string") {
|
|
211
|
+
return BODY_METHODS.has(value.toLowerCase()) ? [value.toUpperCase()] : null;
|
|
212
|
+
}
|
|
213
|
+
const upper = value.map((v) => v.toUpperCase());
|
|
214
|
+
return upper.some((m) => BODY_METHODS.has(m.toLowerCase())) ? upper : null;
|
|
215
|
+
}
|
|
216
|
+
// ---- Python: Flask ----
|
|
217
|
+
function detectPythonCatalog(parsedFile) {
|
|
218
|
+
const tree = parsedFile.raw_ast;
|
|
219
|
+
const out = [];
|
|
220
|
+
// Detect `@app.route('/x', methods=['POST'])\ndef fn(...)` — Flask convention.
|
|
221
|
+
const decorated = tree.rootNode.descendantsOfType(["decorated_definition"]);
|
|
222
|
+
for (const node of decorated) {
|
|
223
|
+
const handler = matchFlaskDecorator(node, parsedFile.file_path);
|
|
224
|
+
if (handler !== null)
|
|
225
|
+
out.push(handler);
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
function matchFlaskDecorator(node, filePath) {
|
|
230
|
+
const fnDef = node.namedChildren.find((c) => c.type === "function_definition");
|
|
231
|
+
if (!fnDef)
|
|
232
|
+
return null;
|
|
233
|
+
for (const dec of node.descendantsOfType(["decorator"])) {
|
|
234
|
+
const route = matchRouteDecorator(dec);
|
|
235
|
+
if (!route)
|
|
236
|
+
continue;
|
|
237
|
+
if (!isWebhookishPath(route.path))
|
|
238
|
+
continue;
|
|
239
|
+
if (!route.methods.some((m) => BODY_METHODS.has(m.toLowerCase())))
|
|
240
|
+
continue;
|
|
241
|
+
return {
|
|
242
|
+
framework: "flask",
|
|
243
|
+
framework_version: null, // issue #5
|
|
244
|
+
route_pattern: route.path,
|
|
245
|
+
http_methods: route.methods,
|
|
246
|
+
file_path: filePath,
|
|
247
|
+
location: {
|
|
248
|
+
line: node.startPosition.row + 1,
|
|
249
|
+
col: node.startPosition.column + 1,
|
|
250
|
+
end_line: node.endPosition.row + 1,
|
|
251
|
+
end_col: node.endPosition.column + 1,
|
|
252
|
+
},
|
|
253
|
+
handler_function_name: fnDef.childForFieldName("name")?.text ?? null,
|
|
254
|
+
handler_body_node: fnDef,
|
|
255
|
+
handler_source_start: fnDef.startIndex,
|
|
256
|
+
handler_source_end: fnDef.endIndex,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
function matchRouteDecorator(dec) {
|
|
262
|
+
const call = dec.descendantsOfType(["call"])[0];
|
|
263
|
+
if (!call)
|
|
264
|
+
return null;
|
|
265
|
+
if (!(call.childForFieldName("function")?.text ?? "").endsWith(".route"))
|
|
266
|
+
return null;
|
|
267
|
+
const args = call.childForFieldName("arguments");
|
|
268
|
+
if (!args)
|
|
269
|
+
return null;
|
|
270
|
+
const pathArg = args.namedChildren.find((c) => c.type === "string");
|
|
271
|
+
if (!pathArg)
|
|
272
|
+
return null;
|
|
273
|
+
const path = stripPyString(pathArg.text);
|
|
274
|
+
const methods = [];
|
|
275
|
+
for (const kw of args.namedChildren) {
|
|
276
|
+
if (kw.type !== "keyword_argument")
|
|
277
|
+
continue;
|
|
278
|
+
if (kw.childForFieldName("name")?.text !== "methods")
|
|
279
|
+
continue;
|
|
280
|
+
const list = kw.childForFieldName("value");
|
|
281
|
+
if (!list)
|
|
282
|
+
continue;
|
|
283
|
+
for (const elem of list.namedChildren) {
|
|
284
|
+
if (elem.type === "string")
|
|
285
|
+
methods.push(stripPyString(elem.text).toUpperCase());
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return { path, methods: methods.length > 0 ? methods : ["GET"] };
|
|
289
|
+
}
|
|
290
|
+
function stripPyString(raw) {
|
|
291
|
+
let i = 0;
|
|
292
|
+
while (i < raw.length && /[bBrRuUfF]/.test(raw[i] ?? ""))
|
|
293
|
+
i++;
|
|
294
|
+
const quoted = raw.slice(i);
|
|
295
|
+
if (quoted.startsWith('"""') || quoted.startsWith("'''")) {
|
|
296
|
+
return quoted.slice(3, quoted.length - 3);
|
|
297
|
+
}
|
|
298
|
+
if (quoted.startsWith('"') || quoted.startsWith("'")) {
|
|
299
|
+
return quoted.slice(1, quoted.length - 1);
|
|
300
|
+
}
|
|
301
|
+
return quoted;
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/model/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SourceLocation } from "../types/finding.js";
|
|
2
|
+
import type { WebhookEvidence } from "../types/handler.js";
|
|
3
|
+
import type { ImportEdge, ParsedFile } from "../types/project-model.js";
|
|
4
|
+
import type { ProviderCatalog } from "../types/rule-set.js";
|
|
5
|
+
import type { CandidateHandler } from "./catalog.js";
|
|
6
|
+
export interface ComputeEvidenceInput {
|
|
7
|
+
readonly handler: CandidateHandler;
|
|
8
|
+
readonly parsedFile: ParsedFile;
|
|
9
|
+
readonly providerCatalog: ProviderCatalog;
|
|
10
|
+
readonly imports: ReadonlyArray<ImportEdge>;
|
|
11
|
+
}
|
|
12
|
+
export interface ComputeEvidenceOutput {
|
|
13
|
+
readonly evidence: ReadonlyArray<WebhookEvidence>;
|
|
14
|
+
readonly provider: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function computeEvidence(input: ComputeEvidenceInput): ComputeEvidenceOutput;
|
|
17
|
+
export declare function locationFromCandidate(handler: CandidateHandler): SourceLocation;
|
|
18
|
+
//# sourceMappingURL=evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/model/evidence.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAoHlF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,GAAG,cAAc,CAE/E"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// D-32: every candidate handler is enriched with WebhookEvidence[] derived from the provider
|
|
2
|
+
// catalog. Engine computes evidence; rules query thresholds. Engine never decides "is webhook?" —
|
|
3
|
+
// that's rule-side. Engine assigns provider attribution heuristically (worst case "unknown").
|
|
4
|
+
//
|
|
5
|
+
// Phase 2 split: this function emits 6 of the 7 D-32 signals. The seventh —
|
|
6
|
+
// `sdk_verify_call` — is appended in Plan 06b's build.ts after reachable_symbols is computed.
|
|
7
|
+
export function computeEvidence(input) {
|
|
8
|
+
const out = [];
|
|
9
|
+
const handlerLoc = input.handler.location;
|
|
10
|
+
const handlerText = input.parsedFile.source_text.slice(input.handler.handler_source_start, input.handler.handler_source_end);
|
|
11
|
+
// Signal A — path_pattern_match (catalog conventional_paths).
|
|
12
|
+
for (const [providerName, entry] of Object.entries(input.providerCatalog)) {
|
|
13
|
+
for (const conv of entry.conventional_paths) {
|
|
14
|
+
if (input.handler.route_pattern.toLowerCase().includes(conv.toLowerCase())) {
|
|
15
|
+
out.push({
|
|
16
|
+
kind: "path_pattern_match",
|
|
17
|
+
provider: providerName,
|
|
18
|
+
location: handlerLoc,
|
|
19
|
+
detail: conv,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Signal B — sdk_import (catalog sdk_packages).
|
|
25
|
+
for (const [providerName, entry] of Object.entries(input.providerCatalog)) {
|
|
26
|
+
for (const pkg of entry.sdk_packages) {
|
|
27
|
+
if (input.imports.some((i) => i.to_module === pkg)) {
|
|
28
|
+
out.push({
|
|
29
|
+
kind: "sdk_import",
|
|
30
|
+
provider: providerName,
|
|
31
|
+
location: handlerLoc,
|
|
32
|
+
detail: pkg,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Signal C — secret_env_var_reference (catalog secret_env_prefix). Substring match within the
|
|
38
|
+
// handler's source range so we count only references in/near this handler.
|
|
39
|
+
for (const [providerName, entry] of Object.entries(input.providerCatalog)) {
|
|
40
|
+
for (const env of entry.secret_env_prefix) {
|
|
41
|
+
if (handlerText.includes(env)) {
|
|
42
|
+
out.push({
|
|
43
|
+
kind: "secret_env_var_reference",
|
|
44
|
+
provider: providerName,
|
|
45
|
+
location: handlerLoc,
|
|
46
|
+
detail: env,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Signal D — secret_literal_match (catalog secret_literal_prefix). Restricted to handler text.
|
|
52
|
+
for (const [providerName, entry] of Object.entries(input.providerCatalog)) {
|
|
53
|
+
for (const prefix of entry.secret_literal_prefix) {
|
|
54
|
+
if (handlerText.includes(prefix)) {
|
|
55
|
+
out.push({
|
|
56
|
+
kind: "secret_literal_match",
|
|
57
|
+
provider: providerName,
|
|
58
|
+
location: handlerLoc,
|
|
59
|
+
detail: prefix,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Signal E — signature_header_read (catalog signature_header). Substring match on handler text.
|
|
65
|
+
for (const [providerName, entry] of Object.entries(input.providerCatalog)) {
|
|
66
|
+
for (const header of entry.signature_header) {
|
|
67
|
+
if (handlerText.toLowerCase().includes(header.toLowerCase())) {
|
|
68
|
+
out.push({
|
|
69
|
+
kind: "signature_header_read",
|
|
70
|
+
provider: providerName,
|
|
71
|
+
location: handlerLoc,
|
|
72
|
+
detail: header,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Signal F — body_as_bytes_or_buffer. Heuristic token search inside the handler.
|
|
78
|
+
if (/(Buffer|Uint8Array|\braw\b|\bbytes\b|c\.req\.raw|request\.get_data\(\)|request\.body)/i.test(handlerText)) {
|
|
79
|
+
out.push({
|
|
80
|
+
kind: "body_as_bytes_or_buffer",
|
|
81
|
+
provider: "unknown",
|
|
82
|
+
location: handlerLoc,
|
|
83
|
+
detail: "heuristic",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Signal G — sdk_verify_call: NOT EMITTED HERE. Plan 06b's build.ts appends it after computing
|
|
87
|
+
// reachable_symbols, by cross-checking against catalog.providers[*].sdk_verify_calls.
|
|
88
|
+
// Provider attribution — most-cited provider wins; ties → "multiple"; zero → "unknown".
|
|
89
|
+
const counts = new Map();
|
|
90
|
+
for (const e of out) {
|
|
91
|
+
if (e.provider === "unknown")
|
|
92
|
+
continue;
|
|
93
|
+
counts.set(e.provider, (counts.get(e.provider) ?? 0) + 1);
|
|
94
|
+
}
|
|
95
|
+
let topProvider = "unknown";
|
|
96
|
+
let topCount = 0;
|
|
97
|
+
let tied = false;
|
|
98
|
+
for (const [p, c] of counts) {
|
|
99
|
+
if (c > topCount) {
|
|
100
|
+
topProvider = p;
|
|
101
|
+
topCount = c;
|
|
102
|
+
tied = false;
|
|
103
|
+
}
|
|
104
|
+
else if (c === topCount && c > 0) {
|
|
105
|
+
tied = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const provider = tied ? "multiple" : topProvider;
|
|
109
|
+
return { evidence: out, provider };
|
|
110
|
+
}
|
|
111
|
+
export function locationFromCandidate(handler) {
|
|
112
|
+
return handler.location;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/model/evidence.ts"],"names":[],"mappings":"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"}
|