@sikkuumiii/defuse 0.0.0-stage → 0.5.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 (236) hide show
  1. package/LICENSE +235 -0
  2. package/README.md +561 -2
  3. package/dist/src/cli.d.ts +20 -0
  4. package/dist/src/cli.js +619 -0
  5. package/dist/src/cli.js.map +1 -0
  6. package/dist/src/core/analyze.d.ts +178 -0
  7. package/dist/src/core/analyze.js +834 -0
  8. package/dist/src/core/analyze.js.map +1 -0
  9. package/dist/src/core/coverage.d.ts +87 -0
  10. package/dist/src/core/coverage.js +181 -0
  11. package/dist/src/core/coverage.js.map +1 -0
  12. package/dist/src/core/finding.d.ts +196 -0
  13. package/dist/src/core/finding.js +178 -0
  14. package/dist/src/core/finding.js.map +1 -0
  15. package/dist/src/core/licence.d.ts +50 -0
  16. package/dist/src/core/licence.js +56 -0
  17. package/dist/src/core/licence.js.map +1 -0
  18. package/dist/src/core/paths.d.ts +21 -0
  19. package/dist/src/core/paths.js +58 -0
  20. package/dist/src/core/paths.js.map +1 -0
  21. package/dist/src/core/score.d.ts +137 -0
  22. package/dist/src/core/score.js +232 -0
  23. package/dist/src/core/score.js.map +1 -0
  24. package/dist/src/dast/targets.d.ts +30 -0
  25. package/dist/src/dast/targets.js +79 -0
  26. package/dist/src/dast/targets.js.map +1 -0
  27. package/dist/src/dast/zap.d.ts +42 -0
  28. package/dist/src/dast/zap.js +184 -0
  29. package/dist/src/dast/zap.js.map +1 -0
  30. package/dist/src/engine/scan.d.ts +80 -0
  31. package/dist/src/engine/scan.js +129 -0
  32. package/dist/src/engine/scan.js.map +1 -0
  33. package/dist/src/engine/shapes.d.ts +47 -0
  34. package/dist/src/engine/shapes.js +328 -0
  35. package/dist/src/engine/shapes.js.map +1 -0
  36. package/dist/src/engine/walk.d.ts +60 -0
  37. package/dist/src/engine/walk.js +156 -0
  38. package/dist/src/engine/walk.js.map +1 -0
  39. package/dist/src/parse/astDump.d.ts +31 -0
  40. package/dist/src/parse/astDump.js +66 -0
  41. package/dist/src/parse/astDump.js.map +1 -0
  42. package/dist/src/parse/languages.d.ts +70 -0
  43. package/dist/src/parse/languages.js +174 -0
  44. package/dist/src/parse/languages.js.map +1 -0
  45. package/dist/src/parse/location.d.ts +17 -0
  46. package/dist/src/parse/location.js +38 -0
  47. package/dist/src/parse/location.js.map +1 -0
  48. package/dist/src/parse/node-grammars.d.ts +12 -0
  49. package/dist/src/parse/node-grammars.js +35 -0
  50. package/dist/src/parse/node-grammars.js.map +1 -0
  51. package/dist/src/parse/parser.d.ts +126 -0
  52. package/dist/src/parse/parser.js +202 -0
  53. package/dist/src/parse/parser.js.map +1 -0
  54. package/dist/src/parse/tree-store.d.ts +139 -0
  55. package/dist/src/parse/tree-store.js +236 -0
  56. package/dist/src/parse/tree-store.js.map +1 -0
  57. package/dist/src/platform/dependency-rule.d.ts +27 -0
  58. package/dist/src/platform/dependency-rule.js +21 -0
  59. package/dist/src/platform/dependency-rule.js.map +1 -0
  60. package/dist/src/platform/deps.d.ts +86 -0
  61. package/dist/src/platform/deps.js +304 -0
  62. package/dist/src/platform/deps.js.map +1 -0
  63. package/dist/src/platform/php-facts.d.ts +49 -0
  64. package/dist/src/platform/php-facts.js +67 -0
  65. package/dist/src/platform/php-facts.js.map +1 -0
  66. package/dist/src/platform/runtime-version.d.ts +78 -0
  67. package/dist/src/platform/runtime-version.js +573 -0
  68. package/dist/src/platform/runtime-version.js.map +1 -0
  69. package/dist/src/report/colors.d.ts +50 -0
  70. package/dist/src/report/colors.js +97 -0
  71. package/dist/src/report/colors.js.map +1 -0
  72. package/dist/src/report/coverage-table.d.ts +90 -0
  73. package/dist/src/report/coverage-table.js +291 -0
  74. package/dist/src/report/coverage-table.js.map +1 -0
  75. package/dist/src/report/html-report.d.ts +66 -0
  76. package/dist/src/report/html-report.js +489 -0
  77. package/dist/src/report/html-report.js.map +1 -0
  78. package/dist/src/report/human.d.ts +44 -0
  79. package/dist/src/report/human.js +575 -0
  80. package/dist/src/report/human.js.map +1 -0
  81. package/dist/src/report/json.d.ts +24 -0
  82. package/dist/src/report/json.js +178 -0
  83. package/dist/src/report/json.js.map +1 -0
  84. package/dist/src/report/mark.d.ts +17 -0
  85. package/dist/src/report/mark.js +39 -0
  86. package/dist/src/report/mark.js.map +1 -0
  87. package/dist/src/report/pricing.d.ts +33 -0
  88. package/dist/src/report/pricing.js +343 -0
  89. package/dist/src/report/pricing.js.map +1 -0
  90. package/dist/src/report/sarif.d.ts +39 -0
  91. package/dist/src/report/sarif.js +219 -0
  92. package/dist/src/report/sarif.js.map +1 -0
  93. package/dist/src/report/site.d.ts +87 -0
  94. package/dist/src/report/site.js +742 -0
  95. package/dist/src/report/site.js.map +1 -0
  96. package/dist/src/rules/code-injection.d.ts +47 -0
  97. package/dist/src/rules/code-injection.js +326 -0
  98. package/dist/src/rules/code-injection.js.map +1 -0
  99. package/dist/src/rules/command-injection.d.ts +30 -0
  100. package/dist/src/rules/command-injection.js +286 -0
  101. package/dist/src/rules/command-injection.js.map +1 -0
  102. package/dist/src/rules/contract.d.ts +198 -0
  103. package/dist/src/rules/contract.js +39 -0
  104. package/dist/src/rules/contract.js.map +1 -0
  105. package/dist/src/rules/format-string.d.ts +46 -0
  106. package/dist/src/rules/format-string.js +146 -0
  107. package/dist/src/rules/format-string.js.map +1 -0
  108. package/dist/src/rules/hardcoded-secret.d.ts +38 -0
  109. package/dist/src/rules/hardcoded-secret.js +440 -0
  110. package/dist/src/rules/hardcoded-secret.js.map +1 -0
  111. package/dist/src/rules/lib/guards.d.ts +168 -0
  112. package/dist/src/rules/lib/guards.js +1179 -0
  113. package/dist/src/rules/lib/guards.js.map +1 -0
  114. package/dist/src/rules/lib/html-safety.d.ts +75 -0
  115. package/dist/src/rules/lib/html-safety.js +755 -0
  116. package/dist/src/rules/lib/html-safety.js.map +1 -0
  117. package/dist/src/rules/lib/local-build.d.ts +59 -0
  118. package/dist/src/rules/lib/local-build.js +320 -0
  119. package/dist/src/rules/lib/local-build.js.map +1 -0
  120. package/dist/src/rules/lib/strings.d.ts +208 -0
  121. package/dist/src/rules/lib/strings.js +1970 -0
  122. package/dist/src/rules/lib/strings.js.map +1 -0
  123. package/dist/src/rules/path-traversal.d.ts +33 -0
  124. package/dist/src/rules/path-traversal.js +96 -0
  125. package/dist/src/rules/path-traversal.js.map +1 -0
  126. package/dist/src/rules/registry.d.ts +19 -0
  127. package/dist/src/rules/registry.js +77 -0
  128. package/dist/src/rules/registry.js.map +1 -0
  129. package/dist/src/rules/sql-injection.d.ts +36 -0
  130. package/dist/src/rules/sql-injection.js +299 -0
  131. package/dist/src/rules/sql-injection.js.map +1 -0
  132. package/dist/src/rules/ssrf.d.ts +39 -0
  133. package/dist/src/rules/ssrf.js +111 -0
  134. package/dist/src/rules/ssrf.js.map +1 -0
  135. package/dist/src/rules/unbounded-copy.d.ts +37 -0
  136. package/dist/src/rules/unbounded-copy.js +147 -0
  137. package/dist/src/rules/unbounded-copy.js.map +1 -0
  138. package/dist/src/rules/unsafe-deserialization.d.ts +43 -0
  139. package/dist/src/rules/unsafe-deserialization.js +276 -0
  140. package/dist/src/rules/unsafe-deserialization.js.map +1 -0
  141. package/dist/src/rules/weak-hash.d.ts +37 -0
  142. package/dist/src/rules/weak-hash.js +197 -0
  143. package/dist/src/rules/weak-hash.js.map +1 -0
  144. package/dist/src/rules/xss.d.ts +38 -0
  145. package/dist/src/rules/xss.js +527 -0
  146. package/dist/src/rules/xss.js.map +1 -0
  147. package/dist/src/taint/content-type.d.ts +19 -0
  148. package/dist/src/taint/content-type.js +20 -0
  149. package/dist/src/taint/content-type.js.map +1 -0
  150. package/dist/src/taint/dictionaries.d.ts +23 -0
  151. package/dist/src/taint/dictionaries.js +1778 -0
  152. package/dist/src/taint/dictionaries.js.map +1 -0
  153. package/dist/src/taint/fold.d.ts +59 -0
  154. package/dist/src/taint/fold.js +1177 -0
  155. package/dist/src/taint/fold.js.map +1 -0
  156. package/dist/src/taint/java-scope.d.ts +61 -0
  157. package/dist/src/taint/java-scope.js +212 -0
  158. package/dist/src/taint/java-scope.js.map +1 -0
  159. package/dist/src/taint/local-map.d.ts +79 -0
  160. package/dist/src/taint/local-map.js +603 -0
  161. package/dist/src/taint/local-map.js.map +1 -0
  162. package/dist/src/taint/project.d.ts +172 -0
  163. package/dist/src/taint/project.js +617 -0
  164. package/dist/src/taint/project.js.map +1 -0
  165. package/dist/src/taint/tracer.d.ts +265 -0
  166. package/dist/src/taint/tracer.js +3817 -0
  167. package/dist/src/taint/tracer.js.map +1 -0
  168. package/dist/src/taint/types.d.ts +484 -0
  169. package/dist/src/taint/types.js +47 -0
  170. package/dist/src/taint/types.js.map +1 -0
  171. package/dist/src/taint/visibility.d.ts +25 -0
  172. package/dist/src/taint/visibility.js +96 -0
  173. package/dist/src/taint/visibility.js.map +1 -0
  174. package/docs/examples/defuse.json +20 -0
  175. package/docs/examples/dvwa-urls.json +14 -0
  176. package/docs/examples/github-code-scanning.yml +54 -0
  177. package/docs/examples/tiny.js +4 -0
  178. package/package.json +73 -4
  179. package/src/cli.ts +747 -0
  180. package/src/core/analyze.ts +1072 -0
  181. package/src/core/coverage.ts +269 -0
  182. package/src/core/finding.ts +311 -0
  183. package/src/core/licence.ts +61 -0
  184. package/src/core/paths.ts +59 -0
  185. package/src/core/score.ts +353 -0
  186. package/src/dast/targets.ts +121 -0
  187. package/src/dast/zap.ts +246 -0
  188. package/src/engine/scan.ts +200 -0
  189. package/src/engine/shapes.ts +348 -0
  190. package/src/engine/walk.ts +196 -0
  191. package/src/parse/astDump.ts +85 -0
  192. package/src/parse/languages.ts +232 -0
  193. package/src/parse/location.ts +44 -0
  194. package/src/parse/node-grammars.ts +41 -0
  195. package/src/parse/parser.ts +262 -0
  196. package/src/parse/tree-store.ts +301 -0
  197. package/src/platform/dependency-rule.ts +33 -0
  198. package/src/platform/deps.ts +374 -0
  199. package/src/platform/php-facts.ts +85 -0
  200. package/src/platform/runtime-version.ts +710 -0
  201. package/src/report/colors.ts +107 -0
  202. package/src/report/coverage-table.ts +359 -0
  203. package/src/report/html-report.ts +601 -0
  204. package/src/report/human.ts +811 -0
  205. package/src/report/json.ts +189 -0
  206. package/src/report/mark.ts +47 -0
  207. package/src/report/pricing.ts +374 -0
  208. package/src/report/sarif.ts +235 -0
  209. package/src/report/site.ts +760 -0
  210. package/src/rules/code-injection.ts +346 -0
  211. package/src/rules/command-injection.ts +296 -0
  212. package/src/rules/contract.ts +220 -0
  213. package/src/rules/format-string.ts +158 -0
  214. package/src/rules/hardcoded-secret.ts +485 -0
  215. package/src/rules/lib/guards.ts +1235 -0
  216. package/src/rules/lib/html-safety.ts +793 -0
  217. package/src/rules/lib/local-build.ts +329 -0
  218. package/src/rules/lib/strings.ts +2034 -0
  219. package/src/rules/path-traversal.ts +101 -0
  220. package/src/rules/registry.ts +90 -0
  221. package/src/rules/sql-injection.ts +311 -0
  222. package/src/rules/ssrf.ts +118 -0
  223. package/src/rules/unbounded-copy.ts +162 -0
  224. package/src/rules/unsafe-deserialization.ts +291 -0
  225. package/src/rules/weak-hash.ts +226 -0
  226. package/src/rules/xss.ts +572 -0
  227. package/src/taint/content-type.ts +20 -0
  228. package/src/taint/dictionaries.ts +1855 -0
  229. package/src/taint/fold.ts +1206 -0
  230. package/src/taint/java-scope.ts +200 -0
  231. package/src/taint/local-map.ts +594 -0
  232. package/src/taint/project.ts +774 -0
  233. package/src/taint/tracer.ts +4298 -0
  234. package/src/taint/types.ts +511 -0
  235. package/src/taint/visibility.ts +100 -0
  236. package/tsconfig.json +61 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * JAVA NAMES, AS JAVA RESOLVES THEM - THE PARTS THE CONSTANT PROOF NEEDS.
3
+ * =====================================================================
4
+ *
5
+ * The constant proof (rules/lib/strings.ts) withdraws a finding when it can
6
+ * show every value spliced into a query or command is fixed. That makes every
7
+ * mistake about WHICH declaration a name refers to a deleted finding, not a
8
+ * noisy one. An agent was set to break it, and in Java alone it found 32 ways
9
+ * (tests/probes/): a class hidden by a pattern variable, a static import, an
10
+ * enum constant or an inherited member type; an inherited overload; a local
11
+ * that is out of scope where the value is read; `final` written only in a
12
+ * comment; a `\u000a` that javac reads as a newline.
13
+ *
14
+ * This module does not resolve names. It answers narrower questions whose
15
+ * "no" is always safe - "is this name used ANY way but as a qualifier?", "does
16
+ * any class between here and there inherit from something?" - so a doubt ends
17
+ * the proof and leaves the finding standing.
18
+ */
19
+
20
+ import type { Node } from 'web-tree-sitter';
21
+
22
+ /** A modifier keyword on a declaration - the token itself, never a word in an annotation or comment. */
23
+ export function hasModifier(declaration: Node, keyword: string): boolean {
24
+ const modifiers = declaration.namedChildren.find((c): c is Node => c?.type === 'modifiers');
25
+ return !!modifiers && modifiers.children.some((c) => c?.type === keyword);
26
+ }
27
+
28
+ /** Declarations whose body is a class body: they can declare and inherit members. */
29
+ const CLASS_LIKE = new Set([
30
+ 'class_declaration', 'interface_declaration', 'enum_declaration', 'record_declaration',
31
+ 'annotation_type_declaration',
32
+ ]);
33
+
34
+ /** A class-like node, including `new T() { ... }` - an anonymous subclass of T. */
35
+ export function isClassLike(n: Node): boolean {
36
+ return (
37
+ CLASS_LIKE.has(n.type) ||
38
+ (n.type === 'object_creation_expression' && n.namedChildren.some((c) => c?.type === 'class_body'))
39
+ );
40
+ }
41
+
42
+ /** Class-like nodes strictly enclosing `n`, innermost first, stopping before `stop` if given. */
43
+ export function enclosingClassLikes(n: Node, stop?: Node): Node[] {
44
+ const out: Node[] = [];
45
+ for (let at = n.parent; at; at = at.parent) {
46
+ if (stop && at.startIndex === stop.startIndex && at.endIndex === stop.endIndex) break;
47
+ if (isClassLike(at)) out.push(at);
48
+ }
49
+ return out;
50
+ }
51
+
52
+ /**
53
+ * The types a class-like inherits from, as written: `extends B`, `implements
54
+ * I, J`, an interface's `extends`, and the T of an anonymous `new T() {}`.
55
+ * Generic arguments are dropped; a qualified name keeps its dots.
56
+ */
57
+ export function supertypesOf(classLike: Node): string[] {
58
+ const names: string[] = [];
59
+ const add = (t: Node | null | undefined): void => {
60
+ if (!t) return;
61
+ const text = (t.text ?? '').replace(/<[\s\S]*$/, '').replace(/\s+/g, '');
62
+ if (text) names.push(text);
63
+ };
64
+ if (classLike.type === 'object_creation_expression') {
65
+ add(classLike.childForFieldName('type'));
66
+ return names;
67
+ }
68
+ for (const child of classLike.namedChildren) {
69
+ if (!child) continue;
70
+ if (child.type === 'superclass' || child.type === 'super_interfaces' || child.type === 'extends_interfaces') {
71
+ const visit = (n: Node): void => {
72
+ if (n.type === 'type_identifier' || n.type === 'scoped_type_identifier' || n.type === 'generic_type') {
73
+ add(n);
74
+ return;
75
+ }
76
+ for (const c of n.namedChildren) if (c) visit(c);
77
+ };
78
+ visit(child);
79
+ }
80
+ }
81
+ return names;
82
+ }
83
+
84
+ /**
85
+ * Is every appearance of `name` in this file - outside its package and import
86
+ * lines - a QUALIFIER, as `name` is in `name.m()`, `name.f` or `@name.A`?
87
+ *
88
+ * A variable, parameter, field, pattern variable, lambda parameter, enum
89
+ * constant, label or type DECLARED with that name is some other kind of
90
+ * appearance, and any one of them could be what `name.m()` means here (JLS
91
+ * 6.4.2: a variable obscures a type, and a type a package). Reading the
92
+ * negative is what keeps this short: there are a dozen ways to declare a name
93
+ * in Java and only these few ways to use one as a qualifier.
94
+ */
95
+ export function usedOnlyAsQualifier(root: Node, name: string): boolean {
96
+ return cached(qualifierCache, root, name, () => walkQualifierUses(root, name));
97
+ }
98
+
99
+ /*
100
+ * Both whole-file questions are asked once per name per proof, and a scan
101
+ * asks thousands - so the answer is kept per tree and name. A tree is never
102
+ * edited, so it never goes stale.
103
+ */
104
+ const qualifierCache = new WeakMap<object, Map<string, boolean>>();
105
+ const bindingCache = new WeakMap<object, Map<string, boolean>>();
106
+ function cached(cache: WeakMap<object, Map<string, boolean>>, root: Node, name: string, compute: () => boolean): boolean {
107
+ const key = root.tree as object;
108
+ let perTree = cache.get(key);
109
+ if (!perTree) {
110
+ perTree = new Map();
111
+ cache.set(key, perTree);
112
+ }
113
+ let answer = perTree.get(name);
114
+ if (answer === undefined) {
115
+ answer = compute();
116
+ perTree.set(name, answer);
117
+ }
118
+ return answer;
119
+ }
120
+
121
+ function walkQualifierUses(root: Node, name: string): boolean {
122
+ const same = (a: Node | null, b: Node): boolean =>
123
+ !!a && a.startIndex === b.startIndex && a.endIndex === b.endIndex;
124
+ let ok = true;
125
+ const visit = (n: Node): void => {
126
+ if (!ok) return;
127
+ if (n.type === 'package_declaration' || n.type === 'import_declaration') return;
128
+ if (n.type === 'identifier' && n.text === name) {
129
+ const p = n.parent;
130
+ const qualifier =
131
+ !!p &&
132
+ (((p.type === 'method_invocation' || p.type === 'field_access') && same(p.childForFieldName('object'), n)) ||
133
+ (p.type === 'scoped_identifier' && same(p.childForFieldName('scope'), n)) ||
134
+ (p.type === 'field_access' && same(p.childForFieldName('field'), n)) ||
135
+ (p.type === 'method_invocation' && same(p.childForFieldName('name'), n)));
136
+ if (!qualifier) ok = false;
137
+ return;
138
+ }
139
+ for (const child of n.namedChildren) if (child) visit(child);
140
+ };
141
+ visit(root);
142
+ return ok;
143
+ }
144
+
145
+ /**
146
+ * Does the file bind `name` in a way the constant proof's write survey does
147
+ * not collect - a lambda parameter, a pattern variable (`instanceof T name`,
148
+ * `case T name ->`, a record pattern), a try-with-resources variable, a
149
+ * record component, or an enum constant? Any one of them can shadow the
150
+ * declaration the proof found (tests/probes/p15, p16, j01, j02).
151
+ */
152
+ export function boundElsewhere(root: Node, name: string): boolean {
153
+ return cached(bindingCache, root, name, () => walkBindings(root, name));
154
+ }
155
+
156
+ function walkBindings(root: Node, name: string): boolean {
157
+ let found = false;
158
+ const word = new RegExp(`(^|[^\\w$])${name.replace(/\$/g, '\\$')}(?![\\w$])`);
159
+ const visit = (n: Node): void => {
160
+ if (found) return;
161
+ if (n.type === 'lambda_expression') {
162
+ const params = n.childForFieldName('parameters');
163
+ if (params && word.test(params.text ?? '')) found = true;
164
+ } else if (n.type === 'instanceof_expression') {
165
+ if (n.childForFieldName('name')?.text === name) found = true;
166
+ } else if (n.type === 'type_pattern' || n.type === 'record_pattern_component') {
167
+ if (n.namedChildren.some((c) => c?.type === 'identifier' && c.text === name)) found = true;
168
+ } else if (n.type === 'resource' || n.type === 'enum_constant') {
169
+ if (n.childForFieldName('name')?.text === name) found = true;
170
+ } else if (n.type === 'record_declaration') {
171
+ const components = n.childForFieldName('parameters')?.namedChildren ?? [];
172
+ if (components.some((c) => c?.childForFieldName('name')?.text === name)) found = true;
173
+ }
174
+ if (!found) for (const child of n.namedChildren) if (child) visit(child);
175
+ };
176
+ visit(root);
177
+ return found;
178
+ }
179
+
180
+ /**
181
+ * Where a Java declaration of a variable is visible: a local from its own
182
+ * declaration to the end of the block that holds it (a `for` header's local,
183
+ * the whole `for`); a field or interface constant, its whole class body.
184
+ * Null for anything else - the caller treats that as "cannot place it".
185
+ */
186
+ export function declarationScope(declarator: Node): { region: Node; from: number } | null {
187
+ const holder = declarator.parent;
188
+ if (!holder) return null;
189
+ if (holder.type === 'local_variable_declaration') {
190
+ const region = holder.parent;
191
+ if (!region) return null;
192
+ return { region, from: declarator.endIndex };
193
+ }
194
+ if (holder.type === 'field_declaration' || holder.type === 'constant_declaration') {
195
+ const body = holder.parent;
196
+ if (!body) return null;
197
+ return { region: body, from: body.startIndex };
198
+ }
199
+ return null;
200
+ }