hullproof 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 (363) hide show
  1. package/LICENSE +222 -0
  2. package/NOTICE +32 -0
  3. package/README.md +313 -2
  4. package/checks/db.firebase-rules-not-open.json +24 -0
  5. package/checks/db.rls-every-table.json +25 -0
  6. package/checks/log.no-credential-in-logs.json +30 -0
  7. package/checks/secrets.no-env-file-committed.json +31 -0
  8. package/checks/secrets.no-secret-in-public-env.json +37 -0
  9. package/checks/storage.firebase-rules-not-open.json +22 -0
  10. package/checks/supply.no-untrusted-event-in-run.json +23 -0
  11. package/checks/web.cors-no-reflected-origin.json +31 -0
  12. package/detectors/ai.json +25 -0
  13. package/detectors/auth.json +27 -0
  14. package/detectors/ci.json +19 -0
  15. package/detectors/containers.json +8 -0
  16. package/detectors/data_stores.json +38 -0
  17. package/detectors/frameworks.json +94 -0
  18. package/detectors/hosting.json +44 -0
  19. package/detectors/iac.json +43 -0
  20. package/detectors/languages.json +48 -0
  21. package/detectors/mobile.json +22 -0
  22. package/detectors/package_managers.json +39 -0
  23. package/detectors/payments.json +19 -0
  24. package/detectors/runtimes.json +17 -0
  25. package/dist/checks/code/admin-role.js +46 -0
  26. package/dist/checks/code/owner-filter.js +91 -0
  27. package/dist/checks/code/shared.js +84 -0
  28. package/dist/checks/code/webhook-verify.js +77 -0
  29. package/dist/checks/context.js +64 -0
  30. package/dist/checks/map/config-checks.js +74 -0
  31. package/dist/checks/map/entry-checks.js +97 -0
  32. package/dist/checks/map/shared.js +44 -0
  33. package/dist/checks/ops.js +307 -0
  34. package/dist/checks/pro-map.js +1 -0
  35. package/dist/checks/run.js +92 -0
  36. package/dist/checks/types.js +1 -0
  37. package/dist/ci/comment.js +44 -0
  38. package/dist/ci/sarif-check.js +157 -0
  39. package/dist/ci/sarif.js +61 -0
  40. package/dist/cli.js +358 -0
  41. package/dist/commands/ci.js +115 -0
  42. package/dist/commands/git.js +58 -0
  43. package/dist/commands/hooks.js +30 -0
  44. package/dist/commands/hunt-pro.js +1 -0
  45. package/dist/commands/hunt.js +86 -0
  46. package/dist/commands/map.js +33 -0
  47. package/dist/commands/mcp.js +17 -0
  48. package/dist/commands/pack.js +48 -0
  49. package/dist/commands/prove-pro.js +1 -0
  50. package/dist/commands/prove.js +173 -0
  51. package/dist/commands/report-gate.js +92 -0
  52. package/dist/commands/scan.js +158 -0
  53. package/dist/confirm/agreement.js +18 -0
  54. package/dist/confirm/chain.js +84 -0
  55. package/dist/confirm/confirm.js +112 -0
  56. package/dist/confirm/honesty.js +75 -0
  57. package/dist/confirm/lead.js +92 -0
  58. package/dist/confirm/merge.js +88 -0
  59. package/dist/confirm/query.js +145 -0
  60. package/dist/confirm/rule.js +86 -0
  61. package/dist/confirm/store.js +42 -0
  62. package/dist/confirm/summary.js +6 -0
  63. package/dist/confirm/synthesis.js +28 -0
  64. package/dist/discover/detectors.js +50 -0
  65. package/dist/discover/discover.js +270 -0
  66. package/dist/discover/extract.js +282 -0
  67. package/dist/discover/git.js +40 -0
  68. package/dist/discover/kit-files.js +47 -0
  69. package/dist/discover/walk.js +110 -0
  70. package/dist/env.js +57 -0
  71. package/dist/gate/gate.js +173 -0
  72. package/dist/hooks/install.js +153 -0
  73. package/dist/hooks/run.js +112 -0
  74. package/dist/map/analyze.js +201 -0
  75. package/dist/map/build.js +253 -0
  76. package/dist/map/candidates.js +49 -0
  77. package/dist/map/cblocks.js +77 -0
  78. package/dist/map/config.js +291 -0
  79. package/dist/map/dotnet.js +242 -0
  80. package/dist/map/entry.js +78 -0
  81. package/dist/map/expo.js +190 -0
  82. package/dist/map/go.js +292 -0
  83. package/dist/map/js-express.js +143 -0
  84. package/dist/map/js-misc.js +295 -0
  85. package/dist/map/js-next.js +292 -0
  86. package/dist/map/laravel.js +389 -0
  87. package/dist/map/lex-more.js +305 -0
  88. package/dist/map/lex.js +425 -0
  89. package/dist/map/light-index.js +191 -0
  90. package/dist/map/light-profiles.js +324 -0
  91. package/dist/map/light.js +574 -0
  92. package/dist/map/local-vocab.js +27 -0
  93. package/dist/map/mobile.js +326 -0
  94. package/dist/map/py-django.js +281 -0
  95. package/dist/map/py.js +391 -0
  96. package/dist/map/rails.js +391 -0
  97. package/dist/map/repo.js +178 -0
  98. package/dist/map/rust.js +312 -0
  99. package/dist/map/spring.js +207 -0
  100. package/dist/map/taint.js +96 -0
  101. package/dist/map/touches.js +355 -0
  102. package/dist/map/vocab.js +88 -0
  103. package/dist/mapping/controls.js +34 -0
  104. package/dist/mapping/dependency-policy.js +44 -0
  105. package/dist/mapping/map.js +44 -0
  106. package/dist/mapping/maps/gitleaks.json +12 -0
  107. package/dist/mapping/maps/osv-scanner.json +7 -0
  108. package/dist/mapping/maps/trivy.json +13 -0
  109. package/dist/mcp/confine.js +59 -0
  110. package/dist/mcp/protocol.js +140 -0
  111. package/dist/mcp/schema.js +43 -0
  112. package/dist/mcp/server.js +37 -0
  113. package/dist/mcp/tools.js +238 -0
  114. package/dist/packs/coverage.js +79 -0
  115. package/dist/packs/manifest.js +36 -0
  116. package/dist/packs/validate.js +271 -0
  117. package/dist/prove/container.js +276 -0
  118. package/dist/prove/exec-main.js +20 -0
  119. package/dist/prove/executor.js +149 -0
  120. package/dist/prove/local.js +131 -0
  121. package/dist/prove/probes.js +65 -0
  122. package/dist/prove/recipe.js +278 -0
  123. package/dist/prove/run.js +136 -0
  124. package/dist/prove/runner.js +1 -0
  125. package/dist/prove/safety.js +126 -0
  126. package/dist/prove/spec.js +357 -0
  127. package/dist/prove/store.js +65 -0
  128. package/dist/report/build.js +297 -0
  129. package/dist/report/entry-points.js +75 -0
  130. package/dist/report/hunt-section.js +71 -0
  131. package/dist/report/kit.js +41 -0
  132. package/dist/report/load.js +35 -0
  133. package/dist/report/messages.js +273 -0
  134. package/dist/report/prove-section.js +65 -0
  135. package/dist/report/report.js +156 -0
  136. package/dist/report/surface.js +21 -0
  137. package/dist/report/test-code.js +51 -0
  138. package/dist/report/tiers.js +44 -0
  139. package/dist/scanners/common.js +162 -0
  140. package/dist/scanners/gitleaks.js +90 -0
  141. package/dist/scanners/index.js +113 -0
  142. package/dist/scanners/osv-scanner.js +77 -0
  143. package/dist/scanners/run-targets.js +34 -0
  144. package/dist/scanners/secret-noise.js +89 -0
  145. package/dist/scanners/semgrep.js +142 -0
  146. package/dist/scanners/trivy.js +65 -0
  147. package/dist/scanners/types.js +1 -0
  148. package/dist/types/check.js +1 -0
  149. package/dist/types/codemap.js +1 -0
  150. package/dist/types/findings.js +5 -0
  151. package/dist/types/hunt.js +1 -0
  152. package/dist/types/index.js +1 -0
  153. package/dist/types/prove.js +1 -0
  154. package/dist/types/results.js +2 -0
  155. package/dist/types/scan.js +1 -0
  156. package/dist/types/stack.js +5 -0
  157. package/dist/util.js +71 -0
  158. package/dist/version.js +2 -0
  159. package/package.json +46 -4
  160. package/packs/android/fixture-result.json +43 -0
  161. package/packs/android/pack.json +56 -0
  162. package/packs/dotnet/fixture-result.json +62 -0
  163. package/packs/dotnet/pack.json +88 -0
  164. package/packs/expo/answer-key.json +51 -0
  165. package/packs/expo/fixture-result.json +62 -0
  166. package/packs/expo/pack.json +79 -0
  167. package/packs/firebase/answer-key.json +27 -0
  168. package/packs/firebase/fixture-result.json +43 -0
  169. package/packs/firebase/pack.json +58 -0
  170. package/packs/go/answer-key.json +101 -0
  171. package/packs/go/fixture-result.json +64 -0
  172. package/packs/go/pack.json +99 -0
  173. package/packs/ios/fixture-result.json +43 -0
  174. package/packs/ios/pack.json +55 -0
  175. package/packs/laravel/answer-key.json +84 -0
  176. package/packs/laravel/fixture-result.json +60 -0
  177. package/packs/laravel/pack.json +91 -0
  178. package/packs/node-ts/answer-key.json +220 -0
  179. package/packs/node-ts/fixture-result.json +114 -0
  180. package/packs/node-ts/pack.json +134 -0
  181. package/packs/python/answer-key.json +195 -0
  182. package/packs/python/fixture-result.json +119 -0
  183. package/packs/python/pack.json +121 -0
  184. package/packs/rails/answer-key.json +84 -0
  185. package/packs/rails/fixture-result.json +60 -0
  186. package/packs/rails/pack.json +78 -0
  187. package/packs/rust/fixture-result.json +43 -0
  188. package/packs/rust/pack.json +69 -0
  189. package/packs/spring/fixture-result.json +63 -0
  190. package/packs/spring/pack.json +103 -0
  191. package/packs/supabase/answer-key.json +52 -0
  192. package/packs/supabase/fixture-result.json +58 -0
  193. package/packs/supabase/pack.json +60 -0
  194. package/rules/gitleaks.toml +256 -0
  195. package/rules/semgrep/api-route-no-auth.yaml +31 -0
  196. package/rules/semgrep/cors-reflect-origin.yaml +48 -0
  197. package/rules/semgrep/eval-or-new-function.yaml +185 -0
  198. package/rules/semgrep/log-session-token.yaml +48 -0
  199. package/rules/semgrep/math-random-tokens.yaml +24 -0
  200. package/rules/semgrep/next-public-secret-name.yaml +32 -0
  201. package/rules/semgrep/postgrest-filter-injection.yaml +78 -0
  202. package/rules/semgrep/raw-sql-concat.yaml +42 -0
  203. package/rules/semgrep/read-by-id-no-owner.yaml +49 -0
  204. package/rules/semgrep/server-action-no-auth.yaml +26 -0
  205. package/rules/semgrep/sql-variants.yaml +99 -0
  206. package/rules/semgrep/ssrf-unguarded-url.yaml +137 -0
  207. package/rules/semgrep/subprocess-injection.yaml +163 -0
  208. package/rules/semgrep/taint-go.yaml +333 -0
  209. package/rules/semgrep/taint-java.yaml +317 -0
  210. package/rules/semgrep/taint-javascript.yaml +412 -0
  211. package/rules/semgrep/taint-php.yaml +361 -0
  212. package/rules/semgrep/taint-python.yaml +414 -0
  213. package/rules/semgrep/tool-endpoint-admin-client.yaml +86 -0
  214. package/rules/semgrep/user-metadata-authz.yaml +22 -0
  215. package/rules/semgrep/verify-result-unused.yaml +62 -0
  216. package/rules/semgrep/webhook-parse-before-verify.yaml +29 -0
  217. package/rules/test-artifact-ids.json +4 -0
  218. package/rules/tiers.json +427 -0
  219. package/schemas/check-results.schema.json +55 -0
  220. package/schemas/check.schema.json +41 -0
  221. package/schemas/codemap.schema.json +216 -0
  222. package/schemas/findings.schema.json +1048 -0
  223. package/schemas/hunt-output.schema.json +46 -0
  224. package/schemas/hunt-result.schema.json +58 -0
  225. package/schemas/results.schema.json +17 -0
  226. package/schemas/scan-hits.schema.json +32 -0
  227. package/schemas/scan-meta.schema.json +25 -0
  228. package/schemas/stack.schema.json +93 -0
  229. package/src/checks/README.md +32 -0
  230. package/src/checks/code/admin-role.ts +49 -0
  231. package/src/checks/code/owner-filter.ts +95 -0
  232. package/src/checks/code/shared.ts +66 -0
  233. package/src/checks/code/webhook-verify.ts +75 -0
  234. package/src/checks/context.ts +72 -0
  235. package/src/checks/map/config-checks.ts +79 -0
  236. package/src/checks/map/entry-checks.ts +116 -0
  237. package/src/checks/map/shared.ts +63 -0
  238. package/src/checks/ops.ts +280 -0
  239. package/src/checks/pro-map.ts +4 -0
  240. package/src/checks/run.ts +80 -0
  241. package/src/checks/types.ts +12 -0
  242. package/src/ci/comment.ts +52 -0
  243. package/src/ci/sarif-check.ts +100 -0
  244. package/src/ci/sarif.ts +72 -0
  245. package/src/cli.ts +273 -0
  246. package/src/commands/ci.ts +104 -0
  247. package/src/commands/git.ts +59 -0
  248. package/src/commands/hooks.ts +27 -0
  249. package/src/commands/hunt-pro.ts +4 -0
  250. package/src/commands/hunt.ts +68 -0
  251. package/src/commands/map.ts +27 -0
  252. package/src/commands/mcp.ts +18 -0
  253. package/src/commands/pack.ts +34 -0
  254. package/src/commands/prove-pro.ts +4 -0
  255. package/src/commands/prove.ts +126 -0
  256. package/src/commands/report-gate.ts +75 -0
  257. package/src/commands/scan.ts +156 -0
  258. package/src/confirm/agreement.ts +23 -0
  259. package/src/confirm/chain.ts +67 -0
  260. package/src/confirm/confirm.ts +107 -0
  261. package/src/confirm/honesty.ts +65 -0
  262. package/src/confirm/lead.ts +76 -0
  263. package/src/confirm/merge.ts +89 -0
  264. package/src/confirm/query.ts +126 -0
  265. package/src/confirm/rule.ts +76 -0
  266. package/src/confirm/store.ts +42 -0
  267. package/src/confirm/summary.ts +9 -0
  268. package/src/confirm/synthesis.ts +33 -0
  269. package/src/discover/detectors.ts +71 -0
  270. package/src/discover/discover.ts +253 -0
  271. package/src/discover/extract.ts +235 -0
  272. package/src/discover/git.ts +31 -0
  273. package/src/discover/kit-files.ts +37 -0
  274. package/src/discover/walk.ts +88 -0
  275. package/src/env.ts +51 -0
  276. package/src/gate/gate.ts +169 -0
  277. package/src/hooks/install.ts +142 -0
  278. package/src/hooks/run.ts +121 -0
  279. package/src/map/analyze.ts +219 -0
  280. package/src/map/build.ts +206 -0
  281. package/src/map/candidates.ts +47 -0
  282. package/src/map/cblocks.ts +55 -0
  283. package/src/map/config.ts +207 -0
  284. package/src/map/dotnet.ts +205 -0
  285. package/src/map/entry.ts +80 -0
  286. package/src/map/expo.ts +171 -0
  287. package/src/map/go.ts +271 -0
  288. package/src/map/js-express.ts +135 -0
  289. package/src/map/js-misc.ts +221 -0
  290. package/src/map/js-next.ts +282 -0
  291. package/src/map/laravel.ts +327 -0
  292. package/src/map/lex-more.ts +152 -0
  293. package/src/map/lex.ts +274 -0
  294. package/src/map/light-index.ts +134 -0
  295. package/src/map/light-profiles.ts +363 -0
  296. package/src/map/light.ts +484 -0
  297. package/src/map/local-vocab.ts +30 -0
  298. package/src/map/mobile.ts +263 -0
  299. package/src/map/py-django.ts +239 -0
  300. package/src/map/py.ts +329 -0
  301. package/src/map/rails.ts +291 -0
  302. package/src/map/repo.ts +150 -0
  303. package/src/map/rust.ts +256 -0
  304. package/src/map/spring.ts +185 -0
  305. package/src/map/taint.ts +86 -0
  306. package/src/map/touches.ts +296 -0
  307. package/src/map/vocab.ts +106 -0
  308. package/src/mapping/controls.ts +47 -0
  309. package/src/mapping/dependency-policy.ts +55 -0
  310. package/src/mapping/map.ts +59 -0
  311. package/src/mapping/maps/gitleaks.json +12 -0
  312. package/src/mapping/maps/osv-scanner.json +7 -0
  313. package/src/mapping/maps/trivy.json +13 -0
  314. package/src/mcp/confine.ts +44 -0
  315. package/src/mcp/protocol.ts +144 -0
  316. package/src/mcp/schema.ts +35 -0
  317. package/src/mcp/server.ts +32 -0
  318. package/src/mcp/tools.ts +235 -0
  319. package/src/packs/coverage.ts +98 -0
  320. package/src/packs/manifest.ts +97 -0
  321. package/src/packs/validate.ts +204 -0
  322. package/src/prove/container.ts +237 -0
  323. package/src/prove/exec-main.ts +16 -0
  324. package/src/prove/executor.ts +130 -0
  325. package/src/prove/local.ts +100 -0
  326. package/src/prove/probes.ts +68 -0
  327. package/src/prove/recipe.ts +215 -0
  328. package/src/prove/run.ts +124 -0
  329. package/src/prove/runner.ts +28 -0
  330. package/src/prove/safety.ts +98 -0
  331. package/src/prove/spec.ts +256 -0
  332. package/src/prove/store.ts +48 -0
  333. package/src/report/build.ts +304 -0
  334. package/src/report/entry-points.ts +78 -0
  335. package/src/report/hunt-section.ts +67 -0
  336. package/src/report/kit.ts +59 -0
  337. package/src/report/load.ts +49 -0
  338. package/src/report/messages.ts +270 -0
  339. package/src/report/prove-section.ts +61 -0
  340. package/src/report/report.ts +160 -0
  341. package/src/report/surface.ts +32 -0
  342. package/src/report/test-code.ts +56 -0
  343. package/src/report/tiers.ts +50 -0
  344. package/src/scanners/common.ts +149 -0
  345. package/src/scanners/gitleaks.ts +89 -0
  346. package/src/scanners/index.ts +103 -0
  347. package/src/scanners/osv-scanner.ts +90 -0
  348. package/src/scanners/run-targets.ts +35 -0
  349. package/src/scanners/secret-noise.ts +80 -0
  350. package/src/scanners/semgrep.ts +139 -0
  351. package/src/scanners/trivy.ts +66 -0
  352. package/src/scanners/types.ts +98 -0
  353. package/src/types/check.ts +52 -0
  354. package/src/types/codemap.ts +142 -0
  355. package/src/types/findings.ts +107 -0
  356. package/src/types/hunt.ts +97 -0
  357. package/src/types/index.ts +10 -0
  358. package/src/types/prove.ts +83 -0
  359. package/src/types/results.ts +15 -0
  360. package/src/types/scan.ts +63 -0
  361. package/src/types/stack.ts +61 -0
  362. package/src/util.ts +59 -0
  363. package/src/version.ts +2 -0
@@ -0,0 +1,412 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Copyright 2026 Rare Phronesis Limited
3
+ # Written for the Hullproof Engine from the CWE definitions named in each rule. Not copied or derived from any Semgrep
4
+ # registry rule. Fixtures: test/fixtures/rules/taint-javascript.js and taint-javascript.tsx, run by test/rules.test.ts.
5
+ #
6
+ # Sources are request data in Express, Koa, Next.js (route handlers, API routes, page props) and plain Node http
7
+ # handlers: the request object's fields and body readers, read through a variable named req, request or ctx. Browser
8
+ # rules also read the page address (location, document.URL, document.referrer). Taint flows through string building,
9
+ # template literals, assignment and calls by default. A project guard is recognised when its name says what it checks.
10
+ rules:
11
+ - id: hullproof-taint-js-sql-injection
12
+ languages: [javascript, typescript]
13
+ mode: taint
14
+ severity: ERROR
15
+ message: >-
16
+ Request data reaches the text of a SQL query (CWE-89). Use placeholders with the values passed apart
17
+ (db.query("... WHERE id = $1", [id])), a tagged template the driver parameterises (sql`...`), or the query
18
+ builder's own methods, and keep the query text constant.
19
+ metadata:
20
+ cwe: "CWE-89: Improper Neutralization of Special Elements used in an SQL Command"
21
+ sec_ids: [SEC-API-017]
22
+ pattern-sources:
23
+ - patterns:
24
+ - pattern-either:
25
+ - pattern: $R.query
26
+ - pattern: $R.body
27
+ - pattern: $R.params
28
+ - pattern: $R.headers
29
+ - pattern: $R.cookies
30
+ - pattern: $R.url
31
+ - pattern: $R.originalUrl
32
+ - pattern: $R.path
33
+ - pattern: $R.nextUrl
34
+ - pattern: $R.get(...)
35
+ - pattern: $R.header(...)
36
+ - pattern: $R.param(...)
37
+ - pattern: $R.json()
38
+ - pattern: $R.formData()
39
+ - pattern: $R.text()
40
+ - pattern: $R.request.body
41
+ - pattern: $R.request.query
42
+ - metavariable-regex:
43
+ metavariable: $R
44
+ regex: ^(req|request|ctx)$
45
+ - pattern: searchParams
46
+ pattern-sanitizers:
47
+ - pattern-either:
48
+ - pattern: parseInt(...)
49
+ - pattern: parseFloat(...)
50
+ - pattern: Number(...)
51
+ - pattern: sql`...`
52
+ - pattern: Prisma.sql`...`
53
+ - patterns:
54
+ - pattern: $ESC(...)
55
+ - metavariable-regex:
56
+ metavariable: $ESC
57
+ regex: (?i)^(.*\.)?(escape|escapeLiteral|escapeIdentifier|escapeId|format)$
58
+ pattern-sinks:
59
+ - patterns:
60
+ - pattern-either:
61
+ - pattern: $DB.query($Q, ...)
62
+ - pattern: $DB.execute($Q, ...)
63
+ - pattern: $DB.raw($Q, ...)
64
+ - pattern: $DB.unsafe($Q, ...)
65
+ - pattern: $DB.$queryRawUnsafe($Q, ...)
66
+ - pattern: $DB.$executeRawUnsafe($Q, ...)
67
+ - patterns:
68
+ - pattern: $DB.$RAW($Q, ...)
69
+ - metavariable-regex:
70
+ metavariable: $RAW
71
+ regex: ^(whereRaw|orWhereRaw|havingRaw|orderByRaw|groupByRaw|joinRaw|selectRaw|fromRaw)$
72
+ - focus-metavariable: $Q
73
+
74
+ - id: hullproof-taint-js-xss
75
+ languages: [javascript, typescript]
76
+ mode: taint
77
+ severity: ERROR
78
+ message: >-
79
+ Request or page address data is written into HTML without encoding (CWE-79). Send it through a template or
80
+ component that escapes by default, set text with textContent, or encode it with one named escape function (or
81
+ DOMPurify for HTML that must stay HTML) before it reaches the HTML sink.
82
+ metadata:
83
+ cwe: "CWE-79: Improper Neutralization of Input During Web Page Generation"
84
+ sec_ids: [SEC-WEB-031]
85
+ pattern-sources:
86
+ - patterns:
87
+ - pattern-either:
88
+ - pattern: $R.query
89
+ - pattern: $R.body
90
+ - pattern: $R.params
91
+ - pattern: $R.headers
92
+ - pattern: $R.cookies
93
+ - pattern: $R.url
94
+ - pattern: $R.originalUrl
95
+ - pattern: $R.path
96
+ - pattern: $R.nextUrl
97
+ - pattern: $R.get(...)
98
+ - pattern: $R.header(...)
99
+ - pattern: $R.param(...)
100
+ - pattern: $R.json()
101
+ - pattern: $R.formData()
102
+ - pattern: $R.text()
103
+ - pattern: $R.request.body
104
+ - pattern: $R.request.query
105
+ - metavariable-regex:
106
+ metavariable: $R
107
+ regex: ^(req|request|ctx)$
108
+ - pattern: searchParams
109
+ - pattern-either:
110
+ - pattern: location.search
111
+ - pattern: location.hash
112
+ - pattern: location.href
113
+ - pattern: window.location.search
114
+ - pattern: window.location.hash
115
+ - pattern: window.location.href
116
+ - pattern: document.location.search
117
+ - pattern: document.location.hash
118
+ - pattern: document.URL
119
+ - pattern: document.documentURI
120
+ - pattern: document.referrer
121
+ pattern-sanitizers:
122
+ - pattern-either:
123
+ - pattern: parseInt(...)
124
+ - pattern: parseFloat(...)
125
+ - pattern: Number(...)
126
+ - pattern: encodeURIComponent(...)
127
+ - patterns:
128
+ - pattern: $ESC(...)
129
+ - metavariable-regex:
130
+ metavariable: $ESC
131
+ regex: (?i)^(.*\.)?\w*(escape|sanitize|encode|purify)\w*$
132
+ pattern-sinks:
133
+ - patterns:
134
+ - pattern-either:
135
+ - pattern: $RES.send($X)
136
+ - pattern: $RES.write($X, ...)
137
+ - pattern: $RES.end($X, ...)
138
+ - metavariable-regex:
139
+ metavariable: $RES
140
+ regex: ^(res|response|reply)$
141
+ - pattern-not: $RES.send({...})
142
+ - pattern-not: $RES.send([...])
143
+ - focus-metavariable: $X
144
+ - patterns:
145
+ - pattern-either:
146
+ - pattern: '$CTX.body = $X'
147
+ - metavariable-regex:
148
+ metavariable: $CTX
149
+ regex: ^(ctx|this)$
150
+ - focus-metavariable: $X
151
+ - patterns:
152
+ - pattern-either:
153
+ - pattern: '$EL.innerHTML = $X'
154
+ - pattern: '$EL.outerHTML = $X'
155
+ - pattern: $EL.insertAdjacentHTML($POS, $X)
156
+ - pattern: document.write($X)
157
+ - pattern: document.writeln($X)
158
+ - pattern: '<$TAG dangerouslySetInnerHTML={{__html: $X}} />'
159
+ - patterns:
160
+ - pattern-either:
161
+ - pattern: 'new Response($X, {..., headers: {..., "Content-Type": $CT, ...}, ...})'
162
+ - pattern: 'new Response($X, {..., headers: {..., "content-type": $CT, ...}, ...})'
163
+ - pattern: 'new NextResponse($X, {..., headers: {..., "Content-Type": $CT, ...}, ...})'
164
+ - metavariable-regex:
165
+ metavariable: $CT
166
+ regex: (?i).*html.*
167
+ - focus-metavariable: $X
168
+
169
+ - id: hullproof-taint-js-command-injection
170
+ languages: [javascript, typescript]
171
+ mode: taint
172
+ severity: ERROR
173
+ message: >-
174
+ Request data reaches a shell command or chooses the program that runs (CWE-78). Use execFile or spawn with a fixed
175
+ program, an argument array and no shell, and map the request value to a fixed set of options.
176
+ metadata:
177
+ cwe: "CWE-78: Improper Neutralization of Special Elements used in an OS Command"
178
+ sec_ids: [SEC-API-020]
179
+ pattern-sources:
180
+ - patterns:
181
+ - pattern-either:
182
+ - pattern: $R.query
183
+ - pattern: $R.body
184
+ - pattern: $R.params
185
+ - pattern: $R.headers
186
+ - pattern: $R.cookies
187
+ - pattern: $R.url
188
+ - pattern: $R.originalUrl
189
+ - pattern: $R.path
190
+ - pattern: $R.nextUrl
191
+ - pattern: $R.get(...)
192
+ - pattern: $R.header(...)
193
+ - pattern: $R.param(...)
194
+ - pattern: $R.json()
195
+ - pattern: $R.formData()
196
+ - pattern: $R.text()
197
+ - pattern: $R.request.body
198
+ - pattern: $R.request.query
199
+ - metavariable-regex:
200
+ metavariable: $R
201
+ regex: ^(req|request|ctx)$
202
+ - pattern: searchParams
203
+ pattern-sanitizers:
204
+ - pattern-either:
205
+ - pattern: parseInt(...)
206
+ - pattern: Number(...)
207
+ - patterns:
208
+ - pattern: $Q(...)
209
+ - metavariable-regex:
210
+ metavariable: $Q
211
+ regex: (?i)^(.*\.)?(quote|shellQuote|shellEscape|escapeShellArg)$
212
+ pattern-sinks:
213
+ - patterns:
214
+ - pattern-either:
215
+ - pattern: child_process.exec($X, ...)
216
+ - pattern: child_process.execSync($X, ...)
217
+ - pattern: child_process.spawn($X, ...)
218
+ - pattern: child_process.spawnSync($X, ...)
219
+ - pattern: child_process.execFile($X, ...)
220
+ - pattern: child_process.execFileSync($X, ...)
221
+ - pattern: child_process.fork($X, ...)
222
+ - pattern: 'child_process.spawn($SH, ["-c", $X, ...], ...)'
223
+ - pattern: 'child_process.spawnSync($SH, ["-c", $X, ...], ...)'
224
+ - pattern: 'child_process.spawn($P, $X, {..., shell: true, ...})'
225
+ - pattern: 'child_process.spawnSync($P, $X, {..., shell: true, ...})'
226
+ - patterns:
227
+ - pattern: $CP.$F($X, ...)
228
+ - metavariable-regex:
229
+ metavariable: $CP
230
+ regex: ^(cp|childProcess|child_process|proc)$
231
+ - metavariable-regex:
232
+ metavariable: $F
233
+ regex: ^(exec|execSync|spawn|spawnSync|execFile|execFileSync)$
234
+ - patterns:
235
+ - pattern-either:
236
+ - pattern-inside: |
237
+ import { $I } from 'node:child_process';
238
+ ...
239
+ - pattern-inside: |
240
+ import { $I, ... } from 'node:child_process';
241
+ ...
242
+ - pattern-inside: |
243
+ import { ..., $I } from 'node:child_process';
244
+ ...
245
+ - pattern: $F($X, ...)
246
+ - metavariable-regex:
247
+ metavariable: $F
248
+ regex: ^(exec|execSync|spawn|spawnSync|execFile|execFileSync)$
249
+ - focus-metavariable: $X
250
+
251
+ - id: hullproof-taint-js-path-traversal
252
+ languages: [javascript, typescript]
253
+ mode: taint
254
+ severity: WARNING
255
+ message: >-
256
+ Request data becomes part of a file path the server reads, writes, lists, deletes or sends (CWE-22). Reduce the
257
+ name with path.basename, or resolve the full path and check that it starts with a fixed root folder, or pass
258
+ the root option to sendFile.
259
+ metadata:
260
+ cwe: "CWE-22: Improper Limitation of a Pathname to a Restricted Directory"
261
+ sec_ids: []
262
+ pattern-sources:
263
+ - patterns:
264
+ - pattern-either:
265
+ - pattern: $R.query
266
+ - pattern: $R.body
267
+ - pattern: $R.params
268
+ - pattern: $R.headers
269
+ - pattern: $R.cookies
270
+ - pattern: $R.url
271
+ - pattern: $R.originalUrl
272
+ - pattern: $R.path
273
+ - pattern: $R.nextUrl
274
+ - pattern: $R.get(...)
275
+ - pattern: $R.header(...)
276
+ - pattern: $R.param(...)
277
+ - pattern: $R.json()
278
+ - pattern: $R.formData()
279
+ - pattern: $R.text()
280
+ - pattern: $R.request.body
281
+ - pattern: $R.request.query
282
+ - metavariable-regex:
283
+ metavariable: $R
284
+ regex: ^(req|request|ctx)$
285
+ - pattern: searchParams
286
+ pattern-sanitizers:
287
+ - pattern-either:
288
+ - pattern: path.basename(...)
289
+ - pattern: parseInt(...)
290
+ - pattern: Number(...)
291
+ - by-side-effect: true
292
+ patterns:
293
+ - pattern: $GUARD($X, ...)
294
+ - metavariable-regex:
295
+ metavariable: $GUARD
296
+ regex: (?i)^(.*\.)?\w*(safe|allow|valid|guard|check|verify|within|inside|contain)\w*$
297
+ - focus-metavariable: $X
298
+ pattern-sinks:
299
+ - patterns:
300
+ - pattern-either:
301
+ - patterns:
302
+ - pattern-either:
303
+ - pattern: fs.$F($X, ...)
304
+ - pattern: fs.promises.$F($X, ...)
305
+ - pattern: $FSP.$F($X, ...)
306
+ - metavariable-regex:
307
+ metavariable: $F
308
+ regex: ^(readFile|readFileSync|writeFile|writeFileSync|appendFile|appendFileSync|createReadStream|createWriteStream|unlink|unlinkSync|rm|rmSync|rmdir|rmdirSync|readdir|readdirSync|open|openSync|mkdir|mkdirSync|rename|renameSync|copyFile|copyFileSync|stat|statSync)$
309
+ - patterns:
310
+ - pattern-either:
311
+ - pattern-inside: |
312
+ import { $I } from '$MOD';
313
+ ...
314
+ - pattern-inside: |
315
+ import { $I, ... } from '$MOD';
316
+ ...
317
+ - pattern-inside: |
318
+ import { ..., $I } from '$MOD';
319
+ ...
320
+ - metavariable-regex:
321
+ metavariable: $MOD
322
+ regex: ^(node:)?fs(/promises)?$
323
+ - pattern: $F($X, ...)
324
+ - metavariable-regex:
325
+ metavariable: $F
326
+ regex: ^(readFile|readFileSync|writeFile|writeFileSync|appendFile|createReadStream|createWriteStream|unlink|unlinkSync|rm|rmSync|readdir|readdirSync|open|mkdir|rename|copyFile)$
327
+ - patterns:
328
+ - pattern-either:
329
+ - pattern: $RES.sendFile($X, ...)
330
+ - pattern: $RES.download($X, ...)
331
+ - pattern-not: '$RES.sendFile($X, {..., root: $ROOT, ...}, ...)'
332
+ - metavariable-regex:
333
+ metavariable: $RES
334
+ regex: ^(res|response|reply)$
335
+ - focus-metavariable: $X
336
+
337
+ - id: hullproof-taint-js-ssrf
338
+ languages: [javascript, typescript]
339
+ mode: taint
340
+ severity: ERROR
341
+ message: >-
342
+ Request data chooses the address of an outbound request the server makes (CWE-918). Allow only http and https,
343
+ resolve the host, refuse private, loopback, link local and metadata addresses, connect to the checked address and
344
+ check every redirect. A value that only fills a path or query under a fixed host is not matched.
345
+ metadata:
346
+ cwe: "CWE-918: Server-Side Request Forgery (SSRF)"
347
+ sec_ids: [SEC-API-035]
348
+ pattern-sources:
349
+ - patterns:
350
+ - pattern-either:
351
+ - pattern: $R.query
352
+ - pattern: $R.body
353
+ - pattern: $R.params
354
+ - pattern: $R.headers
355
+ - pattern: $R.cookies
356
+ - pattern: $R.url
357
+ - pattern: $R.originalUrl
358
+ - pattern: $R.path
359
+ - pattern: $R.nextUrl
360
+ - pattern: $R.get(...)
361
+ - pattern: $R.header(...)
362
+ - pattern: $R.param(...)
363
+ - pattern: $R.json()
364
+ - pattern: $R.formData()
365
+ - pattern: $R.text()
366
+ - pattern: $R.request.body
367
+ - pattern: $R.request.query
368
+ - metavariable-regex:
369
+ metavariable: $R
370
+ regex: ^(req|request|ctx)$
371
+ - pattern: searchParams
372
+ pattern-sanitizers:
373
+ - pattern-either:
374
+ - pattern: parseInt(...)
375
+ - pattern: Number(...)
376
+ - pattern: encodeURIComponent(...)
377
+ # A fixed scheme and host before the request value: the value can only change the path or the query.
378
+ - patterns:
379
+ - pattern: '"$URL" + $Y'
380
+ - metavariable-regex:
381
+ metavariable: $URL
382
+ regex: ^https?://[^/]+/
383
+ - pattern-regex: '`https?://[^/`$]+/[^`]*`'
384
+ - by-side-effect: true
385
+ patterns:
386
+ - pattern: $GUARD($X, ...)
387
+ - metavariable-regex:
388
+ metavariable: $GUARD
389
+ regex: (?i)^(.*\.)?\w*(safe|allow|valid|guard|check|verify)\w*$
390
+ - focus-metavariable: $X
391
+ pattern-sinks:
392
+ - patterns:
393
+ - pattern-either:
394
+ - pattern: fetch($X, ...)
395
+ - pattern: axios($X, ...)
396
+ - pattern: 'axios({..., url: $X, ...})'
397
+ - pattern: 'axios.request({..., url: $X, ...})'
398
+ - pattern: got($X, ...)
399
+ - pattern: undici.request($X, ...)
400
+ - pattern: http.get($X, ...)
401
+ - pattern: http.request($X, ...)
402
+ - pattern: https.get($X, ...)
403
+ - pattern: https.request($X, ...)
404
+ - patterns:
405
+ - pattern-either:
406
+ - pattern: axios.$M($X, ...)
407
+ - pattern: got.$M($X, ...)
408
+ - pattern: superagent.$M($X, ...)
409
+ - metavariable-regex:
410
+ metavariable: $M
411
+ regex: ^(get|post|put|patch|delete|head|options)$
412
+ - focus-metavariable: $X