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,137 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Copyright 2026 Rare Phronesis Limited
3
+ # Copied from the Hullproof kit (kit/tools/hullproof/rules/ssrf-unguarded-url.yaml).
4
+ #
5
+ rules:
6
+ - id: hullproof-ssrf-request-data-to-url
7
+ languages: [typescript, javascript]
8
+ mode: taint
9
+ options:
10
+ taint_assume_safe_functions: true
11
+ severity: ERROR
12
+ message: >-
13
+ Request data reaches an outbound HTTP call, or the second argument form of new URL, with no guard
14
+ call in between. A caller can point the server at internal hosts, cloud metadata addresses or
15
+ redirects. Resolve the host, reject private, loopback, link local and mapped addresses, connect to
16
+ the address that was checked, and do not follow redirects without checking each hop. If the value
17
+ is only a path or a numeric id, encode it with encodeURIComponent or Number.
18
+ # Guard calls are recognized by name. Edit the regex in the sanitizer below to match the project's own
19
+ # guard function. A guard that is not on the list shows up as a finding, which is the safe failure.
20
+ pattern-sources:
21
+ - patterns:
22
+ - pattern-either:
23
+ - pattern: $R.body
24
+ - pattern: $R.query
25
+ - pattern: $R.params
26
+ - pattern: $R.headers
27
+ - pattern: $R.cookies
28
+ - pattern: $R.url
29
+ - pattern: $R.nextUrl
30
+ - pattern: $R.nextUrl.searchParams
31
+ - pattern: $R.searchParams
32
+ - metavariable-regex:
33
+ metavariable: $R
34
+ regex: ^(req|request|ctx|event|context|c\.req|req\.raw)$
35
+ - patterns:
36
+ - pattern-either:
37
+ - pattern: await $R.json()
38
+ - pattern: await $R.formData()
39
+ - pattern: await $R.text()
40
+ - pattern: $R.json()
41
+ - pattern: $R.formData()
42
+ - metavariable-regex:
43
+ metavariable: $R
44
+ regex: ^(req|request|ctx|c\.req|req\.raw)$
45
+ - patterns:
46
+ - pattern-either:
47
+ - pattern: $S.get(...)
48
+ - pattern: $S.getAll(...)
49
+ - metavariable-regex:
50
+ metavariable: $S
51
+ regex: (?s)^(?:.*\.)?(?:searchParams|params|query)$|^(?:formData|data|fd)$
52
+ pattern-sinks:
53
+ - patterns:
54
+ - pattern: $F($U, ...)
55
+ - focus-metavariable: $U
56
+ - metavariable-regex:
57
+ metavariable: $F
58
+ regex: ^(fetch|axios|got|ky|ofetch|\$fetch|needle|superagent|request|undici\.(fetch|request|stream)|axios\.(get|post|put|patch|delete|head|options|request)|got\.(get|post|put|patch|delete|head|stream)|ky\.(get|post|put|patch|delete|head)|https?\.(get|request)|page\.goto|\w+\.goto)$
59
+ - patterns:
60
+ - pattern: new URL($U, $BASE)
61
+ - focus-metavariable: $U
62
+ pattern-sanitizers:
63
+ - by-side-effect: true
64
+ patterns:
65
+ - pattern: $G($X, ...)
66
+ - focus-metavariable: $X
67
+ - metavariable-regex:
68
+ metavariable: $G
69
+ regex: (?i)(safe|ssrf|allowlist|allowedhost|ispublic|assertpublic|validateurl|validatehost|asserturl|checkurl|guard|resolveandcheck|publicurl|externalurl)
70
+ - pattern: encodeURIComponent(...)
71
+ - pattern: encodeURI(...)
72
+ - pattern: Number(...)
73
+ - pattern: parseInt(...)
74
+ - pattern: parseFloat(...)
75
+ metadata:
76
+ sec_ids: [SEC-API-035, SEC-API-034]
77
+ severity_hint: CRITICAL
78
+ confidence: MEDIUM
79
+ note: >-
80
+ Taint within one file. Does not follow values across files or through function parameters
81
+ that are not request objects. Host allowlist checks written inline (an if on the hostname) are
82
+ not recognized, so those show as findings to be closed by reading the check.
83
+ - id: hullproof-ssrf-url-built-from-base
84
+ languages: [typescript, javascript]
85
+ severity: WARNING
86
+ message: >-
87
+ A fixed base URL is joined to a value by concatenation. If the value comes from a caller, it can
88
+ add an at sign, dot dot segments or a query string and change the host or path that is called.
89
+ Encode path segments with encodeURIComponent, or build the URL with new URL and check its origin.
90
+ patterns:
91
+ - pattern-either:
92
+ - pattern: $F($BASE + $X, ...)
93
+ - pattern: $F($BASE + "/" + $X, ...)
94
+ - metavariable-regex:
95
+ metavariable: $F
96
+ regex: ^(fetch|axios|got|ky|ofetch|\$fetch|undici\.(fetch|request)|axios\.(get|post|put|patch|delete|request))$
97
+ - metavariable-regex:
98
+ metavariable: $BASE
99
+ regex: (?i)^[\w.]*(base|api|host|origin|endpoint|baseurl|root)\w*$
100
+ - metavariable-regex:
101
+ metavariable: $X
102
+ regex: (?s)^(?!["'`])(?!\s*(?:encodeURIComponent|encodeURI|Number|parseInt)\s*\()
103
+ metadata:
104
+ sec_ids: [SEC-API-034, SEC-API-035]
105
+ severity_hint: MEDIUM
106
+ confidence: LOW
107
+ note: >-
108
+ Concatenation only. Template literals that join a base and an id are very common and are not
109
+ matched here to keep noise low. A reviewer should read any outbound URL that is built from a caller value.
110
+ - id: hullproof-ssrf-stored-url
111
+ languages: [typescript, javascript]
112
+ severity: WARNING
113
+ message: >-
114
+ An outbound call uses a URL property from a stored record or an options object (a customer webhook
115
+ endpoint, a callback address, a feed address). The address was supplied by a user earlier, so it is
116
+ request data that was saved. Apply the same host checks at the moment of connection, not only when the
117
+ address was saved, and do not follow redirects without checking each hop.
118
+ patterns:
119
+ - pattern-either:
120
+ - pattern: $F($O.$P, ...)
121
+ - pattern: $F($O.$P)
122
+ - metavariable-regex:
123
+ metavariable: $F
124
+ regex: ^(fetch|axios|got|ky|ofetch|\$fetch|needle|superagent|undici\.(fetch|request)|axios\.(get|post|put|patch|delete|head|request)|got\.(get|post|put|patch|delete|head|stream)|https?\.(get|request))$
125
+ - metavariable-regex:
126
+ metavariable: $P
127
+ regex: (?i)^(url|webhookurl|callbackurl|endpoint|endpointurl|targeturl|hookurl|feedurl|href|uri)$
128
+ - metavariable-regex:
129
+ metavariable: $O
130
+ regex: (?s)^(?!(?:window|document|location|response|res|resp|result|self|this|process|req|request|ctx|event|body|query|params|import\.meta|import_meta|new URL)\b)
131
+ metadata:
132
+ sec_ids: [SEC-API-035, SEC-API-034]
133
+ severity_hint: HIGH
134
+ confidence: LOW
135
+ note: >-
136
+ Name based lead. It finds the common customer webhook shape that the request taint rule cannot see
137
+ because the value comes from a database row. Confirm whether a guard runs at connection time.
@@ -0,0 +1,163 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Copyright 2026 Rare Phronesis Limited
3
+ # Copied from the Hullproof kit (kit/tools/hullproof/rules/subprocess-injection.yaml).
4
+ #
5
+ rules:
6
+ - id: hullproof-subprocess-shell-string
7
+ languages: [typescript, javascript]
8
+ severity: ERROR
9
+ message: >-
10
+ A command string that is not a constant goes to a shell. Anything in the string can add commands.
11
+ Call the program directly with an argument array (execFile, spawn with no shell), pass user values
12
+ only as separate arguments after a double dash, and never concatenate them into the command.
13
+ patterns:
14
+ - pattern-either:
15
+ - pattern: exec($C, ...)
16
+ - pattern: execSync($C, ...)
17
+ - pattern: execAsync($C, ...)
18
+ - pattern: execPromise($C, ...)
19
+ - pattern: execP($C, ...)
20
+ - pattern: execaCommand($C, ...)
21
+ - pattern: execaCommandSync($C, ...)
22
+ - pattern: promisify(exec)($C, ...)
23
+ - pattern: util.promisify(exec)($C, ...)
24
+ - pattern: shell.exec($C, ...)
25
+ - pattern: shelljs.exec($C, ...)
26
+ - patterns:
27
+ - pattern-either:
28
+ - pattern: $CP.exec($C, ...)
29
+ - pattern: $CP.execSync($C, ...)
30
+ - metavariable-regex:
31
+ metavariable: $CP
32
+ regex: ^(cp|child_process|childProcess|proc|node_child_process|cproc)$
33
+ - patterns:
34
+ - pattern: '$F($C, ..., {..., shell: true, ...})'
35
+ - metavariable-regex:
36
+ metavariable: $F
37
+ regex: ^(?:\w+\.)?(?:execFile|execFileSync|spawn|spawnSync|execa|execaSync)$
38
+ - metavariable-regex:
39
+ metavariable: $C
40
+ regex: (?s)^(?!(?:"[^"\\]*"|'[^'\\]*'|`[^`$\\]*`)$)
41
+ metadata:
42
+ sec_ids: [SEC-API-020]
43
+ severity_hint: CRITICAL
44
+ confidence: MEDIUM
45
+ note: >-
46
+ Any command that is not a plain string literal. A command built only from constants held in
47
+ variables is a false positive. The bare exec name is matched only as a function call, so
48
+ RegExp exec on a variable is not matched.
49
+ - id: hullproof-subprocess-shell-interpreter
50
+ languages: [typescript, javascript]
51
+ severity: ERROR
52
+ message: >-
53
+ A shell interpreter is started with a script argument that is not a constant. This is the same as
54
+ running a command string through a shell. Run the target program directly with an argument array.
55
+ patterns:
56
+ - pattern-either:
57
+ - pattern: $F($SH, [..., "-c", $S, ...], ...)
58
+ - pattern: $F($SH, [..., "/c", $S, ...], ...)
59
+ - pattern: $F($SH, [..., "/C", $S, ...], ...)
60
+ - pattern: $F($SH, [..., "-Command", $S, ...], ...)
61
+ - pattern: $F($SH, [..., "-lc", $S, ...], ...)
62
+ - metavariable-regex:
63
+ metavariable: $SH
64
+ regex: (?i)^["'`](?:.*[/\\])?(?:sh|bash|zsh|dash|ksh|fish|cmd|cmd\.exe|powershell|powershell\.exe|pwsh|pwsh\.exe)["'`]$
65
+ - metavariable-regex:
66
+ metavariable: $F
67
+ regex: ^(?:\w+\.)?(?:execFile|execFileSync|spawn|spawnSync|execa|execaSync|fork)$
68
+ - metavariable-regex:
69
+ metavariable: $S
70
+ regex: (?s)^(?!(?:"[^"\\]*"|'[^'\\]*'|`[^`$\\]*`)$)
71
+ metadata:
72
+ sec_ids: [SEC-API-020]
73
+ severity_hint: CRITICAL
74
+ confidence: HIGH
75
+ - id: hullproof-subprocess-arg-no-separator
76
+ languages: [typescript, javascript]
77
+ severity: WARNING
78
+ message: >-
79
+ A program is started with an argument that is not a constant, and the argument list has no double
80
+ dash separator. If the value starts with a dash the program reads it as an option (for example git
81
+ ls-remote with an upload pack option runs a command). Put the double dash before user values, check
82
+ that the value does not start with a dash, and pin the allowed options.
83
+ patterns:
84
+ - pattern-either:
85
+ - pattern: $F($BIN, [..., $A, ...], ...)
86
+ - metavariable-regex:
87
+ metavariable: $F
88
+ regex: ^(?:\w+\.)?(?:execFile|execFileSync|spawn|spawnSync|execa|execaSync)$
89
+ - metavariable-regex:
90
+ metavariable: $A
91
+ regex: (?s)^(?!(?:"[^"\\]*"|'[^'\\]*'|`[^`$\\]*`)$)
92
+ - pattern-not: $F($BIN, [..., "--", ...], ...)
93
+ - metavariable-regex:
94
+ metavariable: $BIN
95
+ regex: (?is)^(?!["'`](?:.*[/\\])?(?:sh|bash|zsh|dash|ksh|fish|cmd|cmd\.exe|powershell|powershell\.exe|pwsh|pwsh\.exe)["'`]$)
96
+ metadata:
97
+ sec_ids: [SEC-API-020]
98
+ severity_hint: HIGH
99
+ confidence: LOW
100
+ note: >-
101
+ Every call that passes a non constant list entry without a double dash anywhere in the list.
102
+ Many calls pass a constant held in a variable, or a program that has no dash options. Review each.
103
+ Option injection is a program specific issue: git, curl, tar, ssh, rsync, ffmpeg and convert are
104
+ the usual targets.
105
+ - id: hullproof-subprocess-request-data
106
+ languages: [typescript, javascript]
107
+ mode: taint
108
+ severity: ERROR
109
+ message: >-
110
+ Request data reaches a subprocess call. Run a fixed program with a fixed argument list, put user
111
+ values after a double dash, and allowlist the values that are allowed.
112
+ pattern-sources:
113
+ - patterns:
114
+ - pattern-either:
115
+ - pattern: $R.body
116
+ - pattern: $R.query
117
+ - pattern: $R.params
118
+ - pattern: $R.headers
119
+ - pattern: $R.url
120
+ - pattern: $R.nextUrl.searchParams
121
+ - pattern: $R.searchParams
122
+ - metavariable-regex:
123
+ metavariable: $R
124
+ regex: ^(req|request|ctx|event|context|c\.req|req\.raw)$
125
+ - patterns:
126
+ - pattern-either:
127
+ - pattern: await $R.json()
128
+ - pattern: await $R.formData()
129
+ - pattern: await $R.text()
130
+ - metavariable-regex:
131
+ metavariable: $R
132
+ regex: ^(req|request|ctx|c\.req|req\.raw)$
133
+ - patterns:
134
+ - pattern-either:
135
+ - pattern: $S.get(...)
136
+ - pattern: $S.getAll(...)
137
+ - metavariable-regex:
138
+ metavariable: $S
139
+ regex: ^(searchParams|formData|params|query|\w+\.searchParams|data|fd)$
140
+ pattern-sinks:
141
+ - patterns:
142
+ - pattern-either:
143
+ - pattern: $F($C, ...)
144
+ - pattern: $F($C2, [...], ...)
145
+ - focus-metavariable: $C
146
+ - metavariable-regex:
147
+ metavariable: $F
148
+ regex: ^(?:\w+\.)?(?:exec|execSync|execFile|execFileSync|spawn|spawnSync|execa|execaSync|execaCommand|fork)$
149
+ - patterns:
150
+ - pattern: $F($BIN, [..., $A, ...], ...)
151
+ - focus-metavariable: $A
152
+ - metavariable-regex:
153
+ metavariable: $F
154
+ regex: ^(?:\w+\.)?(?:exec|execSync|execFile|execFileSync|spawn|spawnSync|execa|execaSync|fork)$
155
+ pattern-sanitizers:
156
+ - pattern: Number(...)
157
+ - pattern: parseInt(...)
158
+ - pattern: parseFloat(...)
159
+ metadata:
160
+ sec_ids: [SEC-API-020]
161
+ severity_hint: CRITICAL
162
+ confidence: HIGH
163
+ note: Taint within one file. The parameter names of the request object are matched by name (req, request, ctx, event).
@@ -0,0 +1,333 @@
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-go.go, run by test/rules.test.ts with semgrep --test.
5
+ #
6
+ # Sources are request data in net/http (an *http.Request), gorilla/mux and chi route variables, Gin (*gin.Context) and
7
+ # Echo (echo.Context). Taint flows through string building, fmt.Sprintf, assignment and calls by default. A project
8
+ # guard is recognised when its name says what it checks.
9
+ rules:
10
+ - id: hullproof-taint-go-sql-injection
11
+ languages: [go]
12
+ mode: taint
13
+ severity: ERROR
14
+ message: >-
15
+ Request data reaches the text of a SQL query (CWE-89). Pass values as query arguments with placeholders
16
+ (db.Query("... WHERE id = $1", id)) and keep the query text constant; with GORM, use Where with ? placeholders.
17
+ metadata:
18
+ cwe: "CWE-89: Improper Neutralization of Special Elements used in an SQL Command"
19
+ sec_ids: [SEC-API-017]
20
+ pattern-sources:
21
+ - patterns:
22
+ - pattern: '($R : *http.Request).$M(...)'
23
+ - metavariable-regex:
24
+ metavariable: $M
25
+ regex: ^(FormValue|PostFormValue|Cookie|Cookies|FormFile|MultipartReader|Referer|UserAgent)$
26
+ - patterns:
27
+ - pattern: '($R : *http.Request).$F'
28
+ - metavariable-regex:
29
+ metavariable: $F
30
+ regex: ^(URL|Form|PostForm|MultipartForm|Header|Body|RequestURI|Host)$
31
+ - pattern: mux.Vars(...)
32
+ - pattern: chi.URLParam(...)
33
+ - patterns:
34
+ - pattern-either:
35
+ - pattern: '($C : *gin.Context).$G(...)'
36
+ - pattern: '($C : echo.Context).$G(...)'
37
+ - metavariable-regex:
38
+ metavariable: $G
39
+ regex: ^(Query|DefaultQuery|QueryArray|QueryMap|GetQuery|Param|Params|PostForm|DefaultPostForm|PostFormArray|GetPostForm|GetHeader|Cookie|FormValue|QueryParam|QueryParams|FormParams|GetRawData|FormFile|ShouldBind|ShouldBindJSON|ShouldBindQuery|Bind|BindJSON)$
40
+ pattern-sanitizers:
41
+ - pattern-either:
42
+ - pattern: strconv.Atoi(...)
43
+ - pattern: strconv.ParseInt(...)
44
+ - pattern: strconv.ParseUint(...)
45
+ - pattern: strconv.ParseFloat(...)
46
+ - pattern: strconv.ParseBool(...)
47
+ - pattern: uuid.Parse(...)
48
+ pattern-sinks:
49
+ - patterns:
50
+ - pattern-either:
51
+ - patterns:
52
+ - pattern: $DB.$M($Q, ...)
53
+ - metavariable-regex:
54
+ metavariable: $M
55
+ regex: ^(Query|QueryRow|Exec|Prepare|Queryx|QueryRowx|MustExec|NamedExec|NamedQuery|Raw|Where|Order|Having|Joins)$
56
+ - patterns:
57
+ - pattern: $DB.$M($DEST, $Q, ...)
58
+ - metavariable-regex:
59
+ metavariable: $M
60
+ regex: ^(Get|Select)$
61
+ - patterns:
62
+ - pattern: $DB.$M($CTX, $Q, ...)
63
+ - metavariable-regex:
64
+ metavariable: $M
65
+ regex: ^(QueryContext|QueryRowContext|ExecContext|PrepareContext|QueryxContext|QueryRowxContext|GetContext|SelectContext)$
66
+ - pattern-not: '($C : *gin.Context).$M(...)'
67
+ - pattern-not: '($C : echo.Context).$M(...)'
68
+ - pattern-not: http.$M(...)
69
+ - pattern-not: '($CL : *http.Client).$M(...)'
70
+ - focus-metavariable: $Q
71
+
72
+ - id: hullproof-taint-go-xss
73
+ languages: [go]
74
+ mode: taint
75
+ severity: ERROR
76
+ message: >-
77
+ Request data is written into an HTML response without encoding (CWE-79). Render it with html/template, which
78
+ escapes by context, or encode it with html.EscapeString before writing it; never wrap request data in
79
+ template.HTML.
80
+ metadata:
81
+ cwe: "CWE-79: Improper Neutralization of Input During Web Page Generation"
82
+ sec_ids: [SEC-WEB-031]
83
+ pattern-sources:
84
+ - patterns:
85
+ - pattern: '($R : *http.Request).$M(...)'
86
+ - metavariable-regex:
87
+ metavariable: $M
88
+ regex: ^(FormValue|PostFormValue|Cookie|Cookies|FormFile|MultipartReader|Referer|UserAgent)$
89
+ - patterns:
90
+ - pattern: '($R : *http.Request).$F'
91
+ - metavariable-regex:
92
+ metavariable: $F
93
+ regex: ^(URL|Form|PostForm|MultipartForm|Header|Body|RequestURI|Host)$
94
+ - pattern: mux.Vars(...)
95
+ - pattern: chi.URLParam(...)
96
+ - patterns:
97
+ - pattern-either:
98
+ - pattern: '($C : *gin.Context).$G(...)'
99
+ - pattern: '($C : echo.Context).$G(...)'
100
+ - metavariable-regex:
101
+ metavariable: $G
102
+ regex: ^(Query|DefaultQuery|QueryArray|QueryMap|GetQuery|Param|Params|PostForm|DefaultPostForm|PostFormArray|GetPostForm|GetHeader|Cookie|FormValue|QueryParam|QueryParams|FormParams|GetRawData|FormFile|ShouldBind|ShouldBindJSON|ShouldBindQuery|Bind|BindJSON)$
103
+ pattern-sanitizers:
104
+ - pattern-either:
105
+ - pattern: strconv.Atoi(...)
106
+ - pattern: strconv.ParseInt(...)
107
+ - pattern: strconv.ParseUint(...)
108
+ - pattern: strconv.ParseFloat(...)
109
+ - pattern: strconv.ParseBool(...)
110
+ - pattern: uuid.Parse(...)
111
+
112
+ - pattern-either:
113
+ - pattern: html.EscapeString(...)
114
+ - pattern: template.HTMLEscapeString(...)
115
+ - pattern: template.JSEscapeString(...)
116
+ - pattern: url.QueryEscape(...)
117
+ - pattern: url.PathEscape(...)
118
+ - pattern: json.Marshal(...)
119
+ pattern-sinks:
120
+ - patterns:
121
+ - pattern-either:
122
+ - pattern: '($W : http.ResponseWriter).Write($X)'
123
+ - pattern: 'fmt.Fprintf(($W : http.ResponseWriter), ..., $X, ...)'
124
+ - pattern: 'fmt.Fprint(($W : http.ResponseWriter), ..., $X, ...)'
125
+ - pattern: 'fmt.Fprintln(($W : http.ResponseWriter), ..., $X, ...)'
126
+ - pattern: 'io.WriteString(($W : http.ResponseWriter), $X)'
127
+ - pattern: template.HTML($X)
128
+ - pattern: template.JS($X)
129
+ - pattern: template.HTMLAttr($X)
130
+ - pattern: '($C : *gin.Context).Writer.WriteString($X)'
131
+ - pattern: '($C : echo.Context).HTML($CODE, $X)'
132
+ - patterns:
133
+ - pattern: '($C : *gin.Context).Data($CODE, $CT, $X)'
134
+ - metavariable-regex:
135
+ metavariable: $CT
136
+ regex: (?i).*html.*
137
+ - focus-metavariable: $X
138
+
139
+ - id: hullproof-taint-go-command-injection
140
+ languages: [go]
141
+ mode: taint
142
+ severity: ERROR
143
+ message: >-
144
+ Request data reaches an operating system command or chooses the program that runs (CWE-78). Run a fixed program
145
+ with exec.Command and separate arguments, never through sh -c, and map the request value to a fixed set of
146
+ options.
147
+ metadata:
148
+ cwe: "CWE-78: Improper Neutralization of Special Elements used in an OS Command"
149
+ sec_ids: [SEC-API-020]
150
+ pattern-sources:
151
+ - patterns:
152
+ - pattern: '($R : *http.Request).$M(...)'
153
+ - metavariable-regex:
154
+ metavariable: $M
155
+ regex: ^(FormValue|PostFormValue|Cookie|Cookies|FormFile|MultipartReader|Referer|UserAgent)$
156
+ - patterns:
157
+ - pattern: '($R : *http.Request).$F'
158
+ - metavariable-regex:
159
+ metavariable: $F
160
+ regex: ^(URL|Form|PostForm|MultipartForm|Header|Body|RequestURI|Host)$
161
+ - pattern: mux.Vars(...)
162
+ - pattern: chi.URLParam(...)
163
+ - patterns:
164
+ - pattern-either:
165
+ - pattern: '($C : *gin.Context).$G(...)'
166
+ - pattern: '($C : echo.Context).$G(...)'
167
+ - metavariable-regex:
168
+ metavariable: $G
169
+ regex: ^(Query|DefaultQuery|QueryArray|QueryMap|GetQuery|Param|Params|PostForm|DefaultPostForm|PostFormArray|GetPostForm|GetHeader|Cookie|FormValue|QueryParam|QueryParams|FormParams|GetRawData|FormFile|ShouldBind|ShouldBindJSON|ShouldBindQuery|Bind|BindJSON)$
170
+ pattern-sanitizers:
171
+ - pattern-either:
172
+ - pattern: strconv.Atoi(...)
173
+ - pattern: strconv.ParseInt(...)
174
+ - pattern: strconv.ParseUint(...)
175
+ - pattern: strconv.ParseFloat(...)
176
+ - pattern: strconv.ParseBool(...)
177
+ - pattern: uuid.Parse(...)
178
+ pattern-sinks:
179
+ - patterns:
180
+ - pattern-either:
181
+ - pattern: exec.Command($X, ...)
182
+ - pattern: exec.CommandContext($CTX, $X, ...)
183
+ - pattern: exec.Command($SH, "-c", $X, ...)
184
+ - pattern: exec.CommandContext($CTX, $SH, "-c", $X, ...)
185
+ - pattern: syscall.Exec($X, ...)
186
+ - focus-metavariable: $X
187
+
188
+ - id: hullproof-taint-go-path-traversal
189
+ languages: [go]
190
+ mode: taint
191
+ severity: WARNING
192
+ message: >-
193
+ Request data becomes part of a file path the server opens, writes, lists, deletes or serves (CWE-22).
194
+ filepath.Clean alone does not stop a relative path from leaving the folder. Reduce the name with filepath.Base,
195
+ or join it to a fixed root and check the result still has that root as its prefix before using it.
196
+ metadata:
197
+ cwe: "CWE-22: Improper Limitation of a Pathname to a Restricted Directory"
198
+ sec_ids: []
199
+ pattern-sources:
200
+ - patterns:
201
+ - pattern: '($R : *http.Request).$M(...)'
202
+ - metavariable-regex:
203
+ metavariable: $M
204
+ regex: ^(FormValue|PostFormValue|Cookie|Cookies|FormFile|MultipartReader|Referer|UserAgent)$
205
+ - patterns:
206
+ - pattern: '($R : *http.Request).$F'
207
+ - metavariable-regex:
208
+ metavariable: $F
209
+ regex: ^(URL|Form|PostForm|MultipartForm|Header|Body|RequestURI|Host)$
210
+ - pattern: mux.Vars(...)
211
+ - pattern: chi.URLParam(...)
212
+ - patterns:
213
+ - pattern-either:
214
+ - pattern: '($C : *gin.Context).$G(...)'
215
+ - pattern: '($C : echo.Context).$G(...)'
216
+ - metavariable-regex:
217
+ metavariable: $G
218
+ regex: ^(Query|DefaultQuery|QueryArray|QueryMap|GetQuery|Param|Params|PostForm|DefaultPostForm|PostFormArray|GetPostForm|GetHeader|Cookie|FormValue|QueryParam|QueryParams|FormParams|GetRawData|FormFile|ShouldBind|ShouldBindJSON|ShouldBindQuery|Bind|BindJSON)$
219
+ pattern-sanitizers:
220
+ - pattern-either:
221
+ - pattern: strconv.Atoi(...)
222
+ - pattern: strconv.ParseInt(...)
223
+ - pattern: strconv.ParseUint(...)
224
+ - pattern: strconv.ParseFloat(...)
225
+ - pattern: strconv.ParseBool(...)
226
+ - pattern: uuid.Parse(...)
227
+
228
+ - pattern-either:
229
+ - pattern: filepath.Base(...)
230
+ - pattern: path.Base(...)
231
+
232
+ - by-side-effect: true
233
+ patterns:
234
+ - pattern: $GUARD($X, ...)
235
+ - metavariable-regex:
236
+ metavariable: $GUARD
237
+ regex: (?i)^(.*\.)?\w*(safe|allow|valid|guard|check|verify|within|inside|contain)\w*$
238
+ - focus-metavariable: $X
239
+ pattern-sinks:
240
+ - patterns:
241
+ - pattern-either:
242
+ - patterns:
243
+ - pattern: os.$F($X, ...)
244
+ - metavariable-regex:
245
+ metavariable: $F
246
+ regex: ^(Open|OpenFile|Create|ReadFile|WriteFile|Remove|RemoveAll|Mkdir|MkdirAll|ReadDir|Stat|Lstat|Rename|Chmod|Truncate)$
247
+ - pattern: os.Rename($S, $X)
248
+ - pattern: ioutil.ReadFile($X)
249
+ - pattern: ioutil.WriteFile($X, ...)
250
+ - pattern: ioutil.ReadDir($X)
251
+ - pattern: http.ServeFile($W, $R, $X)
252
+ - pattern: '($C : *gin.Context).File($X)'
253
+ - pattern: '($C : *gin.Context).FileAttachment($X, ...)'
254
+ - pattern: '($C : echo.Context).File($X)'
255
+ - pattern: '($C : echo.Context).Attachment($X, ...)'
256
+ - focus-metavariable: $X
257
+
258
+ - id: hullproof-taint-go-ssrf
259
+ languages: [go]
260
+ mode: taint
261
+ severity: ERROR
262
+ message: >-
263
+ Request data chooses the address of an outbound request the server makes (CWE-918). Allow only http and https,
264
+ resolve the host, refuse private, loopback, link local and metadata addresses, dial the checked address and
265
+ check every redirect. A value that only fills a path or query under a fixed host is not matched.
266
+ metadata:
267
+ cwe: "CWE-918: Server-Side Request Forgery (SSRF)"
268
+ sec_ids: [SEC-API-035]
269
+ pattern-sources:
270
+ - patterns:
271
+ - pattern: '($R : *http.Request).$M(...)'
272
+ - metavariable-regex:
273
+ metavariable: $M
274
+ regex: ^(FormValue|PostFormValue|Cookie|Cookies|FormFile|MultipartReader|Referer|UserAgent)$
275
+ - patterns:
276
+ - pattern: '($R : *http.Request).$F'
277
+ - metavariable-regex:
278
+ metavariable: $F
279
+ regex: ^(URL|Form|PostForm|MultipartForm|Header|Body|RequestURI|Host)$
280
+ - pattern: mux.Vars(...)
281
+ - pattern: chi.URLParam(...)
282
+ - patterns:
283
+ - pattern-either:
284
+ - pattern: '($C : *gin.Context).$G(...)'
285
+ - pattern: '($C : echo.Context).$G(...)'
286
+ - metavariable-regex:
287
+ metavariable: $G
288
+ regex: ^(Query|DefaultQuery|QueryArray|QueryMap|GetQuery|Param|Params|PostForm|DefaultPostForm|PostFormArray|GetPostForm|GetHeader|Cookie|FormValue|QueryParam|QueryParams|FormParams|GetRawData|FormFile|ShouldBind|ShouldBindJSON|ShouldBindQuery|Bind|BindJSON)$
289
+ pattern-sanitizers:
290
+ - pattern-either:
291
+ - pattern: strconv.Atoi(...)
292
+ - pattern: strconv.ParseInt(...)
293
+ - pattern: strconv.ParseUint(...)
294
+ - pattern: strconv.ParseFloat(...)
295
+ - pattern: strconv.ParseBool(...)
296
+ - pattern: uuid.Parse(...)
297
+
298
+ - pattern-either:
299
+ - pattern: url.QueryEscape(...)
300
+ - pattern: url.PathEscape(...)
301
+ - patterns:
302
+ - pattern: '"$URL" + $Y'
303
+ - metavariable-regex:
304
+ metavariable: $URL
305
+ regex: ^https?://[^/]+/
306
+ - patterns:
307
+ - pattern: fmt.Sprintf("$URL", ...)
308
+ - metavariable-regex:
309
+ metavariable: $URL
310
+ regex: ^https?://[^/%]+/
311
+
312
+ - by-side-effect: true
313
+ patterns:
314
+ - pattern: $GUARD($X, ...)
315
+ - metavariable-regex:
316
+ metavariable: $GUARD
317
+ regex: (?i)^(.*\.)?\w*(safe|allow|valid|guard|check|verify)\w*$
318
+ - focus-metavariable: $X
319
+ pattern-sinks:
320
+ - patterns:
321
+ - pattern-either:
322
+ - pattern: http.Get($X)
323
+ - pattern: http.Head($X)
324
+ - pattern: http.Post($X, ...)
325
+ - pattern: http.PostForm($X, ...)
326
+ - pattern: http.NewRequest($M, $X, ...)
327
+ - pattern: http.NewRequestWithContext($CTX, $M, $X, ...)
328
+ - pattern: '($CL : *http.Client).Get($X)'
329
+ - pattern: '($CL : *http.Client).Head($X)'
330
+ - pattern: '($CL : *http.Client).Post($X, ...)'
331
+ - pattern: net.Dial($N, $X)
332
+ - pattern: net.DialTimeout($N, $X, ...)
333
+ - focus-metavariable: $X