insa-check-core 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.
Files changed (258) hide show
  1. package/LICENSE +24 -0
  2. package/dist/annex-b.d.ts +6 -0
  3. package/dist/annex-b.d.ts.map +1 -0
  4. package/dist/annex-b.js +152 -0
  5. package/dist/annex-b.js.map +1 -0
  6. package/dist/checks/content/page.d.ts +7 -0
  7. package/dist/checks/content/page.d.ts.map +1 -0
  8. package/dist/checks/content/page.js +133 -0
  9. package/dist/checks/content/page.js.map +1 -0
  10. package/dist/checks/cookies/cookie-flags.d.ts +3 -0
  11. package/dist/checks/cookies/cookie-flags.d.ts.map +1 -0
  12. package/dist/checks/cookies/cookie-flags.js +41 -0
  13. package/dist/checks/cookies/cookie-flags.js.map +1 -0
  14. package/dist/checks/cookies/cookie-scope.d.ts +6 -0
  15. package/dist/checks/cookies/cookie-scope.d.ts.map +1 -0
  16. package/dist/checks/cookies/cookie-scope.js +60 -0
  17. package/dist/checks/cookies/cookie-scope.js.map +1 -0
  18. package/dist/checks/deployment/server-banner.d.ts +4 -0
  19. package/dist/checks/deployment/server-banner.d.ts.map +1 -0
  20. package/dist/checks/deployment/server-banner.js +60 -0
  21. package/dist/checks/deployment/server-banner.js.map +1 -0
  22. package/dist/checks/deployment/trace.d.ts +3 -0
  23. package/dist/checks/deployment/trace.d.ts.map +1 -0
  24. package/dist/checks/deployment/trace.js +30 -0
  25. package/dist/checks/deployment/trace.js.map +1 -0
  26. package/dist/checks/headers/clickjacking.d.ts +5 -0
  27. package/dist/checks/headers/clickjacking.d.ts.map +1 -0
  28. package/dist/checks/headers/clickjacking.js +57 -0
  29. package/dist/checks/headers/clickjacking.js.map +1 -0
  30. package/dist/checks/headers/content-type-options.d.ts +3 -0
  31. package/dist/checks/headers/content-type-options.d.ts.map +1 -0
  32. package/dist/checks/headers/content-type-options.js +17 -0
  33. package/dist/checks/headers/content-type-options.js.map +1 -0
  34. package/dist/checks/headers/cors.d.ts +4 -0
  35. package/dist/checks/headers/cors.d.ts.map +1 -0
  36. package/dist/checks/headers/cors.js +38 -0
  37. package/dist/checks/headers/cors.js.map +1 -0
  38. package/dist/checks/html.d.ts +11 -0
  39. package/dist/checks/html.d.ts.map +1 -0
  40. package/dist/checks/html.js +30 -0
  41. package/dist/checks/html.js.map +1 -0
  42. package/dist/checks/index.d.ts +7 -0
  43. package/dist/checks/index.d.ts.map +1 -0
  44. package/dist/checks/index.js +48 -0
  45. package/dist/checks/index.js.map +1 -0
  46. package/dist/checks/info-disclosure/directory-listing.d.ts +6 -0
  47. package/dist/checks/info-disclosure/directory-listing.d.ts.map +1 -0
  48. package/dist/checks/info-disclosure/directory-listing.js +60 -0
  49. package/dist/checks/info-disclosure/directory-listing.js.map +1 -0
  50. package/dist/checks/info-disclosure/exposed-files.d.ts +4 -0
  51. package/dist/checks/info-disclosure/exposed-files.d.ts.map +1 -0
  52. package/dist/checks/info-disclosure/exposed-files.js +56 -0
  53. package/dist/checks/info-disclosure/exposed-files.js.map +1 -0
  54. package/dist/checks/info-disclosure/more.d.ts +8 -0
  55. package/dist/checks/info-disclosure/more.d.ts.map +1 -0
  56. package/dist/checks/info-disclosure/more.js +186 -0
  57. package/dist/checks/info-disclosure/more.js.map +1 -0
  58. package/dist/checks/tls/certificate.d.ts +3 -0
  59. package/dist/checks/tls/certificate.d.ts.map +1 -0
  60. package/dist/checks/tls/certificate.js +49 -0
  61. package/dist/checks/tls/certificate.js.map +1 -0
  62. package/dist/checks/tls/hsts.d.ts +5 -0
  63. package/dist/checks/tls/hsts.d.ts.map +1 -0
  64. package/dist/checks/tls/hsts.js +35 -0
  65. package/dist/checks/tls/hsts.js.map +1 -0
  66. package/dist/checks/tls/https-redirect.d.ts +3 -0
  67. package/dist/checks/tls/https-redirect.d.ts.map +1 -0
  68. package/dist/checks/tls/https-redirect.js +46 -0
  69. package/dist/checks/tls/https-redirect.js.map +1 -0
  70. package/dist/checks/tls/legacy-protocols.d.ts +3 -0
  71. package/dist/checks/tls/legacy-protocols.d.ts.map +1 -0
  72. package/dist/checks/tls/legacy-protocols.js +27 -0
  73. package/dist/checks/tls/legacy-protocols.js.map +1 -0
  74. package/dist/checks/util.d.ts +22 -0
  75. package/dist/checks/util.d.ts.map +1 -0
  76. package/dist/checks/util.js +63 -0
  77. package/dist/checks/util.js.map +1 -0
  78. package/dist/code/checks/crypto.d.ts +5 -0
  79. package/dist/code/checks/crypto.d.ts.map +1 -0
  80. package/dist/code/checks/crypto.js +100 -0
  81. package/dist/code/checks/crypto.js.map +1 -0
  82. package/dist/code/checks/django.d.ts +14 -0
  83. package/dist/code/checks/django.d.ts.map +1 -0
  84. package/dist/code/checks/django.js +313 -0
  85. package/dist/code/checks/django.js.map +1 -0
  86. package/dist/code/checks/flask.d.ts +6 -0
  87. package/dist/code/checks/flask.d.ts.map +1 -0
  88. package/dist/code/checks/flask.js +100 -0
  89. package/dist/code/checks/flask.js.map +1 -0
  90. package/dist/code/checks/index.d.ts +9 -0
  91. package/dist/code/checks/index.d.ts.map +1 -0
  92. package/dist/code/checks/index.js +126 -0
  93. package/dist/code/checks/index.js.map +1 -0
  94. package/dist/code/checks/injection.d.ts +27 -0
  95. package/dist/code/checks/injection.d.ts.map +1 -0
  96. package/dist/code/checks/injection.js +189 -0
  97. package/dist/code/checks/injection.js.map +1 -0
  98. package/dist/code/checks/javascript-config.d.ts +14 -0
  99. package/dist/code/checks/javascript-config.d.ts.map +1 -0
  100. package/dist/code/checks/javascript-config.js +251 -0
  101. package/dist/code/checks/javascript-config.js.map +1 -0
  102. package/dist/code/checks/javascript.d.ts +34 -0
  103. package/dist/code/checks/javascript.d.ts.map +1 -0
  104. package/dist/code/checks/javascript.js +333 -0
  105. package/dist/code/checks/javascript.js.map +1 -0
  106. package/dist/code/checks/php.d.ts +27 -0
  107. package/dist/code/checks/php.d.ts.map +1 -0
  108. package/dist/code/checks/php.js +433 -0
  109. package/dist/code/checks/php.js.map +1 -0
  110. package/dist/code/checks/secrets.d.ts +10 -0
  111. package/dist/code/checks/secrets.d.ts.map +1 -0
  112. package/dist/code/checks/secrets.js +140 -0
  113. package/dist/code/checks/secrets.js.map +1 -0
  114. package/dist/code/checks/shared.d.ts +25 -0
  115. package/dist/code/checks/shared.d.ts.map +1 -0
  116. package/dist/code/checks/shared.js +57 -0
  117. package/dist/code/checks/shared.js.map +1 -0
  118. package/dist/code/checks/treesitter.d.ts +15 -0
  119. package/dist/code/checks/treesitter.d.ts.map +1 -0
  120. package/dist/code/checks/treesitter.js +322 -0
  121. package/dist/code/checks/treesitter.js.map +1 -0
  122. package/dist/code/checks/xml.d.ts +9 -0
  123. package/dist/code/checks/xml.d.ts.map +1 -0
  124. package/dist/code/checks/xml.js +78 -0
  125. package/dist/code/checks/xml.js.map +1 -0
  126. package/dist/code/context.d.ts +6 -0
  127. package/dist/code/context.d.ts.map +1 -0
  128. package/dist/code/context.js +92 -0
  129. package/dist/code/context.js.map +1 -0
  130. package/dist/code/detect.d.ts +3 -0
  131. package/dist/code/detect.d.ts.map +1 -0
  132. package/dist/code/detect.js +118 -0
  133. package/dist/code/detect.js.map +1 -0
  134. package/dist/code/django/settings.d.ts +46 -0
  135. package/dist/code/django/settings.d.ts.map +1 -0
  136. package/dist/code/django/settings.js +154 -0
  137. package/dist/code/django/settings.js.map +1 -0
  138. package/dist/code/findings.d.ts +18 -0
  139. package/dist/code/findings.d.ts.map +1 -0
  140. package/dist/code/findings.js +63 -0
  141. package/dist/code/findings.js.map +1 -0
  142. package/dist/code/javascript/ast.d.ts +46 -0
  143. package/dist/code/javascript/ast.d.ts.map +1 -0
  144. package/dist/code/javascript/ast.js +166 -0
  145. package/dist/code/javascript/ast.js.map +1 -0
  146. package/dist/code/javascript/index.d.ts +3 -0
  147. package/dist/code/javascript/index.d.ts.map +1 -0
  148. package/dist/code/javascript/index.js +3 -0
  149. package/dist/code/javascript/index.js.map +1 -0
  150. package/dist/code/javascript/module.d.ts +59 -0
  151. package/dist/code/javascript/module.d.ts.map +1 -0
  152. package/dist/code/javascript/module.js +250 -0
  153. package/dist/code/javascript/module.js.map +1 -0
  154. package/dist/code/php/index.d.ts +92 -0
  155. package/dist/code/php/index.d.ts.map +1 -0
  156. package/dist/code/php/index.js +316 -0
  157. package/dist/code/php/index.js.map +1 -0
  158. package/dist/code/project.d.ts +13 -0
  159. package/dist/code/project.d.ts.map +1 -0
  160. package/dist/code/project.js +69 -0
  161. package/dist/code/project.js.map +1 -0
  162. package/dist/code/python/ast.d.ts +47 -0
  163. package/dist/code/python/ast.d.ts.map +1 -0
  164. package/dist/code/python/ast.js +193 -0
  165. package/dist/code/python/ast.js.map +1 -0
  166. package/dist/code/python/index.d.ts +3 -0
  167. package/dist/code/python/index.d.ts.map +1 -0
  168. package/dist/code/python/index.js +3 -0
  169. package/dist/code/python/index.js.map +1 -0
  170. package/dist/code/python/module.d.ts +55 -0
  171. package/dist/code/python/module.d.ts.map +1 -0
  172. package/dist/code/python/module.js +215 -0
  173. package/dist/code/python/module.js.map +1 -0
  174. package/dist/code/scan.d.ts +24 -0
  175. package/dist/code/scan.d.ts.map +1 -0
  176. package/dist/code/scan.js +159 -0
  177. package/dist/code/scan.js.map +1 -0
  178. package/dist/code/treesitter/index.d.ts +83 -0
  179. package/dist/code/treesitter/index.d.ts.map +1 -0
  180. package/dist/code/treesitter/index.js +243 -0
  181. package/dist/code/treesitter/index.js.map +1 -0
  182. package/dist/code/types.d.ts +48 -0
  183. package/dist/code/types.d.ts.map +1 -0
  184. package/dist/code/types.js +2 -0
  185. package/dist/code/types.js.map +1 -0
  186. package/dist/content/en.d.ts +3 -0
  187. package/dist/content/en.d.ts.map +1 -0
  188. package/dist/content/en.js +722 -0
  189. package/dist/content/en.js.map +1 -0
  190. package/dist/content/index.d.ts +15 -0
  191. package/dist/content/index.d.ts.map +1 -0
  192. package/dist/content/index.js +21 -0
  193. package/dist/content/index.js.map +1 -0
  194. package/dist/content/types.d.ts +87 -0
  195. package/dist/content/types.d.ts.map +1 -0
  196. package/dist/content/types.js +2 -0
  197. package/dist/content/types.js.map +1 -0
  198. package/dist/engine.d.ts +52 -0
  199. package/dist/engine.d.ts.map +1 -0
  200. package/dist/engine.js +120 -0
  201. package/dist/engine.js.map +1 -0
  202. package/dist/index.d.ts +12 -0
  203. package/dist/index.d.ts.map +1 -0
  204. package/dist/index.js +11 -0
  205. package/dist/index.js.map +1 -0
  206. package/dist/meta.d.ts +4 -0
  207. package/dist/meta.d.ts.map +1 -0
  208. package/dist/meta.js +4 -0
  209. package/dist/meta.js.map +1 -0
  210. package/dist/net/context.d.ts +9 -0
  211. package/dist/net/context.d.ts.map +1 -0
  212. package/dist/net/context.js +62 -0
  213. package/dist/net/context.js.map +1 -0
  214. package/dist/net/guard.d.ts +16 -0
  215. package/dist/net/guard.d.ts.map +1 -0
  216. package/dist/net/guard.js +70 -0
  217. package/dist/net/guard.js.map +1 -0
  218. package/dist/net/http.d.ts +22 -0
  219. package/dist/net/http.d.ts.map +1 -0
  220. package/dist/net/http.js +114 -0
  221. package/dist/net/http.js.map +1 -0
  222. package/dist/net/tls.d.ts +15 -0
  223. package/dist/net/tls.d.ts.map +1 -0
  224. package/dist/net/tls.js +103 -0
  225. package/dist/net/tls.js.map +1 -0
  226. package/dist/report/html.d.ts +14 -0
  227. package/dist/report/html.d.ts.map +1 -0
  228. package/dist/report/html.js +210 -0
  229. package/dist/report/html.js.map +1 -0
  230. package/dist/report/index.d.ts +10 -0
  231. package/dist/report/index.d.ts.map +1 -0
  232. package/dist/report/index.js +10 -0
  233. package/dist/report/index.js.map +1 -0
  234. package/dist/report/manual.d.ts +62 -0
  235. package/dist/report/manual.d.ts.map +1 -0
  236. package/dist/report/manual.js +190 -0
  237. package/dist/report/manual.js.map +1 -0
  238. package/dist/report/merge.d.ts +8 -0
  239. package/dist/report/merge.d.ts.map +1 -0
  240. package/dist/report/merge.js +135 -0
  241. package/dist/report/merge.js.map +1 -0
  242. package/dist/report/summary.d.ts +10 -0
  243. package/dist/report/summary.d.ts.map +1 -0
  244. package/dist/report/summary.js +27 -0
  245. package/dist/report/summary.js.map +1 -0
  246. package/dist/report/validate.d.ts +3 -0
  247. package/dist/report/validate.d.ts.map +1 -0
  248. package/dist/report/validate.js +78 -0
  249. package/dist/report/validate.js.map +1 -0
  250. package/dist/types.d.ts +244 -0
  251. package/dist/types.d.ts.map +1 -0
  252. package/dist/types.js +2 -0
  253. package/dist/types.js.map +1 -0
  254. package/dist/validate.d.ts +4 -0
  255. package/dist/validate.d.ts.map +1 -0
  256. package/dist/validate.js +24 -0
  257. package/dist/validate.js.map +1 -0
  258. package/package.json +40 -0
@@ -0,0 +1,193 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createInterface } from "node:readline";
3
+ const HELPER = String.raw `
4
+ import ast, json, os, sys, warnings
5
+ warnings.simplefilter("ignore")
6
+ sys.setrecursionlimit(20000)
7
+ def conv(n):
8
+ if isinstance(n, ast.AST):
9
+ d = {"_t": type(n).__name__}
10
+ if hasattr(n, "lineno"):
11
+ d["_l"] = n.lineno; d["_c"] = n.col_offset
12
+ d["_el"] = getattr(n, "end_lineno", None); d["_ec"] = getattr(n, "end_col_offset", None)
13
+ for f, v in ast.iter_fields(n):
14
+ if f in ("ctx", "type_comment"): continue
15
+ if isinstance(n, ast.Constant) and f == "value":
16
+ if isinstance(v, bytes): d["value"] = v.decode("latin-1"); d["_bytes"] = True; continue
17
+ if isinstance(v, (complex,)) or v is Ellipsis: d["value"] = repr(v); continue
18
+ if isinstance(v, int) and not isinstance(v, bool) and abs(v) > 2**53: d["value"] = float(v); continue
19
+ d[f] = conv(v)
20
+ return d
21
+ if isinstance(n, list): return [conv(x) for x in n]
22
+ if n is None or isinstance(n, (str, int, float, bool)): return n
23
+ return repr(n)
24
+ req = json.load(sys.stdin)
25
+ for rel in req["files"]:
26
+ try:
27
+ with open(os.path.join(req["root"], rel), "rb") as fh: src = fh.read()
28
+ out = {"file": rel, "ast": conv(ast.parse(src, filename=rel))}
29
+ except SyntaxError as e:
30
+ out = {"file": rel, "error": "%s (line %s)" % (e.msg, e.lineno)}
31
+ except (RecursionError, MemoryError, ValueError) as e:
32
+ out = {"file": rel, "error": type(e).__name__}
33
+ sys.stdout.write(json.dumps(out) + "\n"); sys.stdout.flush()
34
+ `;
35
+ export class PythonUnavailableError extends Error {
36
+ code = "ENOPYTHON";
37
+ constructor(detail) {
38
+ super(`python3 is required to analyse Python code but could not be run (${detail}). Install Python 3 or set INSA_CHECK_PYTHON.`);
39
+ }
40
+ }
41
+ export function pythonExecutable() {
42
+ return process.env["INSA_CHECK_PYTHON"] ?? "python3";
43
+ }
44
+ /** Parses project files (relative paths) with the local Python interpreter. */
45
+ export function parsePythonFiles(root, files, timeoutMs = 120_000) {
46
+ return new Promise((resolve, reject) => {
47
+ const child = spawn(pythonExecutable(), ["-I", "-c", HELPER], { stdio: ["pipe", "pipe", "pipe"] });
48
+ const modules = new Map();
49
+ const errors = [];
50
+ let stderr = "";
51
+ const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
52
+ child.on("error", (err) => {
53
+ clearTimeout(timer);
54
+ reject(new PythonUnavailableError(err.code ?? err.message));
55
+ });
56
+ child.stderr.on("data", (d) => (stderr = (stderr + d.toString()).slice(-2000)));
57
+ createInterface({ input: child.stdout }).on("line", (line) => {
58
+ const msg = JSON.parse(line);
59
+ if (msg.ast)
60
+ modules.set(msg.file, msg.ast);
61
+ else
62
+ errors.push({ file: msg.file, error: msg.error ?? "unknown error" });
63
+ });
64
+ child.on("close", (code) => {
65
+ clearTimeout(timer);
66
+ if (code === 0)
67
+ resolve({ modules, errors });
68
+ else
69
+ reject(new Error(`Python parser exited with code ${code}: ${stderr.trim().split("\n").pop() ?? ""}`));
70
+ });
71
+ child.stdin.end(JSON.stringify({ root, files }));
72
+ });
73
+ }
74
+ // --- Tree helpers ------------------------------------------------------------
75
+ export function isNode(x) {
76
+ return typeof x === "object" && x !== null && typeof x._t === "string";
77
+ }
78
+ export function childNodes(node) {
79
+ const out = [];
80
+ for (const [k, v] of Object.entries(node)) {
81
+ if (k.startsWith("_"))
82
+ continue;
83
+ if (isNode(v))
84
+ out.push(v);
85
+ else if (Array.isArray(v))
86
+ for (const x of v)
87
+ if (isNode(x))
88
+ out.push(x);
89
+ }
90
+ return out;
91
+ }
92
+ /** Depth-first, source order. `skip` prunes a subtree (e.g. nested functions). */
93
+ export function* walk(root, skip) {
94
+ const stack = [root];
95
+ while (stack.length) {
96
+ const node = stack.pop();
97
+ yield node;
98
+ const kids = childNodes(node);
99
+ for (let i = kids.length - 1; i >= 0; i--)
100
+ if (!skip?.(kids[i]))
101
+ stack.push(kids[i]);
102
+ }
103
+ }
104
+ const FUNCTION_TYPES = new Set(["FunctionDef", "AsyncFunctionDef", "Lambda"]);
105
+ export const isFunction = (n) => FUNCTION_TYPES.has(n._t);
106
+ /** "a.b.c" for Name/Attribute chains; calls and subscripts in the chain become "()" and "[]". */
107
+ export function dotted(node) {
108
+ if (!isNode(node))
109
+ return undefined;
110
+ switch (node._t) {
111
+ case "Name":
112
+ return node["id"];
113
+ case "Attribute": {
114
+ const base = dotted(node["value"]);
115
+ return base === undefined ? undefined : `${base}.${node["attr"]}`;
116
+ }
117
+ case "Call": {
118
+ const base = dotted(node["func"]);
119
+ return base === undefined ? undefined : `${base}()`;
120
+ }
121
+ case "Subscript": {
122
+ const base = dotted(node["value"]);
123
+ return base === undefined ? undefined : `${base}[]`;
124
+ }
125
+ default:
126
+ return undefined;
127
+ }
128
+ }
129
+ export function strConst(node) {
130
+ return isNode(node) && node._t === "Constant" && typeof node["value"] === "string" && !node["_bytes"]
131
+ ? node["value"]
132
+ : undefined;
133
+ }
134
+ export function isConstant(node) {
135
+ return isNode(node) && node._t === "Constant";
136
+ }
137
+ export function callArgs(call) {
138
+ return call["args"] ?? [];
139
+ }
140
+ export function kwarg(call, name) {
141
+ const kw = (call["keywords"] ?? []).find((k) => k["arg"] === name);
142
+ return kw?.["value"];
143
+ }
144
+ /** `**something` arguments of a call. */
145
+ export function starKwargs(call) {
146
+ return (call["keywords"] ?? []).filter((k) => k["arg"] === null).map((k) => k["value"]);
147
+ }
148
+ export function isTrue(node) {
149
+ return isNode(node) && node._t === "Constant" && node["value"] === true;
150
+ }
151
+ /** f-string with placeholders, "%" formatting, "+" concatenation with a string, or "...".format(...). */
152
+ export function isDynamicString(node) {
153
+ if (!isNode(node))
154
+ return false;
155
+ if (node._t === "JoinedStr")
156
+ return (node["values"] ?? []).some((v) => v._t === "FormattedValue");
157
+ if (node._t === "BinOp") {
158
+ const op = node["op"]._t;
159
+ const left = node["left"];
160
+ const right = node["right"];
161
+ if (op === "Mod")
162
+ return strConst(left) !== undefined || isDynamicString(left);
163
+ if (op === "Add") {
164
+ const stringy = (n) => strConst(n) !== undefined || isDynamicString(n);
165
+ return (stringy(left) || stringy(right)) && !(strConst(left) !== undefined && strConst(right) !== undefined);
166
+ }
167
+ return false;
168
+ }
169
+ if (node._t === "Call") {
170
+ const func = node["func"];
171
+ return isNode(func) && func._t === "Attribute" && func["attr"] === "format" && strConst(func["value"]) !== undefined;
172
+ }
173
+ return false;
174
+ }
175
+ /** Identifiers used in an expression (Name ids, attribute names, constant subscript keys). */
176
+ export function identifiers(node) {
177
+ const out = [];
178
+ for (const n of walk(node)) {
179
+ if (n._t === "Name")
180
+ out.push(n["id"]);
181
+ else if (n._t === "Attribute")
182
+ out.push(n["attr"]);
183
+ else if (n._t === "Subscript") {
184
+ const key = strConst(n["slice"]);
185
+ if (key)
186
+ out.push(key);
187
+ }
188
+ else if (n._t === "keyword" && typeof n["arg"] === "string")
189
+ out.push(n["arg"]);
190
+ }
191
+ return out;
192
+ }
193
+ //# sourceMappingURL=ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.js","sourceRoot":"","sources":["../../../src/code/python/ast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from "./ast.js";
2
+ export * from "./module.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/code/python/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./ast.js";
2
+ export * from "./module.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/code/python/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { type PyNode } from "./ast.js";
2
+ export interface FunctionScope {
3
+ /** Names holding client-controlled data (request data, URL parameters of views, and values derived from them). */
4
+ tainted: Set<string>;
5
+ /** Names assigned a dynamically built string (f-string, %, +, .format). */
6
+ dynamicStrings: Set<string>;
7
+ /** Qualified names of every call made in the function. */
8
+ calls: Set<string>;
9
+ }
10
+ export interface CallSite {
11
+ module: PyModule;
12
+ node: PyNode;
13
+ /** Qualified dotted name with imports resolved, e.g. "subprocess.run", "hashlib.md5", "cursor.execute". */
14
+ name: string | undefined;
15
+ /** Last segment: method or function name ("execute", "md5"). */
16
+ method: string | undefined;
17
+ /** Innermost enclosing function, if any. */
18
+ fn: PyNode | undefined;
19
+ }
20
+ export declare class PyModule {
21
+ readonly path: string;
22
+ readonly tree: PyNode;
23
+ readonly lines: string[];
24
+ /** Local alias -> fully qualified module/name, from every import statement in the file. */
25
+ readonly imports: Map<string, string>;
26
+ private readonly scopes;
27
+ constructor(path: string, tree: PyNode, source: string);
28
+ /** Resolves the first segment of a dotted name through this file's imports. */
29
+ qualify(name: string | undefined): string | undefined;
30
+ line(n: number | undefined): string;
31
+ /** Exact source text of a node (columns are UTF-8 byte offsets). */
32
+ text(node: PyNode): string;
33
+ /** True when the expression contains client-controlled data, given the scope's tainted names. */
34
+ isRequestDerived(node: unknown, scope?: FunctionScope): boolean;
35
+ /** Lightweight intra-procedural taint: request data, view URL params, and names assigned from them. */
36
+ scope(fn: PyNode | undefined): FunctionScope | undefined;
37
+ }
38
+ export declare class PythonIndex {
39
+ readonly modules: PyModule[];
40
+ readonly parseErrors: {
41
+ file: string;
42
+ error: string;
43
+ }[];
44
+ readonly byPath: Map<string, PyModule>;
45
+ private callSites?;
46
+ constructor(modules: PyModule[], parseErrors: {
47
+ file: string;
48
+ error: string;
49
+ }[]);
50
+ /** Every call in the project with its resolved name and enclosing function. Computed once. */
51
+ calls(): CallSite[];
52
+ /** Whether any module imports something under the given dotted prefix (e.g. "defusedxml"). */
53
+ imports(prefix: string): boolean;
54
+ }
55
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/code/python/module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AA6B1F,MAAM,WAAW,aAAa;IAC5B,kHAAkH;IAClH,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,2EAA2E;IAC3E,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,0DAA0D;IAC1D,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,2GAA2G;IAC3G,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,gEAAgE;IAChE,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,4CAA4C;IAC5C,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;CACxB;AAED,qBAAa,QAAQ;IAOjB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;IAPvB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,sBAA6B;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAE/D,YACW,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EAmBf;IAED,+EAA+E;IAC/E,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKpD;IAED,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAElC;IAED,oEAAoE;IACpE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzB;IAED,iGAAiG;IACjG,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAO9D;IAED,uGAAuG;IACvG,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAkDvD;CACF;AAED,qBAAa,WAAW;IAKpB,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE;IAC5B,QAAQ,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE;IALzD,QAAQ,CAAC,MAAM,wBAA+B;IAC9C,OAAO,CAAC,SAAS,CAAC,CAAa;IAE/B,YACW,OAAO,EAAE,QAAQ,EAAE,EACnB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,EAGxD;IAED,8FAA8F;IAC9F,KAAK,IAAI,QAAQ,EAAE,CA2BlB;IAED,8FAA8F;IAC9F,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE/B;CACF"}
@@ -0,0 +1,215 @@
1
+ import { dotted, isDynamicString, isFunction, isNode, walk } from "./ast.js";
2
+ /** Attributes of a Django/DRF request that carry client-controlled data. */
3
+ /** Client-controlled request attributes: Django/DRF (GET, POST, FILES...) and Flask/Starlette (args, form, json...). */
4
+ const REQUEST_DATA = /^(self\.)?request\.(GET|POST|FILES|data|query_params|body|COOKIES|META|headers|content_params|path_info|path|args|form|values|json|files|cookies|get_json|get_data|view_args|stream|path_params)$/;
5
+ /** Flask / FastAPI route decorators: @app.route(...), @bp.get(...), @router.post(...). */
6
+ const ROUTE_DECORATOR = /\.(route|get|post|put|patch|delete|websocket)$/;
7
+ /**
8
+ * Nodes whose value can flow into the expression's result. Skips parts that only produce booleans or
9
+ * pick between branches: comparisons, `not`, and the test of a conditional expression.
10
+ */
11
+ function* valueNodes(root) {
12
+ const stack = [root];
13
+ while (stack.length) {
14
+ const n = stack.pop();
15
+ if (n._t === "Compare")
16
+ continue;
17
+ if (n._t === "UnaryOp" && n["op"]._t === "Not")
18
+ continue;
19
+ yield n;
20
+ for (const [k, v] of Object.entries(n)) {
21
+ if (k.startsWith("_") || (n._t === "IfExp" && k === "test"))
22
+ continue;
23
+ if (isNode(v))
24
+ stack.push(v);
25
+ else if (Array.isArray(v))
26
+ for (const x of v)
27
+ if (isNode(x))
28
+ stack.push(x);
29
+ }
30
+ }
31
+ }
32
+ export class PyModule {
33
+ path;
34
+ tree;
35
+ lines;
36
+ /** Local alias -> fully qualified module/name, from every import statement in the file. */
37
+ imports = new Map();
38
+ scopes = new WeakMap();
39
+ constructor(path, tree, source) {
40
+ this.path = path;
41
+ this.tree = tree;
42
+ this.lines = source.split(/\r?\n/);
43
+ for (const n of walk(tree)) {
44
+ if (n._t === "Import") {
45
+ for (const a of n["names"]) {
46
+ const name = a["name"];
47
+ const asname = a["asname"];
48
+ if (asname)
49
+ this.imports.set(asname, name);
50
+ else
51
+ this.imports.set(name.split(".")[0], name.split(".")[0]);
52
+ }
53
+ }
54
+ else if (n._t === "ImportFrom") {
55
+ const mod = `${".".repeat(n["level"] ?? 0)}${n["module"] ?? ""}`;
56
+ for (const a of n["names"]) {
57
+ const name = a["name"];
58
+ this.imports.set(a["asname"] ?? name, mod ? `${mod}.${name}` : name);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ /** Resolves the first segment of a dotted name through this file's imports. */
64
+ qualify(name) {
65
+ if (!name)
66
+ return undefined;
67
+ const [head, ...rest] = name.split(".");
68
+ const resolved = this.imports.get(head);
69
+ return resolved ? [resolved, ...rest].join(".") : name;
70
+ }
71
+ line(n) {
72
+ return n ? (this.lines[n - 1] ?? "") : "";
73
+ }
74
+ /** Exact source text of a node (columns are UTF-8 byte offsets). */
75
+ text(node) {
76
+ const { _l: l, _c: c, _el: el, _ec: ec } = node;
77
+ if (!l || c === undefined || !el || ec === undefined)
78
+ return "";
79
+ if (l === el)
80
+ return Buffer.from(this.line(l)).subarray(c, ec).toString();
81
+ const parts = [Buffer.from(this.line(l)).subarray(c).toString()];
82
+ for (let i = l + 1; i < el; i++)
83
+ parts.push(this.line(i));
84
+ parts.push(Buffer.from(this.line(el)).subarray(0, ec).toString());
85
+ return parts.join("\n");
86
+ }
87
+ /** True when the expression contains client-controlled data, given the scope's tainted names. */
88
+ isRequestDerived(node, scope) {
89
+ if (!isNode(node))
90
+ return false;
91
+ for (const n of valueNodes(node)) {
92
+ if (n._t === "Name" && scope?.tainted.has(n["id"]))
93
+ return true;
94
+ if (n._t === "Attribute" && REQUEST_DATA.test(dotted(n) ?? ""))
95
+ return true;
96
+ }
97
+ return false;
98
+ }
99
+ /** Lightweight intra-procedural taint: request data, view URL params, and names assigned from them. */
100
+ scope(fn) {
101
+ if (!fn)
102
+ return undefined;
103
+ let scope = this.scopes.get(fn);
104
+ if (scope)
105
+ return scope;
106
+ scope = { tainted: new Set(), dynamicStrings: new Set(), calls: new Set() };
107
+ this.scopes.set(fn, scope);
108
+ // URL parameters of Django views (def view(request, slug) / def get(self, request, pk)) are client-controlled.
109
+ const args = (fn["args"]?.["args"] ?? []).map((a) => a["arg"]);
110
+ const reqIndex = args.indexOf("request");
111
+ const isRoute = (fn["decorator_list"] ?? []).some((d) => ROUTE_DECORATOR.test(dotted(d._t === "Call" ? d["func"] : d) ?? ""));
112
+ if (isRoute) {
113
+ // Flask / FastAPI route handlers: parameters come from the URL, query string or body.
114
+ // Parameters whose default is Depends(...) are injected dependencies, not client input.
115
+ const argNodes = fn["args"]?.["args"] ?? [];
116
+ const defaults = fn["args"]?.["defaults"] ?? [];
117
+ const firstDefault = argNodes.length - defaults.length;
118
+ argNodes.forEach((a, i) => {
119
+ const def = i >= firstDefault ? defaults[i - firstDefault] : undefined;
120
+ const injected = def?._t === "Call" && /(^|\.)Depends$/.test(dotted(def["func"]) ?? "");
121
+ const name = a["arg"];
122
+ if (!injected && !["self", "cls", "request", "response", "db", "session", "background_tasks"].includes(name))
123
+ scope.tainted.add(name);
124
+ });
125
+ }
126
+ if (reqIndex === 0 || (reqIndex === 1 && (args[0] === "self" || args[0] === "cls"))) {
127
+ for (const a of args.slice(reqIndex + 1))
128
+ scope.tainted.add(a);
129
+ const kw = fn["args"]?.["kwarg"];
130
+ if (kw)
131
+ scope.tainted.add(kw["arg"]);
132
+ }
133
+ const body = fn["body"] ?? [];
134
+ const roots = Array.isArray(body) ? body : [body];
135
+ const nodes = roots.flatMap((r) => [...walk(r, isFunction)]);
136
+ // Two passes so that assignments inside loops propagate.
137
+ for (let pass = 0; pass < 2; pass++) {
138
+ for (const n of nodes) {
139
+ let targets = [];
140
+ let value;
141
+ if (n._t === "Assign")
142
+ [targets, value] = [n["targets"], n["value"]];
143
+ else if (n._t === "AnnAssign" || n._t === "AugAssign" || n._t === "NamedExpr")
144
+ [targets, value] = [[n["target"]], n["value"]];
145
+ else if (n._t === "For" || n._t === "AsyncFor" || n._t === "comprehension")
146
+ [targets, value] = [[n["target"]], n["iter"]];
147
+ else if (n._t === "withitem" && n["optional_vars"])
148
+ [targets, value] = [[n["optional_vars"]], n["context_expr"]];
149
+ else
150
+ continue;
151
+ const names = targets.flatMap((t) => [...walk(t)].filter((x) => x._t === "Name").map((x) => x["id"]));
152
+ if (this.isRequestDerived(value, scope))
153
+ names.forEach((x) => scope.tainted.add(x));
154
+ if (isDynamicString(value))
155
+ names.forEach((x) => scope.dynamicStrings.add(x));
156
+ }
157
+ }
158
+ for (const n of nodes)
159
+ if (n._t === "Call")
160
+ scope.calls.add(this.qualify(dotted(n["func"])) ?? "");
161
+ return scope;
162
+ }
163
+ }
164
+ export class PythonIndex {
165
+ modules;
166
+ parseErrors;
167
+ byPath = new Map();
168
+ callSites;
169
+ constructor(modules, parseErrors) {
170
+ this.modules = modules;
171
+ this.parseErrors = parseErrors;
172
+ for (const m of modules)
173
+ this.byPath.set(m.path, m);
174
+ }
175
+ /** Every call in the project with its resolved name and enclosing function. Computed once. */
176
+ calls() {
177
+ if (this.callSites)
178
+ return this.callSites;
179
+ const sites = [];
180
+ for (const module of this.modules) {
181
+ const visit = (node, fn) => {
182
+ const stack = [[node, fn]];
183
+ while (stack.length) {
184
+ const [n, f] = stack.pop();
185
+ if (n._t === "Call") {
186
+ const name = module.qualify(dotted(n["func"]));
187
+ const func = n["func"];
188
+ const method = func._t === "Attribute" ? func["attr"] : func._t === "Name" ? func["id"] : undefined;
189
+ sites.push({ module, node: n, name, method, fn: f });
190
+ }
191
+ const inner = isFunction(n) ? n : f;
192
+ for (const [k, v] of Object.entries(n)) {
193
+ if (k.startsWith("_"))
194
+ continue;
195
+ if (isNode(v))
196
+ stack.push([v, inner]);
197
+ else if (Array.isArray(v))
198
+ for (const x of v)
199
+ if (isNode(x))
200
+ stack.push([x, inner]);
201
+ }
202
+ }
203
+ };
204
+ visit(module.tree, undefined);
205
+ }
206
+ sites.sort((a, b) => a.module.path.localeCompare(b.module.path) || (a.node._l ?? 0) - (b.node._l ?? 0));
207
+ this.callSites = sites;
208
+ return sites;
209
+ }
210
+ /** Whether any module imports something under the given dotted prefix (e.g. "defusedxml"). */
211
+ imports(prefix) {
212
+ return this.modules.some((m) => [...m.imports.values()].some((v) => v === prefix || v.startsWith(`${prefix}.`)));
213
+ }
214
+ }
215
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../../../src/code/python/module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAe,MAAM,UAAU,CAAC;AAE1F,4EAA4E;AAC5E,wHAAwH;AACxH,MAAM,YAAY,GAChB,mMAAmM,CAAC;AAEtM,0FAA0F;AAC1F,MAAM,eAAe,GAAG,gDAAgD,CAAC;AAEzE;;;GAGG;AACH,QAAQ,CAAC,CAAC,UAAU,CAAC,IAAY;IAC/B,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QACvB,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS;YAAE,SAAS;QACjC,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,IAAK,CAAC,CAAC,IAAI,CAAY,CAAC,EAAE,KAAK,KAAK;YAAE,SAAS;QACrE,MAAM,CAAC,CAAC;QACR,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM,CAAC;gBAAE,SAAS;YACtE,IAAI,MAAM,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;iBACxB,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBAAE,KAAK,MAAM,CAAC,IAAI,CAAC;oBAAE,IAAI,MAAM,CAAC,CAAC,CAAC;wBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;AACH,CAAC;AAsBD,MAAM,OAAO,QAAQ;IAOR,IAAI;IACJ,IAAI;IAPN,KAAK,CAAW;IACzB,2FAA2F;IAClF,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5B,MAAM,GAAG,IAAI,OAAO,EAAyB,CAAC;IAE/D,YACW,IAAY,EACZ,IAAY,EACrB,MAAc;oBAFL,IAAI;oBACJ,IAAI;QAGb,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBACtB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAa,EAAE,CAAC;oBACvC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAW,CAAC;oBACjC,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAkB,CAAC;oBAC5C,IAAI,MAAM;wBAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;;wBACtC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;iBAAM,IAAI,CAAC,CAAC,EAAE,KAAK,YAAY,EAAE,CAAC;gBACjC,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAE,CAAC,CAAC,OAAO,CAAY,IAAI,CAAC,CAAC,GAAI,CAAC,CAAC,QAAQ,CAAmB,IAAI,EAAE,EAAE,CAAC;gBAChG,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAa,EAAE,CAAC;oBACvC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAW,CAAC;oBACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAE,CAAC,CAAC,QAAQ,CAAmB,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC1F,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,OAAO,CAAC,IAAwB;QAC9B,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAC5B,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAK,CAAC,CAAC;QACzC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,CAAC;IAED,IAAI,CAAC,CAAqB;QACxB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5C,CAAC;IAED,oEAAoE;IACpE,IAAI,CAAC,IAAY;QACf,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;QAChD,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAChE,IAAI,CAAC,KAAK,EAAE;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1E,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjE,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,iGAAiG;IACjG,gBAAgB,CAAC,IAAa,EAAE,KAAqB;QACnD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,IAAI,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAW,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC1E,IAAI,CAAC,CAAC,EAAE,KAAK,WAAW,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC9E,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,uGAAuG;IACvG,KAAK,CAAC,EAAsB;QAC1B,IAAI,CAAC,EAAE;YAAE,OAAO,SAAS,CAAC;QAC1B,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,cAAc,EAAE,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QAC5E,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAE3B,+GAA+G;QAC/G,MAAM,IAAI,GAAG,CAAG,EAAE,CAAC,MAAM,CAAY,EAAE,CAAC,MAAM,CAAc,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAW,CAAC,CAAC;QACnG,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,CAAE,EAAE,CAAC,gBAAgB,CAAc,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5I,IAAI,OAAO,EAAE,CAAC;YACZ,sFAAsF;YACtF,wFAAwF;YACxF,MAAM,QAAQ,GAAK,EAAE,CAAC,MAAM,CAAY,EAAE,CAAC,MAAM,CAAc,IAAI,EAAE,CAAC;YACtE,MAAM,QAAQ,GAAK,EAAE,CAAC,MAAM,CAAY,EAAE,CAAC,UAAU,CAAc,IAAI,EAAE,CAAC;YAC1E,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YACvD,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACxB,MAAM,GAAG,GAAG,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACvE,MAAM,QAAQ,GAAG,GAAG,EAAE,EAAE,KAAK,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBACxF,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAW,CAAC;gBAChC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAAE,KAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACzI,CAAC,CAAC,CAAC;QACL,CAAC;QACD,IAAI,QAAQ,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACpF,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC;gBAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC/D,MAAM,EAAE,GAAI,EAAE,CAAC,MAAM,CAAY,EAAE,CAAC,OAAO,CAAkB,CAAC;YAC9D,IAAI,EAAE;gBAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAW,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,IAAI,GAAI,EAAE,CAAC,MAAM,CAAuB,IAAI,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7D,yDAAyD;QACzD,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;YACpC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,IAAI,OAAO,GAAa,EAAE,CAAC;gBAC3B,IAAI,KAAc,CAAC;gBACnB,IAAI,CAAC,CAAC,EAAE,KAAK,QAAQ;oBAAE,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAa,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;qBAC5E,IAAI,CAAC,CAAC,EAAE,KAAK,WAAW,IAAI,CAAC,CAAC,EAAE,KAAK,WAAW,IAAI,CAAC,CAAC,EAAE,KAAK,WAAW;oBAAE,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAW,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;qBACnI,IAAI,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,EAAE,KAAK,UAAU,IAAI,CAAC,CAAC,EAAE,KAAK,eAAe;oBAAE,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAW,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;qBAC/H,IAAI,CAAC,CAAC,EAAE,KAAK,UAAU,IAAI,CAAC,CAAC,eAAe,CAAC;oBAAE,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAW,CAAC,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;;oBACtH,SAAS;gBACd,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAW,CAAC,CAAC,CAAC;gBAChH,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;oBAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrF,IAAI,eAAe,CAAC,KAAK,CAAC;oBAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACjF,CAAC;QACH,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM;gBAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnG,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAED,MAAM,OAAO,WAAW;IAKX,OAAO;IACP,WAAW;IALb,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IACtC,SAAS,CAAc;IAE/B,YACW,OAAmB,EACnB,WAA8C;uBAD9C,OAAO;2BACP,WAAW;QAEpB,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,8FAA8F;IAC9F,KAAK;QACH,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC;QAC1C,MAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAsB,EAAE,EAAE;gBACrD,MAAM,KAAK,GAAmC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC3D,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;oBACpB,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;oBAC5B,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;wBACpB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;wBAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAW,CAAC;wBACjC,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC,CAAE,IAAI,CAAC,MAAM,CAAY,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAE,IAAI,CAAC,IAAI,CAAY,CAAC,CAAC,CAAC,SAAS,CAAC;wBAC5H,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;oBACvD,CAAC;oBACD,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACpC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;wBACvC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;4BAAE,SAAS;wBAChC,IAAI,MAAM,CAAC,CAAC,CAAC;4BAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;6BACjC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;4BAAE,KAAK,MAAM,CAAC,IAAI,CAAC;gCAAE,IAAI,MAAM,CAAC,CAAC,CAAC;oCAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;oBACtF,CAAC;gBACH,CAAC;YACH,CAAC,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACxG,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8FAA8F;IAC9F,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACnH,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ import type { AnnexItem, CheckResult, Report } from "../types.js";
2
+ import type { CodeCheck, FrameworkId } from "./types.js";
3
+ export interface CodeScanOptions {
4
+ /** Force a framework instead of auto-detecting it. */
5
+ framework?: FrameworkId;
6
+ /** Per-check time budget. Default 60 s. */
7
+ checkTimeoutMs?: number;
8
+ /** Default 20 000. */
9
+ maxFiles?: number;
10
+ /** Files larger than this are skipped. Default 1 MB. */
11
+ maxFileBytes?: number;
12
+ registry?: readonly AnnexItem[];
13
+ checks?: ReadonlyMap<string, CodeCheck>;
14
+ onResult?: (result: CheckResult) => void;
15
+ }
16
+ export declare class CodeScanError extends Error {
17
+ name: string;
18
+ }
19
+ /**
20
+ * Tier 2: statically analyse a local project directory. Source code is read from disk
21
+ * and parsed locally; nothing is sent anywhere. Returns the same 102-row Report as runScan().
22
+ */
23
+ export declare function runCodeScan(dir: string, options?: CodeScanOptions): Promise<Report>;
24
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/code/scan.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAgB,WAAW,EAAE,MAAM,EAAU,MAAM,aAAa,CAAC;AAMxF,OAAO,KAAK,EAAE,SAAS,EAAe,WAAW,EAAY,MAAM,YAAY,CAAC;AAEhF,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;CAC1C;AAED,qBAAa,aAAc,SAAQ,KAAK;IAC7B,IAAI,SAAmB;CACjC;AA8BD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0G7F"}