@hookwarden/engine 0.0.1 → 0.1.1
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 +186 -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 +18 -4
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CandidateHandler } from "../model/catalog.js";
|
|
2
|
+
import type { ParsedFile } from "../types/project-model.js";
|
|
3
|
+
export declare function djangoAdapter(file: ParsedFile, allFiles: ReadonlyArray<ParsedFile>): ReadonlyArray<CandidateHandler>;
|
|
4
|
+
//# sourceMappingURL=django.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"django.d.ts","sourceRoot":"","sources":["../../src/adapters/django.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAiB5D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,GAClC,aAAa,CAAC,gBAAgB,CAAC,CAyBjC"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// D-31 bespoke adapter: Django. Two patterns:
|
|
2
|
+
// 1. urls.py: urlpatterns = [path('webhooks/x', view_fn)] → view_fn (cross-file by name)
|
|
3
|
+
// 2. CBV: class StripeView(View): def post(self, ...) → the post method is the handler
|
|
4
|
+
// Reflective dispatch is out of scope for v1; Phase 6 corpus run will surface what's missed.
|
|
5
|
+
// Pure: tree-sitter trees + ParsedFile only. No I/O.
|
|
6
|
+
const CBV_HTTP_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
7
|
+
const URL_FUNCTIONS = new Set(["path", "re_path"]);
|
|
8
|
+
export function djangoAdapter(file, allFiles) {
|
|
9
|
+
if (file.dialect !== "tree-sitter-python")
|
|
10
|
+
return [];
|
|
11
|
+
if (file.parse_error !== null || file.raw_ast === null)
|
|
12
|
+
return [];
|
|
13
|
+
const usesDjango = file.imports.some((i) => i.to_module === "django" || i.to_module.startsWith("django."));
|
|
14
|
+
const isUrlsPy = file.file_path.endsWith("urls.py");
|
|
15
|
+
if (!usesDjango && !isUrlsPy)
|
|
16
|
+
return [];
|
|
17
|
+
const tree = file.raw_ast;
|
|
18
|
+
const out = [];
|
|
19
|
+
// Pattern 1 — class-based views.
|
|
20
|
+
for (const klass of tree.rootNode.descendantsOfType(["class_definition"])) {
|
|
21
|
+
out.push(...detectClassBasedView(klass, file, allFiles, usesDjango));
|
|
22
|
+
}
|
|
23
|
+
// Pattern 2 — urls.py function references.
|
|
24
|
+
if (isUrlsPy) {
|
|
25
|
+
for (const call of tree.rootNode.descendantsOfType(["call"])) {
|
|
26
|
+
const handler = matchUrlsPyPath(call, file);
|
|
27
|
+
if (handler !== null)
|
|
28
|
+
out.push(handler);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function detectClassBasedView(klass, file, allFiles, usesDjango) {
|
|
34
|
+
// CBV heuristic: superclass list contains View / APIView / GenericAPIView, OR the file imports django.
|
|
35
|
+
const superList = klass.childForFieldName("superclasses");
|
|
36
|
+
const superText = superList?.text ?? "";
|
|
37
|
+
const isCBV = /\b(View|APIView|GenericAPIView)\b/.test(superText) || usesDjango;
|
|
38
|
+
if (!isCBV)
|
|
39
|
+
return [];
|
|
40
|
+
const className = klass.childForFieldName("name")?.text ?? null;
|
|
41
|
+
const body = klass.childForFieldName("body");
|
|
42
|
+
if (!body)
|
|
43
|
+
return [];
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const fnDef of body.descendantsOfType(["function_definition"])) {
|
|
46
|
+
const methodName = fnDef.childForFieldName("name")?.text;
|
|
47
|
+
if (!methodName)
|
|
48
|
+
continue;
|
|
49
|
+
const upper = methodName.toUpperCase();
|
|
50
|
+
if (!CBV_HTTP_METHODS.has(upper))
|
|
51
|
+
continue;
|
|
52
|
+
out.push({
|
|
53
|
+
framework: "django",
|
|
54
|
+
framework_version: null, // issue #5
|
|
55
|
+
route_pattern: resolveCBVRoute(className, allFiles),
|
|
56
|
+
http_methods: [upper],
|
|
57
|
+
file_path: file.file_path,
|
|
58
|
+
location: locOf(fnDef),
|
|
59
|
+
handler_function_name: `${className ?? "<view>"}.${methodName}`,
|
|
60
|
+
handler_body_node: fnDef,
|
|
61
|
+
handler_source_start: fnDef.startIndex,
|
|
62
|
+
handler_source_end: fnDef.endIndex,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
function matchUrlsPyPath(call, file) {
|
|
68
|
+
const fnText = call.childForFieldName("function")?.text ?? "";
|
|
69
|
+
if (!URL_FUNCTIONS.has(fnText))
|
|
70
|
+
return null;
|
|
71
|
+
const args = call.childForFieldName("arguments");
|
|
72
|
+
if (!args)
|
|
73
|
+
return null;
|
|
74
|
+
const positional = args.namedChildren.filter((c) => c.type !== "keyword_argument");
|
|
75
|
+
const pathArg = positional[0];
|
|
76
|
+
const viewArg = positional[1];
|
|
77
|
+
if (!pathArg || pathArg.type !== "string" || !viewArg)
|
|
78
|
+
return null;
|
|
79
|
+
const path = `/${stripPyString(pathArg.text).replace(/^\/+/, "")}`;
|
|
80
|
+
// Filter to webhooky paths only — keeps signal high.
|
|
81
|
+
if (!/(webhook|hook|hooks)/i.test(path))
|
|
82
|
+
return null;
|
|
83
|
+
return {
|
|
84
|
+
framework: "django",
|
|
85
|
+
framework_version: null, // issue #5
|
|
86
|
+
route_pattern: path,
|
|
87
|
+
// urls.py alone doesn't carry methods — assume POST for webhook routes.
|
|
88
|
+
http_methods: ["POST"],
|
|
89
|
+
file_path: file.file_path,
|
|
90
|
+
location: locOf(call),
|
|
91
|
+
handler_function_name: viewArg.text,
|
|
92
|
+
handler_body_node: viewArg, // placeholder; reachability will resolve via the import graph
|
|
93
|
+
handler_source_start: call.startIndex,
|
|
94
|
+
handler_source_end: call.endIndex,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Map className → '/<path>' by scanning every urls.py for `path('...', ClassName.as_view())`.
|
|
98
|
+
function resolveCBVRoute(className, allFiles) {
|
|
99
|
+
if (!className)
|
|
100
|
+
return "/";
|
|
101
|
+
for (const f of allFiles) {
|
|
102
|
+
if (!f.file_path.endsWith("urls.py") ||
|
|
103
|
+
f.dialect !== "tree-sitter-python" ||
|
|
104
|
+
f.raw_ast === null) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const tree = f.raw_ast;
|
|
108
|
+
for (const call of tree.rootNode.descendantsOfType(["call"])) {
|
|
109
|
+
const fnText = call.childForFieldName("function")?.text ?? "";
|
|
110
|
+
if (!URL_FUNCTIONS.has(fnText))
|
|
111
|
+
continue;
|
|
112
|
+
const args = call.childForFieldName("arguments");
|
|
113
|
+
if (!args)
|
|
114
|
+
continue;
|
|
115
|
+
const positional = args.namedChildren.filter((c) => c.type !== "keyword_argument");
|
|
116
|
+
const pathArg = positional[0];
|
|
117
|
+
const viewArg = positional[1];
|
|
118
|
+
if (!pathArg || pathArg.type !== "string" || !viewArg)
|
|
119
|
+
continue;
|
|
120
|
+
if (viewArg.text === `${className}.as_view()`) {
|
|
121
|
+
return `/${stripPyString(pathArg.text).replace(/^\/+/, "")}`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return "/";
|
|
126
|
+
}
|
|
127
|
+
function locOf(n) {
|
|
128
|
+
return {
|
|
129
|
+
line: n.startPosition.row + 1,
|
|
130
|
+
col: n.startPosition.column + 1,
|
|
131
|
+
end_line: n.endPosition.row + 1,
|
|
132
|
+
end_col: n.endPosition.column + 1,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function stripPyString(raw) {
|
|
136
|
+
let i = 0;
|
|
137
|
+
while (i < raw.length && /[bBrRuUfF]/.test(raw[i] ?? ""))
|
|
138
|
+
i++;
|
|
139
|
+
const quoted = raw.slice(i);
|
|
140
|
+
if (quoted.startsWith('"""') || quoted.startsWith("'''")) {
|
|
141
|
+
return quoted.slice(3, quoted.length - 3);
|
|
142
|
+
}
|
|
143
|
+
if (quoted.startsWith('"') || quoted.startsWith("'")) {
|
|
144
|
+
return quoted.slice(1, quoted.length - 1);
|
|
145
|
+
}
|
|
146
|
+
return quoted;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=django.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"django.js","sourceRoot":"","sources":["../../src/adapters/django.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CandidateHandler } from "../model/catalog.js";
|
|
2
|
+
import type { ParsedFile } from "../types/project-model.js";
|
|
3
|
+
export declare function fastapiAdapter(file: ParsedFile, allFiles: ReadonlyArray<ParsedFile>): ReadonlyArray<CandidateHandler>;
|
|
4
|
+
//# sourceMappingURL=fastapi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastapi.d.ts","sourceRoot":"","sources":["../../src/adapters/fastapi.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAiB5D,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,GAClC,aAAa,CAAC,gBAAgB,CAAC,CAiBjC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// D-31 bespoke adapter: FastAPI. @router.post('/x') / @app.post('/x') on async def.
|
|
2
|
+
// Cross-file include_router prefix resolution via single-pass scan over allFiles.
|
|
3
|
+
// Pure: tree-sitter trees + ParsedFile only. No I/O.
|
|
4
|
+
const BODY_METHODS = new Set(["post", "put", "patch", "delete"]);
|
|
5
|
+
export function fastapiAdapter(file, allFiles) {
|
|
6
|
+
if (file.dialect !== "tree-sitter-python")
|
|
7
|
+
return [];
|
|
8
|
+
if (file.parse_error !== null || file.raw_ast === null)
|
|
9
|
+
return [];
|
|
10
|
+
const usesFastAPI = file.imports.some((i) => i.to_module === "fastapi" || i.to_module.startsWith("fastapi."));
|
|
11
|
+
if (!usesFastAPI)
|
|
12
|
+
return [];
|
|
13
|
+
const tree = file.raw_ast;
|
|
14
|
+
const prefixByRouterVar = collectIncludeRouterPrefixes(allFiles);
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const node of tree.rootNode.descendantsOfType(["decorated_definition"])) {
|
|
17
|
+
const handler = matchFastApiDecorator(node, file, prefixByRouterVar);
|
|
18
|
+
if (handler !== null)
|
|
19
|
+
out.push(handler);
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function matchFastApiDecorator(node, file, prefixByRouterVar) {
|
|
24
|
+
const fnDef = node.namedChildren.find((c) => c.type === "function_definition");
|
|
25
|
+
if (!fnDef)
|
|
26
|
+
return null;
|
|
27
|
+
for (const dec of node.descendantsOfType(["decorator"])) {
|
|
28
|
+
const call = dec.descendantsOfType(["call"])[0];
|
|
29
|
+
if (!call)
|
|
30
|
+
continue;
|
|
31
|
+
const fnText = call.childForFieldName("function")?.text ?? "";
|
|
32
|
+
const dot = fnText.lastIndexOf(".");
|
|
33
|
+
if (dot < 0)
|
|
34
|
+
continue;
|
|
35
|
+
const routerVar = fnText.slice(0, dot);
|
|
36
|
+
const methodName = fnText.slice(dot + 1);
|
|
37
|
+
if (!BODY_METHODS.has(methodName))
|
|
38
|
+
continue;
|
|
39
|
+
const args = call.childForFieldName("arguments");
|
|
40
|
+
if (!args)
|
|
41
|
+
continue;
|
|
42
|
+
const pathArg = args.namedChildren.find((c) => c.type === "string");
|
|
43
|
+
if (!pathArg)
|
|
44
|
+
continue;
|
|
45
|
+
const path = stripPyString(pathArg.text);
|
|
46
|
+
const prefix = prefixByRouterVar.get(routerVar) ?? "";
|
|
47
|
+
const routePattern = prefix + path;
|
|
48
|
+
return {
|
|
49
|
+
framework: "fastapi",
|
|
50
|
+
framework_version: null, // issue #5
|
|
51
|
+
route_pattern: routePattern,
|
|
52
|
+
http_methods: [methodName.toUpperCase()],
|
|
53
|
+
file_path: file.file_path,
|
|
54
|
+
location: {
|
|
55
|
+
line: node.startPosition.row + 1,
|
|
56
|
+
col: node.startPosition.column + 1,
|
|
57
|
+
end_line: node.endPosition.row + 1,
|
|
58
|
+
end_col: node.endPosition.column + 1,
|
|
59
|
+
},
|
|
60
|
+
handler_function_name: fnDef.childForFieldName("name")?.text ?? null,
|
|
61
|
+
handler_body_node: fnDef,
|
|
62
|
+
handler_source_start: fnDef.startIndex,
|
|
63
|
+
handler_source_end: fnDef.endIndex,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
// Map router-variable-name → prefix from `app.include_router(router_var, prefix='/api')`
|
|
69
|
+
// across every Python file. Cross-file: a router defined in module A and included in module B
|
|
70
|
+
// resolves as long as both files are in `allFiles`.
|
|
71
|
+
function collectIncludeRouterPrefixes(allFiles) {
|
|
72
|
+
const out = new Map();
|
|
73
|
+
for (const f of allFiles) {
|
|
74
|
+
if (f.dialect !== "tree-sitter-python" || f.raw_ast === null)
|
|
75
|
+
continue;
|
|
76
|
+
const tree = f.raw_ast;
|
|
77
|
+
for (const call of tree.rootNode.descendantsOfType(["call"])) {
|
|
78
|
+
const fnText = call.childForFieldName("function")?.text ?? "";
|
|
79
|
+
if (!fnText.endsWith(".include_router"))
|
|
80
|
+
continue;
|
|
81
|
+
const args = call.childForFieldName("arguments");
|
|
82
|
+
if (!args)
|
|
83
|
+
continue;
|
|
84
|
+
const positional = args.namedChildren.filter((c) => c.type !== "keyword_argument");
|
|
85
|
+
const routerArg = positional[0];
|
|
86
|
+
if (!routerArg)
|
|
87
|
+
continue;
|
|
88
|
+
const routerName = routerArg.text;
|
|
89
|
+
let prefix = "";
|
|
90
|
+
for (const kw of args.namedChildren) {
|
|
91
|
+
if (kw.type !== "keyword_argument")
|
|
92
|
+
continue;
|
|
93
|
+
if (kw.childForFieldName("name")?.text !== "prefix")
|
|
94
|
+
continue;
|
|
95
|
+
const valNode = kw.childForFieldName("value");
|
|
96
|
+
if (valNode?.type === "string")
|
|
97
|
+
prefix = stripPyString(valNode.text);
|
|
98
|
+
}
|
|
99
|
+
if (prefix !== "")
|
|
100
|
+
out.set(routerName, prefix);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
function stripPyString(raw) {
|
|
106
|
+
let i = 0;
|
|
107
|
+
while (i < raw.length && /[bBrRuUfF]/.test(raw[i] ?? ""))
|
|
108
|
+
i++;
|
|
109
|
+
const quoted = raw.slice(i);
|
|
110
|
+
if (quoted.startsWith('"""') || quoted.startsWith("'''")) {
|
|
111
|
+
return quoted.slice(3, quoted.length - 3);
|
|
112
|
+
}
|
|
113
|
+
if (quoted.startsWith('"') || quoted.startsWith("'")) {
|
|
114
|
+
return quoted.slice(1, quoted.length - 1);
|
|
115
|
+
}
|
|
116
|
+
return quoted;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=fastapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastapi.js","sourceRoot":"","sources":["../../src/adapters/fastapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CandidateHandler } from "../model/catalog.js";
|
|
2
|
+
import type { ParsedFile } from "../types/project-model.js";
|
|
3
|
+
import { djangoAdapter } from "./django.js";
|
|
4
|
+
import { fastapiAdapter } from "./fastapi.js";
|
|
5
|
+
import { nextjsAdapter } from "./nextjs.js";
|
|
6
|
+
export type FrameworkAdapter = (file: ParsedFile, allFiles: ReadonlyArray<ParsedFile>) => ReadonlyArray<CandidateHandler>;
|
|
7
|
+
export declare const ALL_ADAPTERS: ReadonlyArray<FrameworkAdapter>;
|
|
8
|
+
export { djangoAdapter, fastapiAdapter, nextjsAdapter };
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,KAChC,aAAa,CAAC,gBAAgB,CAAC,CAAC;AAErC,eAAO,MAAM,YAAY,EAAE,aAAa,CAAC,gBAAgB,CAIxD,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { djangoAdapter } from "./django.js";
|
|
2
|
+
import { fastapiAdapter } from "./fastapi.js";
|
|
3
|
+
import { nextjsAdapter } from "./nextjs.js";
|
|
4
|
+
export const ALL_ADAPTERS = [
|
|
5
|
+
nextjsAdapter,
|
|
6
|
+
fastapiAdapter,
|
|
7
|
+
djangoAdapter,
|
|
8
|
+
];
|
|
9
|
+
export { djangoAdapter, fastapiAdapter, nextjsAdapter };
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAO5C,MAAM,CAAC,MAAM,YAAY,GAAoC;IAC3D,aAAa;IACb,cAAc;IACd,aAAa;CACd,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CandidateHandler } from "../model/catalog.js";
|
|
2
|
+
import type { ParsedFile } from "../types/project-model.js";
|
|
3
|
+
export declare function nextjsAdapter(file: ParsedFile, _allFiles: ReadonlyArray<ParsedFile>): ReadonlyArray<CandidateHandler>;
|
|
4
|
+
//# sourceMappingURL=nextjs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../src/adapters/nextjs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAa5D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,aAAa,CAAC,UAAU,CAAC,GACnC,aAAa,CAAC,gBAAgB,CAAC,CAiBjC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// D-31 bespoke adapter: Next.js App Router. Filesystem-derived routes + HTTP-method-named exports.
|
|
2
|
+
// Pure: AST traversal + path-string matching only — engine purity gate (D-01) bans I/O imports.
|
|
3
|
+
// Match `app/...` at the start of the path or after a `/` separator, so both
|
|
4
|
+
// repo-root paths (`app/api/...`) and nested paths (`apps/web/app/api/...`) work.
|
|
5
|
+
const ROUTE_FILE_RE = /(?:^|\/)app\/(?:.+\/)?route\.(?:ts|tsx|js|jsx)$/;
|
|
6
|
+
const ROUTE_PATH_RE = /(?:^|\/)app\/(.+?)\/route\.(?:ts|tsx|js|jsx)$/;
|
|
7
|
+
const BODY_METHOD_NAMES = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
8
|
+
export function nextjsAdapter(file, _allFiles) {
|
|
9
|
+
if (file.dialect !== "babel")
|
|
10
|
+
return [];
|
|
11
|
+
if (file.parse_error !== null || file.raw_ast === null)
|
|
12
|
+
return [];
|
|
13
|
+
if (!ROUTE_FILE_RE.test(file.file_path))
|
|
14
|
+
return [];
|
|
15
|
+
const routePattern = deriveRoute(file.file_path);
|
|
16
|
+
const ast = file.raw_ast;
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const stmt of ast.program.body) {
|
|
19
|
+
if (stmt.type !== "ExportNamedDeclaration")
|
|
20
|
+
continue;
|
|
21
|
+
for (const exported of collectNamedFunctionExports(stmt)) {
|
|
22
|
+
if (!BODY_METHOD_NAMES.has(exported.name))
|
|
23
|
+
continue;
|
|
24
|
+
out.push(buildHandler(file, routePattern, exported));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
function deriveRoute(filePath) {
|
|
30
|
+
// app/api/webhooks/stripe/route.ts → /api/webhooks/stripe
|
|
31
|
+
const m = filePath.match(ROUTE_PATH_RE);
|
|
32
|
+
if (!m?.[1])
|
|
33
|
+
return "/";
|
|
34
|
+
return `/${m[1]}`;
|
|
35
|
+
}
|
|
36
|
+
// Pull out `export function POST(...)` and `export const POST = ...` named function shapes.
|
|
37
|
+
function collectNamedFunctionExports(exp) {
|
|
38
|
+
const decl = exp.declaration;
|
|
39
|
+
if (!decl)
|
|
40
|
+
return [];
|
|
41
|
+
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
42
|
+
return [{ name: decl.id.name, fnNode: decl }];
|
|
43
|
+
}
|
|
44
|
+
if (decl.type === "VariableDeclaration") {
|
|
45
|
+
const out = [];
|
|
46
|
+
for (const v of decl.declarations) {
|
|
47
|
+
if (v.id.type !== "Identifier" || !v.init)
|
|
48
|
+
continue;
|
|
49
|
+
out.push({ name: v.id.name, fnNode: v.init });
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
function buildHandler(file, routePattern, exported) {
|
|
56
|
+
const span = spanOf(exported.fnNode);
|
|
57
|
+
return {
|
|
58
|
+
framework: "nextjs",
|
|
59
|
+
framework_version: null, // D-01 — never inferred in Phase 2 (issue #5).
|
|
60
|
+
route_pattern: routePattern,
|
|
61
|
+
http_methods: [exported.name],
|
|
62
|
+
file_path: file.file_path,
|
|
63
|
+
location: locationOf(exported.fnNode),
|
|
64
|
+
handler_function_name: exported.name,
|
|
65
|
+
handler_body_node: exported.fnNode,
|
|
66
|
+
handler_source_start: span.start,
|
|
67
|
+
handler_source_end: span.end,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function locationOf(node) {
|
|
71
|
+
const loc = node.loc;
|
|
72
|
+
return {
|
|
73
|
+
line: loc?.start.line ?? 1,
|
|
74
|
+
col: (loc?.start.column ?? 0) + 1,
|
|
75
|
+
end_line: loc?.end.line ?? 1,
|
|
76
|
+
end_col: (loc?.end.column ?? 0) + 1,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function spanOf(node) {
|
|
80
|
+
return { start: node.start ?? 0, end: node.end ?? 0 };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=nextjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/adapters/nextjs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Config } from "./types/config.js";
|
|
2
|
+
import type { ProjectModel } from "./types/project-model.js";
|
|
3
|
+
import type { RuleSet } from "./types/rule-set.js";
|
|
4
|
+
import type { ScanResult } from "./types/scan-result.js";
|
|
5
|
+
export declare function evaluate(model: ProjectModel, ruleSet: RuleSet, config: Config): Promise<ScanResult>;
|
|
6
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAkB,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,wBAAwB,CAAC;AASvE,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,CAAC,CAuDrB"}
|
package/dist/evaluate.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Top-level evaluate(model, ruleSet, config) → Promise<ScanResult>.
|
|
2
|
+
// D-35 + D-29 + D-30 + D-38. Pure: no I/O.
|
|
3
|
+
//
|
|
4
|
+
// Producer of ScanResult.inventory — DISCOVERY-01 (engine portion). The inventory carries every
|
|
5
|
+
// field listed in D-36 (handler shape) and is internally consistent with findings + metadata.
|
|
6
|
+
import { buildParseErrorFinding } from "./evaluator/parse-error.js";
|
|
7
|
+
import { applyPathSeverityOverrides } from "./evaluator/path-severity-overrides.js";
|
|
8
|
+
import { evaluateRulesForHandler } from "./evaluator/visit.js";
|
|
9
|
+
import { ENGINE_VERSION } from "./version.js";
|
|
10
|
+
const VERDICT_RANK = {
|
|
11
|
+
verified: 0,
|
|
12
|
+
"manual-review": 1,
|
|
13
|
+
"not-verified": 2,
|
|
14
|
+
};
|
|
15
|
+
export async function evaluate(model, ruleSet, config) {
|
|
16
|
+
const findings = [];
|
|
17
|
+
// 1. Parse-error findings (D-27 + ENGINE-07) — exactly one per parse-error file, no rules run.
|
|
18
|
+
let parseErrorsCount = 0;
|
|
19
|
+
let parsedFilesCount = 0;
|
|
20
|
+
for (const file of model.parsed_files) {
|
|
21
|
+
if (file.parse_error !== null) {
|
|
22
|
+
parseErrorsCount++;
|
|
23
|
+
findings.push(await buildParseErrorFinding(file));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
parsedFilesCount++;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// 2. Per-handler rule evaluation. Update verification_state + findings_ref on each handler.
|
|
30
|
+
// DISCOVERY-01: this loop is the producer of ScanResult.inventory.
|
|
31
|
+
// D-57: build a rule_id → RuleDefinition lookup once per scan so each emitted finding can be
|
|
32
|
+
// run through applyPathSeverityOverrides before being pushed onto findings[].
|
|
33
|
+
const rulesById = new Map();
|
|
34
|
+
for (const r of ruleSet.rules)
|
|
35
|
+
rulesById.set(r.rule_id, r);
|
|
36
|
+
const inventory = [];
|
|
37
|
+
for (const handler of model.handlers) {
|
|
38
|
+
const out = await evaluateRulesForHandler({ handler, ruleSet, model });
|
|
39
|
+
for (const f of out.findings) {
|
|
40
|
+
const rule = rulesById.get(f.rule_id);
|
|
41
|
+
// D-57 path_severity_overrides applied post-emit; identity when rule has no overrides.
|
|
42
|
+
findings.push(rule ? applyPathSeverityOverrides(f, rule) : f);
|
|
43
|
+
}
|
|
44
|
+
// Pick the worst verdict including the existing manual-review baseline.
|
|
45
|
+
const baseline = handler.verification_state;
|
|
46
|
+
const worst = VERDICT_RANK[out.worst_state] >= VERDICT_RANK[baseline] ? out.worst_state : baseline;
|
|
47
|
+
inventory.push({
|
|
48
|
+
...handler,
|
|
49
|
+
verification_state: worst,
|
|
50
|
+
findings_ref: out.findings_ref,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// 3. Metadata (ENGINE-08, D-38).
|
|
54
|
+
const metadata = {
|
|
55
|
+
engine_version: ENGINE_VERSION,
|
|
56
|
+
engine_commit_sha: config.engine_commit_sha,
|
|
57
|
+
rule_pack_version: ruleSet.rule_pack_version,
|
|
58
|
+
rule_pack_content_hash: await computeRulePackContentHash(ruleSet),
|
|
59
|
+
scanned_at: config.scanned_at,
|
|
60
|
+
parse_errors_count: parseErrorsCount,
|
|
61
|
+
parsed_files_count: parsedFilesCount,
|
|
62
|
+
total_files_count: config.total_files_count,
|
|
63
|
+
parse_candidates_count: 0, // D-64 placeholder — Plan 09 (cli/src/pipeline.ts) overrides with walker-derived count.
|
|
64
|
+
};
|
|
65
|
+
return { findings, inventory, metadata };
|
|
66
|
+
}
|
|
67
|
+
// Same canonicalization algorithm as @hookwarden/rules' computeContentHash. Engine re-implements
|
|
68
|
+
// here to avoid a runtime dependency on the rules package (D-23 — engine consumes RuleSet, not
|
|
69
|
+
// the rules package itself).
|
|
70
|
+
async function computeRulePackContentHash(ruleSet) {
|
|
71
|
+
const rules = ruleSet.rules.map((r) => ({
|
|
72
|
+
schema_version: ruleSet.schema_version,
|
|
73
|
+
rule_id: r.rule_id,
|
|
74
|
+
provider: r.provider,
|
|
75
|
+
severity: r.severity,
|
|
76
|
+
emits_state: r.emits_state,
|
|
77
|
+
message: r.message,
|
|
78
|
+
matcher: r.matcher,
|
|
79
|
+
predicate: r.predicate_name,
|
|
80
|
+
applies_to: r.applies_to,
|
|
81
|
+
// B-3: new RuleDefinition fields MUST be canonicalized — Phase 12 evidence-pack
|
|
82
|
+
// export consumes this hash for reproducibility. Adding a field without updating
|
|
83
|
+
// this function silently breaks the contract.
|
|
84
|
+
provider_docs_url: r.provider_docs_url,
|
|
85
|
+
path_severity_overrides: r.path_severity_overrides,
|
|
86
|
+
}));
|
|
87
|
+
const payload = canonicalize({ providers: ruleSet.providers, rules });
|
|
88
|
+
return sha256Hex(payload);
|
|
89
|
+
}
|
|
90
|
+
function canonicalize(value) {
|
|
91
|
+
if (value === null || typeof value !== "object")
|
|
92
|
+
return JSON.stringify(value);
|
|
93
|
+
if (Array.isArray(value))
|
|
94
|
+
return `[${value.map(canonicalize).join(",")}]`;
|
|
95
|
+
const obj = value;
|
|
96
|
+
const keys = Object.keys(obj).sort();
|
|
97
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalize(obj[k])}`).join(",")}}`;
|
|
98
|
+
}
|
|
99
|
+
async function sha256Hex(input) {
|
|
100
|
+
const bytes = new TextEncoder().encode(input);
|
|
101
|
+
const buffer = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
102
|
+
const view = new Uint8Array(buffer);
|
|
103
|
+
let out = "";
|
|
104
|
+
for (let i = 0; i < view.length; i++)
|
|
105
|
+
out += (view[i] ?? 0).toString(16).padStart(2, "0");
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { type ApplyMatcherInput, applyMatcher } from "./matchers.js";
|
|
2
|
+
export { buildParseErrorFinding } from "./parse-error.js";
|
|
3
|
+
export { type EvaluateForHandlerInput, type EvaluateForHandlerOutput, evaluateRulesForHandler, } from "./visit.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/evaluator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,uBAAuB,GACxB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/evaluator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,YAAY,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAGL,uBAAuB,GACxB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Verdict } from "../types/finding.js";
|
|
2
|
+
import type { WebhookHandler } from "../types/handler.js";
|
|
3
|
+
import type { ProjectModel } from "../types/project-model.js";
|
|
4
|
+
import type { DeclarativeMatcher, ProviderCatalog } from "../types/rule-set.js";
|
|
5
|
+
export interface ApplyMatcherInput {
|
|
6
|
+
readonly matcher: DeclarativeMatcher;
|
|
7
|
+
readonly handler: WebhookHandler;
|
|
8
|
+
readonly model: ProjectModel;
|
|
9
|
+
readonly providers: ProviderCatalog;
|
|
10
|
+
readonly emits_state: Verdict;
|
|
11
|
+
}
|
|
12
|
+
export declare function applyMatcher(input: ApplyMatcherInput): Verdict | null;
|
|
13
|
+
//# sourceMappingURL=matchers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/evaluator/matchers.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEhF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,GAAG,IAAI,CAoErE"}
|