0sec-cli 0.15.0 → 0.18.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 +327 -0
  2. package/README.md +68 -48
  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-SMH6V4PD.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-HQ7HLP6V.js +34 -0
  59. package/chunks/assumption-mining-5SNIVVDN.js +79 -0
  60. package/chunks/chunk-242TMK6G.js +110 -0
  61. package/chunks/chunk-2JCCA2JL.js +381 -0
  62. package/chunks/chunk-2RMLOJVB.js +47 -0
  63. package/chunks/chunk-3KPWWJCI.js +1422 -0
  64. package/chunks/chunk-3MOLBTLS.js +953 -0
  65. package/chunks/chunk-53G27VPS.js +136 -0
  66. package/chunks/chunk-57ZENEX2.js +169 -0
  67. package/chunks/chunk-5G3ZXFBW.js +182 -0
  68. package/chunks/chunk-5JI7L7KV.js +74240 -0
  69. package/chunks/chunk-6L26OBWI.js +1094 -0
  70. package/chunks/chunk-6LKRLK2R.js +542 -0
  71. package/chunks/chunk-72CISA2D.js +2597 -0
  72. package/chunks/chunk-7DQEV5QI.js +287 -0
  73. package/chunks/chunk-AD7WXDH6.js +3250 -0
  74. package/chunks/chunk-ADOLI6DT.js +102 -0
  75. package/chunks/chunk-AHTBZC3E.js +596 -0
  76. package/chunks/chunk-ASUR522M.js +518 -0
  77. package/chunks/chunk-ATATQACO.js +471 -0
  78. package/chunks/chunk-BATRQBOR.js +1732 -0
  79. package/chunks/chunk-BFR2CDV5.js +5735 -0
  80. package/chunks/chunk-BFRB6HB2.js +235 -0
  81. package/chunks/chunk-BKZFDZ23.js +361 -0
  82. package/chunks/chunk-BVAZO4WA.js +193 -0
  83. package/chunks/chunk-BZNEY2ZS.js +33225 -0
  84. package/chunks/chunk-C2WXWFBB.js +1070 -0
  85. package/chunks/chunk-CMKXP5RR.js +917 -0
  86. package/chunks/chunk-DW5UWPFY.js +275 -0
  87. package/chunks/chunk-F3WBKITT.js +687 -0
  88. package/chunks/chunk-GMT2AZKM.js +2439 -0
  89. package/chunks/chunk-H2FFLZNK.js +3 -0
  90. package/chunks/chunk-IOL7D5YV.js +515 -0
  91. package/chunks/chunk-IR537GON.js +49 -0
  92. package/chunks/chunk-JQHOLLTD.js +3729 -0
  93. package/chunks/chunk-KLTNTE2Z.js +121 -0
  94. package/chunks/chunk-LP3HHYQU.js +245 -0
  95. package/chunks/chunk-MYVT64FN.js +16 -0
  96. package/chunks/chunk-N7BOUYEV.js +852 -0
  97. package/chunks/chunk-OEFNRYI2.js +150 -0
  98. package/chunks/chunk-P6WKNFWX.js +1968 -0
  99. package/chunks/chunk-QI233I24.js +333 -0
  100. package/chunks/chunk-RJWOYEOG.js +271 -0
  101. package/chunks/chunk-RRMJC3ZE.js +105 -0
  102. package/chunks/chunk-RWONANDA.js +973 -0
  103. package/chunks/chunk-SAFFWQW4.js +5921 -0
  104. package/chunks/chunk-SF4KZ4O3.js +502 -0
  105. package/chunks/chunk-SVJEDVYK.js +808 -0
  106. package/chunks/chunk-TSK2AG6J.js +5108 -0
  107. package/chunks/chunk-U5FLXGHT.js +2010 -0
  108. package/chunks/chunk-UCYRA73C.js +662 -0
  109. package/chunks/chunk-UR4ELX2Y.js +4784 -0
  110. package/chunks/chunk-VQG4FT5D.js +685 -0
  111. package/chunks/chunk-WEBMRWEG.js +9337 -0
  112. package/chunks/chunk-WU6AFRAZ.js +1167 -0
  113. package/chunks/chunk-WVTBZEQO.js +1531 -0
  114. package/chunks/chunk-WXQ6BJ6A.js +596 -0
  115. package/chunks/chunk-XM5NLHYU.js +600 -0
  116. package/chunks/chunk-YLMN3N25.js +4054 -0
  117. package/chunks/chunk-YYRBQVFE.js +2572 -0
  118. package/chunks/chunk-Z5FA2XOX.js +2798 -0
  119. package/chunks/commands-TBC2F5CE.js +16999 -0
  120. package/chunks/corpus-v1.json +403 -0
  121. package/chunks/cost-ledger-NU63MYZM.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-KFWOAUUB.js +16 -0
  127. package/chunks/disclose-EKTWSCGW.js +132 -0
  128. package/chunks/dist-4MAW5X6L.js +74 -0
  129. package/chunks/dist-4YICS2J2.js +2776 -0
  130. package/chunks/dist-FMGSR3DW.js +159 -0
  131. package/chunks/eval-runner-7ZF472KV.js +27 -0
  132. package/chunks/example-manifest.json +95 -0
  133. package/chunks/exploit-agent-45YQOHPJ.js +98 -0
  134. package/chunks/exploit-autoclimb-UZGEMGYT.js +81 -0
  135. package/chunks/exploit-climb-SG23KB2G.js +358 -0
  136. package/chunks/fix-IG7LYXH3.js +12 -0
  137. package/chunks/github-issues-MJ6OYOOU.js +157 -0
  138. package/chunks/harness-7ODOLOWU.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-MMKUOETU.js +38 -0
  142. package/chunks/identity-6ZAIIWOR.js +191 -0
  143. package/chunks/kernel-primitive-7U6CWHXD.js +44 -0
  144. package/chunks/kernel-vm-runner-3JAU4O6J.js +62 -0
  145. package/chunks/memsafety-scan-J7SECV5W.js +16 -0
  146. package/chunks/native-loop-RJCJPOUS.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-OC2VZN2X.js +62 -0
  150. package/chunks/pipeline-BZK5BLJR.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-LQRCS5OY.js +13 -0
  154. package/chunks/replay-runner-45G3VU7X.js +39 -0
  155. package/chunks/run-D56MPDKY.js +32716 -0
  156. package/chunks/runtime-G7YWIO3B.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-GZ4A4KYT.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-YERCYMPD.js +13 -0
  164. package/chunks/web-recon-prepass-JJGPEOLE.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,808 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __0secCreateRequire } from "node:module";
3
+ const require = __0secCreateRequire(import.meta.url);
4
+ import {
5
+ homeStateDir
6
+ } from "./chunk-6L26OBWI.js";
7
+
8
+ // packages/core/dist/cve/artifact-scraper.js
9
+ import { createHash } from "node:crypto";
10
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { dirname, join } from "node:path";
12
+ function normaliseCveId(input) {
13
+ const trimmed = input.trim().toUpperCase();
14
+ if (!/^CVE-\d{4}-\d{4,}$/.test(trimmed)) {
15
+ throw new Error(`invalid CVE id: ${JSON.stringify(input)}`);
16
+ }
17
+ return trimmed;
18
+ }
19
+ var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
20
+ function defaultCacheDir() {
21
+ return join(homeStateDir(), "cve-cache");
22
+ }
23
+ function cacheKey(source, query) {
24
+ const h = createHash("sha256").update(query).digest("hex").slice(0, 24);
25
+ return `${source}/${h}.json`;
26
+ }
27
+ function readCache(cacheDir, source, query, now, ttlMs) {
28
+ const file = join(cacheDir, cacheKey(source, query));
29
+ if (!existsSync(file))
30
+ return void 0;
31
+ try {
32
+ const raw = readFileSync(file, "utf8");
33
+ const parsed = JSON.parse(raw);
34
+ if (typeof parsed.savedAt !== "number")
35
+ return void 0;
36
+ if (now - parsed.savedAt > ttlMs)
37
+ return void 0;
38
+ return parsed.payload;
39
+ } catch {
40
+ return void 0;
41
+ }
42
+ }
43
+ function writeCache(cacheDir, source, query, now, payload) {
44
+ const file = join(cacheDir, cacheKey(source, query));
45
+ try {
46
+ mkdirSync(dirname(file), { recursive: true, mode: 448 });
47
+ writeFileSync(file, JSON.stringify({ savedAt: now, payload }), {
48
+ mode: 384
49
+ });
50
+ } catch {
51
+ }
52
+ }
53
+ async function fetchJsonWithRetry(url, opts) {
54
+ const treat404AsMiss = opts.treat404AsMiss ?? true;
55
+ if (opts.cacheDir && !opts.noCache) {
56
+ const hit = readCache(opts.cacheDir, opts.source, opts.cacheQuery, opts.now(), opts.ttlMs);
57
+ if (hit !== void 0) {
58
+ return { data: hit, ok: true, status: 200, cached: true, durationMs: 0 };
59
+ }
60
+ }
61
+ let lastErr;
62
+ let lastStatus = 0;
63
+ let attempt = 0;
64
+ const start = opts.now();
65
+ while (attempt <= opts.retries) {
66
+ attempt += 1;
67
+ const controller = new AbortController();
68
+ const timer = setTimeout(() => controller.abort(), opts.timeoutMs);
69
+ try {
70
+ const res = await opts.fetchImpl(url, {
71
+ ...opts.init ?? {},
72
+ // AbortSignal isn't part of the FetchLike contract above, but
73
+ // the real fetch honours it; for mock fetches it's a no-op.
74
+ ...{ signal: controller.signal }
75
+ });
76
+ clearTimeout(timer);
77
+ lastStatus = res.status;
78
+ if (treat404AsMiss && res.status === 404) {
79
+ return {
80
+ ok: false,
81
+ status: 404,
82
+ durationMs: opts.now() - start
83
+ };
84
+ }
85
+ if (res.status === 429) {
86
+ return {
87
+ ok: false,
88
+ status: 429,
89
+ rateLimited: true,
90
+ error: "rate limited",
91
+ durationMs: opts.now() - start
92
+ };
93
+ }
94
+ if (res.status === 403) {
95
+ const remaining = res.headers?.get?.("x-ratelimit-remaining");
96
+ if (remaining === "0") {
97
+ return {
98
+ ok: false,
99
+ status: 403,
100
+ rateLimited: true,
101
+ error: "GitHub API rate limit (set GITHUB_TOKEN to lift)",
102
+ durationMs: opts.now() - start
103
+ };
104
+ }
105
+ }
106
+ if (res.status >= 500 && res.status < 600) {
107
+ lastErr = `HTTP ${res.status}`;
108
+ if (attempt <= opts.retries) {
109
+ await delay(opts.backoffMs * Math.pow(2, attempt - 1));
110
+ continue;
111
+ }
112
+ return {
113
+ ok: false,
114
+ status: res.status,
115
+ error: lastErr,
116
+ durationMs: opts.now() - start
117
+ };
118
+ }
119
+ if (!res.ok) {
120
+ return {
121
+ ok: false,
122
+ status: res.status,
123
+ error: `HTTP ${res.status}`,
124
+ durationMs: opts.now() - start
125
+ };
126
+ }
127
+ const data = await res.json();
128
+ if (opts.cacheDir && !opts.noCache) {
129
+ writeCache(opts.cacheDir, opts.source, opts.cacheQuery, opts.now(), data);
130
+ }
131
+ return {
132
+ data,
133
+ ok: true,
134
+ status: res.status,
135
+ durationMs: opts.now() - start
136
+ };
137
+ } catch (err) {
138
+ clearTimeout(timer);
139
+ lastErr = err instanceof Error ? err.message : String(err);
140
+ if (attempt <= opts.retries) {
141
+ await delay(opts.backoffMs * Math.pow(2, attempt - 1));
142
+ continue;
143
+ }
144
+ return {
145
+ ok: false,
146
+ status: lastStatus,
147
+ error: lastErr,
148
+ durationMs: opts.now() - start
149
+ };
150
+ }
151
+ }
152
+ return {
153
+ ok: false,
154
+ status: lastStatus,
155
+ error: lastErr ?? "exhausted retries",
156
+ durationMs: opts.now() - start
157
+ };
158
+ }
159
+ function delay(ms) {
160
+ return new Promise((resolve) => {
161
+ if (ms <= 0)
162
+ resolve();
163
+ else
164
+ setTimeout(resolve, ms);
165
+ });
166
+ }
167
+ function parseNvdResponse(json, cveId) {
168
+ const item = (json.vulnerabilities ?? []).find((v) => v.cve?.id?.toUpperCase() === cveId);
169
+ if (!item?.cve) {
170
+ return { references: [], affected: [] };
171
+ }
172
+ const description = (item.cve.descriptions ?? []).find((d) => d.lang === "en")?.value;
173
+ const references = item.cve.references ?? [];
174
+ const affected = [];
175
+ for (const cfg of item.cve.configurations ?? []) {
176
+ for (const node of cfg.nodes ?? []) {
177
+ for (const m of node.cpeMatch ?? []) {
178
+ if (m.vulnerable === false)
179
+ continue;
180
+ const range = renderCpeRange(m);
181
+ if (!range)
182
+ continue;
183
+ const parsed = parseCpeUri(m.criteria);
184
+ affected.push({
185
+ vendor: parsed?.vendor,
186
+ product: parsed?.product,
187
+ range,
188
+ source: "nvd"
189
+ });
190
+ }
191
+ }
192
+ }
193
+ return {
194
+ description,
195
+ published: item.cve.published,
196
+ references,
197
+ affected
198
+ };
199
+ }
200
+ function renderCpeRange(m) {
201
+ const parts = [];
202
+ if (m.versionStartIncluding)
203
+ parts.push(`>=${m.versionStartIncluding}`);
204
+ if (m.versionStartExcluding)
205
+ parts.push(`>${m.versionStartExcluding}`);
206
+ if (m.versionEndIncluding)
207
+ parts.push(`<=${m.versionEndIncluding}`);
208
+ if (m.versionEndExcluding)
209
+ parts.push(`<${m.versionEndExcluding}`);
210
+ if (parts.length === 0 && m.criteria) {
211
+ return m.criteria;
212
+ }
213
+ return parts.join(",");
214
+ }
215
+ function parseCpeUri(uri) {
216
+ if (!uri)
217
+ return void 0;
218
+ const parts = uri.split(":");
219
+ if (parts.length < 6)
220
+ return void 0;
221
+ return { vendor: parts[3] ?? "", product: parts[4] ?? "" };
222
+ }
223
+ async function fetchNvd(cveId, plumbing, log) {
224
+ const url = `https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=${encodeURIComponent(cveId)}`;
225
+ const result = await fetchJsonWithRetry(url, {
226
+ ...plumbing,
227
+ source: "nvd",
228
+ cacheQuery: `GET ${url}`
229
+ });
230
+ log.push({
231
+ source: "nvd",
232
+ url,
233
+ status: result.cached ? "cached" : result.ok ? "ok" : result.status === 404 ? "miss" : result.rateLimited ? "rate-limited" : "error",
234
+ httpStatus: result.status || void 0,
235
+ error: result.error,
236
+ durationMs: result.durationMs
237
+ });
238
+ if (!result.ok || !result.data) {
239
+ return { references: [], affected: [] };
240
+ }
241
+ return parseNvdResponse(result.data, cveId);
242
+ }
243
+ function parseGhsaResponse(json, cveId) {
244
+ const items = Array.isArray(json) ? json : json && typeof json === "object" && "advisories" in json ? json.advisories ?? [] : [];
245
+ if (items.length === 0)
246
+ return { references: [], affected: [] };
247
+ const adv = items.find((it) => (it.cve_id ?? "").toUpperCase() === cveId) ?? items[0];
248
+ const references = normaliseGhsaRefs(adv.references);
249
+ const affected = [];
250
+ for (const v of adv.vulnerabilities ?? []) {
251
+ if (!v.vulnerable_version_range)
252
+ continue;
253
+ affected.push({
254
+ product: v.package?.name,
255
+ range: v.vulnerable_version_range,
256
+ source: "ghsa"
257
+ });
258
+ }
259
+ return {
260
+ description: adv.summary ?? adv.description,
261
+ published: adv.published_at,
262
+ references,
263
+ affected
264
+ };
265
+ }
266
+ function normaliseGhsaRefs(refs) {
267
+ if (!refs)
268
+ return [];
269
+ if (Array.isArray(refs)) {
270
+ return refs.map((r) => typeof r === "string" ? r : r?.url ?? "").filter((s) => s.length > 0);
271
+ }
272
+ return [];
273
+ }
274
+ async function fetchGhsa(cveId, plumbing, githubToken, log) {
275
+ const url = `https://api.github.com/advisories?cve_id=${encodeURIComponent(cveId)}`;
276
+ const headers = {
277
+ accept: "application/vnd.github+json",
278
+ "user-agent": "0sec-cve-scraper"
279
+ };
280
+ if (githubToken)
281
+ headers.authorization = `Bearer ${githubToken}`;
282
+ const result = await fetchJsonWithRetry(url, {
283
+ ...plumbing,
284
+ source: "ghsa",
285
+ cacheQuery: `GET ${url}`,
286
+ init: { method: "GET", headers }
287
+ });
288
+ log.push({
289
+ source: "ghsa",
290
+ url,
291
+ status: result.cached ? "cached" : result.ok ? "ok" : result.status === 404 ? "miss" : result.rateLimited ? "rate-limited" : "error",
292
+ httpStatus: result.status || void 0,
293
+ error: result.error,
294
+ durationMs: result.durationMs
295
+ });
296
+ if (!result.ok || !result.data) {
297
+ return { references: [], affected: [] };
298
+ }
299
+ return parseGhsaResponse(result.data, cveId);
300
+ }
301
+ function parseOsvResponse(json) {
302
+ const references = (json.references ?? []).map((r) => r.url).filter((u) => typeof u === "string" && u.length > 0);
303
+ const affected = [];
304
+ for (const aff of json.affected ?? []) {
305
+ for (const rng of aff.ranges ?? []) {
306
+ const parts = [];
307
+ for (const ev of rng.events ?? []) {
308
+ if (ev.introduced && ev.introduced !== "0")
309
+ parts.push(`>=${ev.introduced}`);
310
+ if (ev.fixed)
311
+ parts.push(`<${ev.fixed}`);
312
+ if (ev.last_affected)
313
+ parts.push(`<=${ev.last_affected}`);
314
+ }
315
+ if (parts.length === 0)
316
+ continue;
317
+ affected.push({
318
+ product: aff.package?.name,
319
+ range: parts.join(","),
320
+ source: "osv"
321
+ });
322
+ }
323
+ }
324
+ return {
325
+ description: json.summary ?? json.details,
326
+ published: json.published,
327
+ references,
328
+ affected
329
+ };
330
+ }
331
+ async function fetchOsv(cveId, plumbing, log) {
332
+ const url = `https://api.osv.dev/v1/vulns/${encodeURIComponent(cveId)}`;
333
+ const result = await fetchJsonWithRetry(url, {
334
+ ...plumbing,
335
+ source: "osv",
336
+ cacheQuery: `GET ${url}`
337
+ });
338
+ log.push({
339
+ source: "osv",
340
+ url,
341
+ status: result.cached ? "cached" : result.ok ? "ok" : result.status === 404 ? "miss" : result.rateLimited ? "rate-limited" : "error",
342
+ httpStatus: result.status || void 0,
343
+ error: result.error,
344
+ durationMs: result.durationMs
345
+ });
346
+ if (!result.ok || !result.data) {
347
+ return { references: [], affected: [] };
348
+ }
349
+ return parseOsvResponse(result.data);
350
+ }
351
+ function findUbuntuTrackerUrls(refs) {
352
+ const out = /* @__PURE__ */ new Set();
353
+ for (const ref of refs) {
354
+ const m = ref.match(/ubuntu\.com\/security\/notices\/(USN-[A-Z0-9-]+)/i);
355
+ if (m)
356
+ out.add(`https://ubuntu.com/security/notices/${m[1]}.json`);
357
+ }
358
+ return [...out];
359
+ }
360
+ function findRedHatTrackerUrls(refs, cveId) {
361
+ const out = /* @__PURE__ */ new Set();
362
+ for (const ref of refs) {
363
+ const m = ref.match(/access\.redhat\.com\/security\/cve\/(CVE-[\d-]+)/i);
364
+ if (m) {
365
+ out.add(`https://access.redhat.com/hydra/rest/securitydata/cve/${m[1]}.json`);
366
+ }
367
+ }
368
+ if (out.size === 0) {
369
+ out.add(`https://access.redhat.com/hydra/rest/securitydata/cve/${cveId}.json`);
370
+ }
371
+ return [...out];
372
+ }
373
+ function parseUbuntuTracker(json) {
374
+ const refs = (json.references ?? []).filter((s) => typeof s === "string" && s.length > 0);
375
+ const affected = [];
376
+ for (const [release, payload] of Object.entries(json.releases ?? {})) {
377
+ for (const [pkg, info] of Object.entries(payload?.sources ?? {})) {
378
+ if (!info?.version)
379
+ continue;
380
+ affected.push({
381
+ product: pkg,
382
+ vendor: `ubuntu/${release}`,
383
+ range: `<${info.version}`,
384
+ source: "ubuntu"
385
+ });
386
+ }
387
+ }
388
+ return { references: refs, affected };
389
+ }
390
+ function parseRedHatTracker(json) {
391
+ const refs = /* @__PURE__ */ new Set();
392
+ if (json.bugzilla?.url)
393
+ refs.add(json.bugzilla.url);
394
+ for (const u of json.references ?? []) {
395
+ if (typeof u === "string" && u.length > 0)
396
+ refs.add(u);
397
+ }
398
+ const affected = [];
399
+ for (const rel of json.affected_release ?? []) {
400
+ if (!rel.package)
401
+ continue;
402
+ affected.push({
403
+ product: rel.package,
404
+ vendor: rel.product_name ?? "redhat",
405
+ range: `fixed in ${rel.package}`,
406
+ source: "redhat"
407
+ });
408
+ }
409
+ return { references: [...refs], affected };
410
+ }
411
+ async function fetchUbuntu(url, plumbing, log) {
412
+ const result = await fetchJsonWithRetry(url, {
413
+ ...plumbing,
414
+ source: "ubuntu",
415
+ cacheQuery: `GET ${url}`
416
+ });
417
+ log.push({
418
+ source: "ubuntu",
419
+ url,
420
+ status: result.cached ? "cached" : result.ok ? "ok" : result.status === 404 ? "miss" : result.rateLimited ? "rate-limited" : "error",
421
+ httpStatus: result.status || void 0,
422
+ error: result.error,
423
+ durationMs: result.durationMs
424
+ });
425
+ if (!result.ok || !result.data)
426
+ return { references: [], affected: [] };
427
+ return parseUbuntuTracker(result.data);
428
+ }
429
+ async function fetchRedHat(url, plumbing, log) {
430
+ const result = await fetchJsonWithRetry(url, {
431
+ ...plumbing,
432
+ source: "redhat",
433
+ cacheQuery: `GET ${url}`
434
+ });
435
+ log.push({
436
+ source: "redhat",
437
+ url,
438
+ status: result.cached ? "cached" : result.ok ? "ok" : result.status === 404 ? "miss" : result.rateLimited ? "rate-limited" : "error",
439
+ httpStatus: result.status || void 0,
440
+ error: result.error,
441
+ durationMs: result.durationMs
442
+ });
443
+ if (!result.ok || !result.data)
444
+ return { references: [], affected: [] };
445
+ return parseRedHatTracker(result.data);
446
+ }
447
+ function scoreRepoCandidate(repo, cveId) {
448
+ const stars = repo.stargazers_count ?? 0;
449
+ let score = 0;
450
+ score += Math.min(0.5, Math.log10(stars + 1) / 4);
451
+ const name = (repo.full_name ?? "").toLowerCase();
452
+ const desc = (repo.description ?? "").toLowerCase();
453
+ const cveLower = cveId.toLowerCase();
454
+ if (name.includes(cveLower))
455
+ score += 0.35;
456
+ else if (desc.includes(cveLower))
457
+ score += 0.15;
458
+ if (/poc|exploit|reproducer/.test(name))
459
+ score += 0.12;
460
+ else if (/poc|exploit|reproducer/.test(desc))
461
+ score += 0.06;
462
+ if (repo.fork)
463
+ score -= 0.2;
464
+ if (repo.pushed_at) {
465
+ const updated = Date.parse(repo.pushed_at);
466
+ if (Number.isFinite(updated)) {
467
+ const ageDays = (Date.now() - updated) / 864e5;
468
+ if (ageDays < 30)
469
+ score += 0.08;
470
+ else if (ageDays < 365)
471
+ score += 0.04;
472
+ else if (ageDays > 365 * 3)
473
+ score -= 0.05;
474
+ }
475
+ }
476
+ const language = languageFromHint(repo.language);
477
+ return { confidence: clamp01(score), language };
478
+ }
479
+ function scoreCodeCandidate(item, cveId) {
480
+ const name = (item.name ?? "").toLowerCase();
481
+ const path = (item.path ?? "").toLowerCase();
482
+ const cveLower = cveId.toLowerCase();
483
+ let score = 0;
484
+ if (name === "poc.c" || name === "exploit.c")
485
+ score += 0.4;
486
+ if (name === `${cveLower}.c`)
487
+ score += 0.5;
488
+ if (name.includes(cveLower))
489
+ score += 0.2;
490
+ if (/poc|exploit/.test(path))
491
+ score += 0.1;
492
+ const stars = item.repository?.stargazers_count ?? 0;
493
+ score += Math.min(0.3, Math.log10(stars + 1) / 5);
494
+ if (item.repository?.fork)
495
+ score -= 0.2;
496
+ return {
497
+ confidence: clamp01(score),
498
+ language: languageFromFilename(name) ?? "c"
499
+ };
500
+ }
501
+ function languageFromHint(lang) {
502
+ if (!lang)
503
+ return void 0;
504
+ const l = lang.toLowerCase();
505
+ if (l === "c" || l === "c++")
506
+ return "c";
507
+ if (l === "python")
508
+ return "python";
509
+ if (l === "shell" || l === "sh" || l === "bash")
510
+ return "shell";
511
+ return "unknown";
512
+ }
513
+ function languageFromFilename(name) {
514
+ if (name.endsWith(".c") || name.endsWith(".cpp") || name.endsWith(".cc"))
515
+ return "c";
516
+ if (name.endsWith(".py"))
517
+ return "python";
518
+ if (name.endsWith(".syz") || name.endsWith(".syzprog"))
519
+ return "syz";
520
+ if (name.endsWith(".sh") || name.endsWith(".bash"))
521
+ return "shell";
522
+ return void 0;
523
+ }
524
+ function clamp01(n) {
525
+ if (!Number.isFinite(n))
526
+ return 0;
527
+ if (n < 0)
528
+ return 0;
529
+ if (n > 1)
530
+ return 1;
531
+ return n;
532
+ }
533
+ async function fetchGithubPocSearch(cveId, plumbing, githubToken, log) {
534
+ const headers = {
535
+ accept: "application/vnd.github+json",
536
+ "user-agent": "0sec-cve-scraper"
537
+ };
538
+ if (githubToken)
539
+ headers.authorization = `Bearer ${githubToken}`;
540
+ const candidates = [];
541
+ const reposUrl = `https://api.github.com/search/repositories?q=${encodeURIComponent(cveId)}`;
542
+ const repos = await fetchJsonWithRetry(reposUrl, {
543
+ ...plumbing,
544
+ source: "github-search",
545
+ cacheQuery: `GET ${reposUrl}`,
546
+ init: { method: "GET", headers }
547
+ });
548
+ log.push({
549
+ source: "github-search",
550
+ url: reposUrl,
551
+ status: repos.cached ? "cached" : repos.ok ? "ok" : repos.rateLimited ? "rate-limited" : repos.status === 404 ? "miss" : "error",
552
+ httpStatus: repos.status || void 0,
553
+ error: repos.error,
554
+ durationMs: repos.durationMs
555
+ });
556
+ if (repos.ok && repos.data) {
557
+ for (const r of repos.data.items ?? []) {
558
+ const { confidence, language } = scoreRepoCandidate(r, cveId);
559
+ if (confidence <= 0 || !r.html_url)
560
+ continue;
561
+ candidates.push({
562
+ url: r.html_url,
563
+ source: "github",
564
+ confidence,
565
+ language,
566
+ label: r.full_name,
567
+ stars: r.stargazers_count,
568
+ updatedAt: r.pushed_at ?? r.updated_at
569
+ });
570
+ }
571
+ }
572
+ const codeUrl = `https://api.github.com/search/code?q=${encodeURIComponent(`${cveId} in:file filename:*.c`)}`;
573
+ if (githubToken) {
574
+ const code = await fetchJsonWithRetry(codeUrl, {
575
+ ...plumbing,
576
+ source: "github-search",
577
+ cacheQuery: `GET ${codeUrl}`,
578
+ init: { method: "GET", headers }
579
+ });
580
+ log.push({
581
+ source: "github-search",
582
+ url: codeUrl,
583
+ status: code.cached ? "cached" : code.ok ? "ok" : code.rateLimited ? "rate-limited" : code.status === 404 ? "miss" : "error",
584
+ httpStatus: code.status || void 0,
585
+ error: code.error,
586
+ durationMs: code.durationMs
587
+ });
588
+ if (code.ok && code.data) {
589
+ for (const item of code.data.items ?? []) {
590
+ const { confidence, language } = scoreCodeCandidate(item, cveId);
591
+ if (confidence <= 0 || !item.html_url)
592
+ continue;
593
+ candidates.push({
594
+ url: item.html_url,
595
+ source: "github",
596
+ confidence,
597
+ language,
598
+ label: `${item.repository?.full_name ?? ""}:${item.path ?? item.name ?? ""}`,
599
+ stars: item.repository?.stargazers_count,
600
+ updatedAt: item.repository?.pushed_at
601
+ });
602
+ }
603
+ }
604
+ } else {
605
+ log.push({
606
+ source: "github-search",
607
+ url: codeUrl,
608
+ status: "skipped",
609
+ durationMs: 0,
610
+ error: "code search requires GITHUB_TOKEN"
611
+ });
612
+ }
613
+ return candidates;
614
+ }
615
+ function classifyReferences(refs, source = "inline-writeup") {
616
+ const pocs = [];
617
+ const writeups = [];
618
+ for (const refRaw of refs) {
619
+ const url = typeof refRaw === "string" ? refRaw : refRaw.url;
620
+ if (!url)
621
+ continue;
622
+ const tags = typeof refRaw === "string" ? [] : refRaw.tags ?? [];
623
+ const lowered = url.toLowerCase();
624
+ if (/^https?:\/\/(www\.)?github\.com\/[^/]+\/[^/]+/.test(url)) {
625
+ const isPatch = /\/(commit|pull|blob|security|advisories)\//i.test(url);
626
+ if (!isPatch || tags.includes("Exploit")) {
627
+ pocs.push({
628
+ url,
629
+ source: "github",
630
+ confidence: tags.includes("Exploit") ? 0.85 : 0.5,
631
+ language: languageFromFilename(url.toLowerCase()) ?? void 0
632
+ });
633
+ }
634
+ writeups.push(url);
635
+ continue;
636
+ }
637
+ if (/^https?:\/\/gist\.github\.com\//.test(url)) {
638
+ pocs.push({
639
+ url,
640
+ source: "gist",
641
+ confidence: tags.includes("Exploit") ? 0.8 : 0.55
642
+ });
643
+ writeups.push(url);
644
+ continue;
645
+ }
646
+ if (tags.includes("Exploit")) {
647
+ pocs.push({ url, source, confidence: 0.7 });
648
+ }
649
+ writeups.push(url);
650
+ void lowered;
651
+ }
652
+ return { pocs, writeups };
653
+ }
654
+ async function findCveArtifacts(cveIdInput, opts = {}) {
655
+ const cveId = normaliseCveId(cveIdInput);
656
+ const cache = opts.cache !== false;
657
+ const cacheDir = cache ? opts.cacheDir ?? defaultCacheDir() : void 0;
658
+ const ttlMs = opts.cacheTtlMs ?? DEFAULT_TTL_MS;
659
+ const timeoutMs = opts.timeoutMs ?? 1e4;
660
+ const retries = opts.retries ?? 2;
661
+ const backoffMs = opts.backoffMs ?? 250;
662
+ const now = opts.now ?? (() => Date.now());
663
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
664
+ if (!fetchImpl) {
665
+ return {
666
+ cve_id: cveId,
667
+ writeup_urls: [],
668
+ poc_urls: [],
669
+ affected: [],
670
+ sources: [
671
+ {
672
+ source: "nvd",
673
+ url: "",
674
+ status: "error",
675
+ error: "no fetch impl available",
676
+ durationMs: 0
677
+ }
678
+ ]
679
+ };
680
+ }
681
+ const githubToken = opts.githubToken ?? process.env.GITHUB_TOKEN;
682
+ const log = [];
683
+ const plumbing = {
684
+ fetchImpl,
685
+ timeoutMs,
686
+ retries,
687
+ backoffMs,
688
+ now,
689
+ cacheDir,
690
+ ttlMs,
691
+ noCache: !cache
692
+ };
693
+ const nvd = await fetchNvd(cveId, plumbing, log);
694
+ const ghsa = await fetchGhsa(cveId, plumbing, githubToken, log);
695
+ let osvOut = { references: [], affected: [] };
696
+ if (ghsa.references.length === 0 && ghsa.affected.length === 0) {
697
+ osvOut = await fetchOsv(cveId, plumbing, log);
698
+ } else {
699
+ log.push({
700
+ source: "osv",
701
+ url: `https://api.osv.dev/v1/vulns/${cveId}`,
702
+ status: "skipped",
703
+ durationMs: 0,
704
+ error: "GHSA had hits \u2014 OSV fallback skipped"
705
+ });
706
+ }
707
+ const allRefsNvd = nvd.references;
708
+ const allRefsString = [...ghsa.references, ...osvOut.references];
709
+ const nvdSplit = classifyReferences(allRefsNvd, "inline-writeup");
710
+ const otherSplit = classifyReferences(allRefsString, "inline-writeup");
711
+ const flatRefs = [
712
+ ...allRefsString,
713
+ ...allRefsNvd.map((r) => r.url)
714
+ ];
715
+ const ubuntuUrls = findUbuntuTrackerUrls(flatRefs);
716
+ const redHatUrls = findRedHatTrackerUrls(flatRefs, cveId);
717
+ const distroResults = await Promise.all([
718
+ ...ubuntuUrls.map((u) => fetchUbuntu(u, plumbing, log)),
719
+ ...redHatUrls.map((u) => fetchRedHat(u, plumbing, log))
720
+ ]);
721
+ const ghSearchCandidates = opts.skipGithubPocSearch ? [] : await fetchGithubPocSearch(cveId, plumbing, githubToken, log);
722
+ const description = nvd.description ?? ghsa.description ?? osvOut.description;
723
+ const published = nvd.published ?? ghsa.published ?? osvOut.published;
724
+ const writeup_urls = uniqueStrings([
725
+ ...nvdSplit.writeups,
726
+ ...otherSplit.writeups,
727
+ ...distroResults.flatMap((d) => d.references)
728
+ ]);
729
+ const allPocs = [
730
+ ...nvdSplit.pocs.map((p) => ({ ...p, source: p.source })),
731
+ ...otherSplit.pocs,
732
+ ...ghSearchCandidates
733
+ ];
734
+ const poc_urls = dedupePocs(allPocs).sort((a, b) => b.confidence - a.confidence);
735
+ const affected = dedupeAffected([
736
+ ...nvd.affected,
737
+ ...ghsa.affected,
738
+ ...osvOut.affected,
739
+ ...distroResults.flatMap((d) => d.affected)
740
+ ]);
741
+ return {
742
+ cve_id: cveId,
743
+ description,
744
+ published,
745
+ writeup_urls,
746
+ poc_urls,
747
+ affected,
748
+ sources: log
749
+ };
750
+ }
751
+ function uniqueStrings(arr) {
752
+ const seen = /* @__PURE__ */ new Set();
753
+ const out = [];
754
+ for (const s of arr) {
755
+ if (!s || seen.has(s))
756
+ continue;
757
+ seen.add(s);
758
+ out.push(s);
759
+ }
760
+ return out;
761
+ }
762
+ function dedupePocs(pocs) {
763
+ const byUrl = /* @__PURE__ */ new Map();
764
+ for (const p of pocs) {
765
+ const existing = byUrl.get(p.url);
766
+ if (!existing) {
767
+ byUrl.set(p.url, p);
768
+ continue;
769
+ }
770
+ const merged = {
771
+ ...existing,
772
+ confidence: Math.max(existing.confidence, p.confidence),
773
+ language: existing.language ?? p.language,
774
+ label: existing.label ?? p.label,
775
+ stars: existing.stars ?? p.stars,
776
+ updatedAt: existing.updatedAt ?? p.updatedAt
777
+ };
778
+ byUrl.set(p.url, merged);
779
+ }
780
+ return [...byUrl.values()];
781
+ }
782
+ function dedupeAffected(rows) {
783
+ const seen = /* @__PURE__ */ new Set();
784
+ const out = [];
785
+ for (const r of rows) {
786
+ const key = `${r.source}|${r.vendor ?? ""}|${r.product ?? ""}|${r.range}`;
787
+ if (seen.has(key))
788
+ continue;
789
+ seen.add(key);
790
+ out.push(r);
791
+ }
792
+ return out;
793
+ }
794
+
795
+ export {
796
+ normaliseCveId,
797
+ parseNvdResponse,
798
+ parseGhsaResponse,
799
+ parseOsvResponse,
800
+ findUbuntuTrackerUrls,
801
+ findRedHatTrackerUrls,
802
+ parseUbuntuTracker,
803
+ parseRedHatTracker,
804
+ scoreRepoCandidate,
805
+ scoreCodeCandidate,
806
+ classifyReferences,
807
+ findCveArtifacts
808
+ };