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.
- package/0sec.js +327 -0
- package/README.md +68 -48
- package/attacks/data-exfiltration/pii-leakage.yaml +27 -0
- package/attacks/encoding-bypass/base64-encoding.yaml +24 -0
- package/attacks/jailbreak/dan-roleplay.yaml +27 -0
- package/attacks/jailbreak/hypothetical-scenario.yaml +25 -0
- package/attacks/jailbreak/multilingual-bypass.yaml +22 -0
- package/attacks/output-manipulation/harmful-content.yaml +25 -0
- package/attacks/prompt-injection/context-manipulation.yaml +32 -0
- package/attacks/prompt-injection/direct-injection.yaml +28 -0
- package/attacks/prompt-injection/indirect-injection.yaml +33 -0
- package/attacks/system-prompt-extraction/direct-ask.yaml +30 -0
- package/attacks/system-prompt-extraction/markdown-exfil.yaml +26 -0
- package/attacks/tool-misuse/ssrf-via-tools.yaml +27 -0
- package/chunks/adapt-loop-SMH6V4PD.js +18 -0
- package/chunks/adgraph-JLGA6RYI.js +54 -0
- package/chunks/agent/skills/frameworks/entra-id.yaml +63 -0
- package/chunks/agent/skills/frameworks/graphql-introspection.yaml +116 -0
- package/chunks/agent/skills/frameworks/nextjs.yaml +82 -0
- package/chunks/agent/skills/frameworks/python-web.yaml +85 -0
- package/chunks/agent/skills/frameworks/supabase.yaml +83 -0
- package/chunks/agent/skills/frameworks/wordpress-deep.yaml +122 -0
- package/chunks/agent/skills/techniques/ad-attack-paths.yaml +57 -0
- package/chunks/agent/skills/techniques/advisory-disclosure.yaml +59 -0
- package/chunks/agent/skills/techniques/assumption-mining.yaml +53 -0
- package/chunks/agent/skills/techniques/blind-exploitation.yaml +143 -0
- package/chunks/agent/skills/techniques/crypto-misuse.yaml +88 -0
- package/chunks/agent/skills/techniques/cve-poc-adaptation.yaml +82 -0
- package/chunks/agent/skills/techniques/entra-attack-paths.yaml +55 -0
- package/chunks/agent/skills/techniques/http-conformance-diff.yaml +48 -0
- package/chunks/agent/skills/techniques/jwt-attacks.yaml +137 -0
- package/chunks/agent/skills/techniques/kernel-weaponization.yaml +53 -0
- package/chunks/agent/skills/techniques/llm-excessive-agency.yaml +55 -0
- package/chunks/agent/skills/techniques/llm-insecure-output-handling.yaml +56 -0
- package/chunks/agent/skills/techniques/llm-prompt-injection.yaml +54 -0
- package/chunks/agent/skills/techniques/llm-prompt-layer-write.yaml +68 -0
- package/chunks/agent/skills/techniques/llm-rag-poisoning.yaml +57 -0
- package/chunks/agent/skills/techniques/llm-safety-eval.yaml +51 -0
- package/chunks/agent/skills/techniques/npm-ecosystem.yaml +51 -0
- package/chunks/agent/skills/techniques/poc-verification.yaml +55 -0
- package/chunks/agent/skills/techniques/race-condition.yaml +122 -0
- package/chunks/agent/skills/techniques/scoped-fix.yaml +49 -0
- package/chunks/agent/skills/techniques/seedless-depth-review.yaml +59 -0
- package/chunks/agent/skills/techniques/spec-differential.yaml +48 -0
- package/chunks/agent/skills/techniques/variant-hunting.yaml +55 -0
- package/chunks/agent/skills/vulnerabilities/cardano-eutxo-validators.yaml +72 -0
- package/chunks/agent/skills/vulnerabilities/command-injection.yaml +99 -0
- package/chunks/agent/skills/vulnerabilities/deserialization-chains.yaml +106 -0
- package/chunks/agent/skills/vulnerabilities/native-memory-safety.yaml +93 -0
- package/chunks/agent/skills/vulnerabilities/path-traversal.yaml +78 -0
- package/chunks/agent/skills/vulnerabilities/prototype-pollution.yaml +132 -0
- package/chunks/agent/skills/vulnerabilities/request-smuggling.yaml +129 -0
- package/chunks/agent/skills/vulnerabilities/sqli-advanced.yaml +102 -0
- package/chunks/agent/skills/vulnerabilities/ssrf-bypass.yaml +84 -0
- package/chunks/agent/skills/vulnerabilities/ssti-exploitation.yaml +112 -0
- package/chunks/agent/skills/vulnerabilities/structural-sqli.yaml +91 -0
- package/chunks/appsec-catalog-CBWHGTEL.js +24 -0
- package/chunks/artifact-scraper-HQ7HLP6V.js +34 -0
- package/chunks/assumption-mining-5SNIVVDN.js +79 -0
- package/chunks/chunk-242TMK6G.js +110 -0
- package/chunks/chunk-2JCCA2JL.js +381 -0
- package/chunks/chunk-2RMLOJVB.js +47 -0
- package/chunks/chunk-3KPWWJCI.js +1422 -0
- package/chunks/chunk-3MOLBTLS.js +953 -0
- package/chunks/chunk-53G27VPS.js +136 -0
- package/chunks/chunk-57ZENEX2.js +169 -0
- package/chunks/chunk-5G3ZXFBW.js +182 -0
- package/chunks/chunk-5JI7L7KV.js +74240 -0
- package/chunks/chunk-6L26OBWI.js +1094 -0
- package/chunks/chunk-6LKRLK2R.js +542 -0
- package/chunks/chunk-72CISA2D.js +2597 -0
- package/chunks/chunk-7DQEV5QI.js +287 -0
- package/chunks/chunk-AD7WXDH6.js +3250 -0
- package/chunks/chunk-ADOLI6DT.js +102 -0
- package/chunks/chunk-AHTBZC3E.js +596 -0
- package/chunks/chunk-ASUR522M.js +518 -0
- package/chunks/chunk-ATATQACO.js +471 -0
- package/chunks/chunk-BATRQBOR.js +1732 -0
- package/chunks/chunk-BFR2CDV5.js +5735 -0
- package/chunks/chunk-BFRB6HB2.js +235 -0
- package/chunks/chunk-BKZFDZ23.js +361 -0
- package/chunks/chunk-BVAZO4WA.js +193 -0
- package/chunks/chunk-BZNEY2ZS.js +33225 -0
- package/chunks/chunk-C2WXWFBB.js +1070 -0
- package/chunks/chunk-CMKXP5RR.js +917 -0
- package/chunks/chunk-DW5UWPFY.js +275 -0
- package/chunks/chunk-F3WBKITT.js +687 -0
- package/chunks/chunk-GMT2AZKM.js +2439 -0
- package/chunks/chunk-H2FFLZNK.js +3 -0
- package/chunks/chunk-IOL7D5YV.js +515 -0
- package/chunks/chunk-IR537GON.js +49 -0
- package/chunks/chunk-JQHOLLTD.js +3729 -0
- package/chunks/chunk-KLTNTE2Z.js +121 -0
- package/chunks/chunk-LP3HHYQU.js +245 -0
- package/chunks/chunk-MYVT64FN.js +16 -0
- package/chunks/chunk-N7BOUYEV.js +852 -0
- package/chunks/chunk-OEFNRYI2.js +150 -0
- package/chunks/chunk-P6WKNFWX.js +1968 -0
- package/chunks/chunk-QI233I24.js +333 -0
- package/chunks/chunk-RJWOYEOG.js +271 -0
- package/chunks/chunk-RRMJC3ZE.js +105 -0
- package/chunks/chunk-RWONANDA.js +973 -0
- package/chunks/chunk-SAFFWQW4.js +5921 -0
- package/chunks/chunk-SF4KZ4O3.js +502 -0
- package/chunks/chunk-SVJEDVYK.js +808 -0
- package/chunks/chunk-TSK2AG6J.js +5108 -0
- package/chunks/chunk-U5FLXGHT.js +2010 -0
- package/chunks/chunk-UCYRA73C.js +662 -0
- package/chunks/chunk-UR4ELX2Y.js +4784 -0
- package/chunks/chunk-VQG4FT5D.js +685 -0
- package/chunks/chunk-WEBMRWEG.js +9337 -0
- package/chunks/chunk-WU6AFRAZ.js +1167 -0
- package/chunks/chunk-WVTBZEQO.js +1531 -0
- package/chunks/chunk-WXQ6BJ6A.js +596 -0
- package/chunks/chunk-XM5NLHYU.js +600 -0
- package/chunks/chunk-YLMN3N25.js +4054 -0
- package/chunks/chunk-YYRBQVFE.js +2572 -0
- package/chunks/chunk-Z5FA2XOX.js +2798 -0
- package/chunks/commands-TBC2F5CE.js +16999 -0
- package/chunks/corpus-v1.json +403 -0
- package/chunks/cost-ledger-NU63MYZM.js +13 -0
- package/chunks/data/appsec-archetypes.json +102 -0
- package/chunks/data/chromium-archetypes.json +204 -0
- package/chunks/data/freebsd-archetypes.json +171 -0
- package/chunks/data/kernel-archetypes.json +611 -0
- package/chunks/db-KFWOAUUB.js +16 -0
- package/chunks/disclose-EKTWSCGW.js +132 -0
- package/chunks/dist-4MAW5X6L.js +74 -0
- package/chunks/dist-4YICS2J2.js +2776 -0
- package/chunks/dist-FMGSR3DW.js +159 -0
- package/chunks/eval-runner-7ZF472KV.js +27 -0
- package/chunks/example-manifest.json +95 -0
- package/chunks/exploit-agent-45YQOHPJ.js +98 -0
- package/chunks/exploit-autoclimb-UZGEMGYT.js +81 -0
- package/chunks/exploit-climb-SG23KB2G.js +358 -0
- package/chunks/fix-IG7LYXH3.js +12 -0
- package/chunks/github-issues-MJ6OYOOU.js +157 -0
- package/chunks/harness-7ODOLOWU.js +22 -0
- package/chunks/http-conformance-DU66MZIU.js +11 -0
- package/chunks/http-sender-GWH2IYEA.js +10 -0
- package/chunks/hunt-scan-MMKUOETU.js +38 -0
- package/chunks/identity-6ZAIIWOR.js +191 -0
- package/chunks/kernel-primitive-7U6CWHXD.js +44 -0
- package/chunks/kernel-vm-runner-3JAU4O6J.js +62 -0
- package/chunks/memsafety-scan-J7SECV5W.js +16 -0
- package/chunks/native-loop-RJCJPOUS.js +54 -0
- package/chunks/npm-detectors-KB5Y5ZFX.js +69 -0
- package/chunks/npm-dynamic-discovery-AHROOZDB.js +13 -0
- package/chunks/orchestrate-OC2VZN2X.js +62 -0
- package/chunks/pipeline-BZK5BLJR.js +14 -0
- package/chunks/pre-recon-cve-66EB6G4M.js +351 -0
- package/chunks/prepare-MYY743TK.js +13 -0
- package/chunks/process-LQRCS5OY.js +13 -0
- package/chunks/replay-runner-45G3VU7X.js +39 -0
- package/chunks/run-D56MPDKY.js +32716 -0
- package/chunks/runtime-G7YWIO3B.js +43 -0
- package/chunks/runtime-J7PLXZNM.js +12 -0
- package/chunks/scan-stream-FHI2FYZE.js +96 -0
- package/chunks/scope-BI7BF4ZY.js +18 -0
- package/chunks/session-store-GZ4A4KYT.js +28 -0
- package/chunks/source-files-PWRQR6LY.js +12 -0
- package/chunks/specdrift-WCLH6TTQ.js +19 -0
- package/chunks/variant-candidates-YERCYMPD.js +13 -0
- package/chunks/web-recon-prepass-JJGPEOLE.js +1148 -0
- package/dashboard/assets/0sec-icon-66SreztZ.gif +0 -0
- package/dashboard/assets/bot-yILPnRgD.js +1 -0
- package/dashboard/assets/chevron-down-BeDJ-Vka.js +1 -0
- package/dashboard/assets/circle-alert-BEnMBMT-.js +1 -0
- package/dashboard/assets/client-DRYsuOGl.js +9 -0
- package/dashboard/assets/copy-Bi5vzoF9.js +1 -0
- package/dashboard/assets/desktop-CXTQonNm.js +32 -0
- package/dashboard/assets/desktop-NOekk4QS.css +1 -0
- package/dashboard/assets/dist-CmX-h7a4.js +1 -0
- package/dashboard/assets/dist-DAwYQmf_.js +45 -0
- package/dashboard/assets/findings-page-CACW9nw2.js +5 -0
- package/dashboard/assets/format-PoISqsES.js +1 -0
- package/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/dashboard/assets/jsx-runtime-C7oxC63R.js +1 -0
- package/dashboard/assets/live-page-DrgQqQK5.js +1 -0
- package/dashboard/assets/meta-tile-CcskIV_o.js +1 -0
- package/dashboard/assets/operations-BKE8T-vY.css +2 -0
- package/dashboard/assets/operations-D8nUP5_m.js +4 -0
- package/dashboard/assets/operations-app-CQQhG54n.js +2 -0
- package/dashboard/assets/overview-page-DTGS8dPo.js +1 -0
- package/dashboard/assets/page-header-MFVvEtZW.js +1 -0
- package/dashboard/assets/play-Dwo-9xt6.js +1 -0
- package/dashboard/assets/scans-page-ChVtq8Us.js +1 -0
- package/dashboard/assets/search-CiooTLck.js +1 -0
- package/dashboard/assets/siren-CCBwTSuk.js +1 -0
- package/dashboard/assets/table-BYS-nIVA.js +1 -0
- package/dashboard/assets/tabs-DivxbhQy.js +1 -0
- package/dashboard/desktop.html +21 -0
- package/dashboard/index.html +15 -0
- package/package.json +30 -18
- package/bin/0sec.cjs +0 -361
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __0secCreateRequire } from "node:module";
|
|
3
|
+
const require = __0secCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
verifyKernelFinding
|
|
6
|
+
} from "./chunk-3KPWWJCI.js";
|
|
7
|
+
import {
|
|
8
|
+
createJournalWriter
|
|
9
|
+
} from "./chunk-UCYRA73C.js";
|
|
10
|
+
import {
|
|
11
|
+
homeStateDir
|
|
12
|
+
} from "./chunk-6L26OBWI.js";
|
|
13
|
+
|
|
14
|
+
// packages/core/dist/cve/adapt-loop.js
|
|
15
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
16
|
+
import { basename } from "node:path";
|
|
17
|
+
|
|
18
|
+
// packages/core/dist/cve/poc-fetcher.js
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
var MAX_POC_BYTES = 1 * 1024 * 1024;
|
|
23
|
+
var LANG_EXT = {
|
|
24
|
+
c: "c",
|
|
25
|
+
py: "py",
|
|
26
|
+
sh: "sh",
|
|
27
|
+
syz: "syz"
|
|
28
|
+
};
|
|
29
|
+
var TEXT_CONTENT_TYPES = [
|
|
30
|
+
"text/",
|
|
31
|
+
"application/json",
|
|
32
|
+
"application/javascript",
|
|
33
|
+
"application/x-sh",
|
|
34
|
+
"application/x-shellscript",
|
|
35
|
+
"application/x-python",
|
|
36
|
+
"application/octet-stream"
|
|
37
|
+
// GitHub raw sometimes serves this for plaintext
|
|
38
|
+
];
|
|
39
|
+
var PocFetchError = class extends Error {
|
|
40
|
+
kind;
|
|
41
|
+
constructor(kind, message) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.name = "PocFetchError";
|
|
44
|
+
this.kind = kind;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function defaultCacheDir() {
|
|
48
|
+
return process.env["0SEC_CVE_POC_CACHE"]?.trim() || join(homeStateDir(), "cve-pocs");
|
|
49
|
+
}
|
|
50
|
+
function buildHeaders(token) {
|
|
51
|
+
const headers = {
|
|
52
|
+
"Accept": "*/*",
|
|
53
|
+
"User-Agent": "0sec-cve-adapt/0.1"
|
|
54
|
+
};
|
|
55
|
+
const resolved = token ?? process.env.GITHUB_TOKEN?.trim();
|
|
56
|
+
if (resolved)
|
|
57
|
+
headers["Authorization"] = `Bearer ${resolved}`;
|
|
58
|
+
return headers;
|
|
59
|
+
}
|
|
60
|
+
function isTextContentType(contentType) {
|
|
61
|
+
if (!contentType)
|
|
62
|
+
return true;
|
|
63
|
+
const lower = contentType.toLowerCase();
|
|
64
|
+
return TEXT_CONTENT_TYPES.some((prefix) => lower.includes(prefix));
|
|
65
|
+
}
|
|
66
|
+
function extractInlineCodeBlock(html) {
|
|
67
|
+
const fenced = /```(?:[a-zA-Z0-9_+-]*)\n([\s\S]*?)\n?```/m.exec(html);
|
|
68
|
+
if (fenced && fenced[1]) {
|
|
69
|
+
return fenced[1];
|
|
70
|
+
}
|
|
71
|
+
const htmlBlock = /<pre[^>]*>\s*<code[^>]*>([\s\S]*?)<\/code>\s*<\/pre>/i.exec(html);
|
|
72
|
+
if (htmlBlock && htmlBlock[1]) {
|
|
73
|
+
return decodeHtmlEntities(htmlBlock[1]);
|
|
74
|
+
}
|
|
75
|
+
const preBlock = /<pre[^>]*>([\s\S]*?)<\/pre>/i.exec(html);
|
|
76
|
+
if (preBlock && preBlock[1]) {
|
|
77
|
+
return decodeHtmlEntities(preBlock[1]);
|
|
78
|
+
}
|
|
79
|
+
throw new PocFetchError("no-inline-block", "no fenced code block found in writeup");
|
|
80
|
+
}
|
|
81
|
+
function decodeHtmlEntities(input) {
|
|
82
|
+
return input.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
83
|
+
}
|
|
84
|
+
function sha256(bytes) {
|
|
85
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
86
|
+
}
|
|
87
|
+
function cacheEntryPaths(cacheDir, sha, ext) {
|
|
88
|
+
return {
|
|
89
|
+
source: join(cacheDir, `${sha}.${ext}`),
|
|
90
|
+
meta: join(cacheDir, `${sha}.meta.json`)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function urlPointerPath(cacheDir, url) {
|
|
94
|
+
return join(cacheDir, `${sha256(url).slice(0, 24)}.url.json`);
|
|
95
|
+
}
|
|
96
|
+
function readUrlPointer(cacheDir, url) {
|
|
97
|
+
const path = urlPointerPath(cacheDir, url);
|
|
98
|
+
if (!existsSync(path))
|
|
99
|
+
return null;
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
102
|
+
if (parsed.url !== url)
|
|
103
|
+
return null;
|
|
104
|
+
return parsed;
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function writeUrlPointer(cacheDir, pointer) {
|
|
110
|
+
writeFileSync(urlPointerPath(cacheDir, pointer.url), JSON.stringify(pointer, null, 2), "utf-8");
|
|
111
|
+
}
|
|
112
|
+
async function fetchPoc(candidate, opts = {}) {
|
|
113
|
+
const cacheDir = opts.cacheDir ?? defaultCacheDir();
|
|
114
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
115
|
+
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
116
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
117
|
+
const pointer = readUrlPointer(cacheDir, candidate.url);
|
|
118
|
+
if (pointer) {
|
|
119
|
+
const { source: source2 } = cacheEntryPaths(cacheDir, pointer.sha256, pointer.ext);
|
|
120
|
+
if (existsSync(source2)) {
|
|
121
|
+
const bytes = readFileSync(source2);
|
|
122
|
+
if (sha256(bytes) === pointer.sha256) {
|
|
123
|
+
return {
|
|
124
|
+
local_path: source2,
|
|
125
|
+
language: candidate.language,
|
|
126
|
+
sha256: pointer.sha256,
|
|
127
|
+
source_url: candidate.url,
|
|
128
|
+
fetched_at: pointer.fetched_at
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
let response;
|
|
134
|
+
try {
|
|
135
|
+
response = await fetchImpl(candidate.url, {
|
|
136
|
+
method: "GET",
|
|
137
|
+
headers: buildHeaders(opts.githubToken)
|
|
138
|
+
});
|
|
139
|
+
} catch (err) {
|
|
140
|
+
throw new PocFetchError("network", `network error fetching ${candidate.url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
141
|
+
}
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
throw new PocFetchError("http", `HTTP ${response.status} fetching ${candidate.url}: ${response.statusText}`);
|
|
144
|
+
}
|
|
145
|
+
const contentLength = response.headers.get("content-length");
|
|
146
|
+
if (contentLength && parseInt(contentLength, 10) > MAX_POC_BYTES) {
|
|
147
|
+
throw new PocFetchError("too-large", `PoC at ${candidate.url} is ${contentLength} bytes (cap ${MAX_POC_BYTES})`);
|
|
148
|
+
}
|
|
149
|
+
const contentType = response.headers.get("content-type");
|
|
150
|
+
if (!isTextContentType(contentType)) {
|
|
151
|
+
throw new PocFetchError("non-text", `PoC at ${candidate.url} has non-text content-type: ${contentType}`);
|
|
152
|
+
}
|
|
153
|
+
const buf = Buffer.from(await response.arrayBuffer());
|
|
154
|
+
if (buf.byteLength > MAX_POC_BYTES) {
|
|
155
|
+
throw new PocFetchError("too-large", `PoC at ${candidate.url} is ${buf.byteLength} bytes (cap ${MAX_POC_BYTES})`);
|
|
156
|
+
}
|
|
157
|
+
let payload = buf.toString("utf-8");
|
|
158
|
+
if (candidate.source === "inline-writeup") {
|
|
159
|
+
try {
|
|
160
|
+
payload = extractInlineCodeBlock(payload);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (err instanceof PocFetchError)
|
|
163
|
+
throw err;
|
|
164
|
+
throw new PocFetchError("extract", `failed to extract inline code block: ${String(err)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const newSha = sha256(payload);
|
|
168
|
+
const ext = LANG_EXT[candidate.language];
|
|
169
|
+
const { source } = cacheEntryPaths(cacheDir, newSha, ext);
|
|
170
|
+
if (pointer && pointer.sha256 !== newSha && opts.onShaMismatch) {
|
|
171
|
+
opts.onShaMismatch({ url: candidate.url, oldSha: pointer.sha256, newSha });
|
|
172
|
+
}
|
|
173
|
+
writeFileSync(source, payload, "utf-8");
|
|
174
|
+
const fetched_at = now().toISOString();
|
|
175
|
+
writeUrlPointer(cacheDir, { url: candidate.url, sha256: newSha, ext, fetched_at });
|
|
176
|
+
return {
|
|
177
|
+
local_path: source,
|
|
178
|
+
language: candidate.language,
|
|
179
|
+
sha256: newSha,
|
|
180
|
+
source_url: candidate.url,
|
|
181
|
+
fetched_at
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// packages/core/dist/cve/adapt-loop.js
|
|
186
|
+
var DEFAULT_ATTEMPTS = 5;
|
|
187
|
+
var DEFAULT_WALL_CLOCK_MS = 30 * 60 * 1e3;
|
|
188
|
+
function applyUnifiedDiff(source, diff) {
|
|
189
|
+
const sourceLines = source.split("\n");
|
|
190
|
+
const diffLines = diff.replace(/\r\n/g, "\n").split("\n");
|
|
191
|
+
while (diffLines.length > 0 && diffLines[diffLines.length - 1] === "") {
|
|
192
|
+
diffLines.pop();
|
|
193
|
+
}
|
|
194
|
+
let i = 0;
|
|
195
|
+
while (i < diffLines.length && !diffLines[i].startsWith("@@"))
|
|
196
|
+
i += 1;
|
|
197
|
+
if (i >= diffLines.length) {
|
|
198
|
+
return source;
|
|
199
|
+
}
|
|
200
|
+
const out = [...sourceLines];
|
|
201
|
+
let cursorOffset = 0;
|
|
202
|
+
while (i < diffLines.length) {
|
|
203
|
+
const header = diffLines[i];
|
|
204
|
+
const match = /^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@/.exec(header);
|
|
205
|
+
if (!match) {
|
|
206
|
+
throw new Error(`malformed hunk header: ${header}`);
|
|
207
|
+
}
|
|
208
|
+
const oldStart = parseInt(match[1], 10);
|
|
209
|
+
i += 1;
|
|
210
|
+
let target = oldStart - 1 + cursorOffset;
|
|
211
|
+
while (i < diffLines.length && !diffLines[i].startsWith("@@")) {
|
|
212
|
+
const line = diffLines[i];
|
|
213
|
+
const marker = line[0];
|
|
214
|
+
const text = line.slice(1);
|
|
215
|
+
if (marker === " ") {
|
|
216
|
+
if (out[target] !== text) {
|
|
217
|
+
throw new Error(`context mismatch at line ${target + 1}: expected "${text}", got "${out[target] ?? "<eof>"}"`);
|
|
218
|
+
}
|
|
219
|
+
target += 1;
|
|
220
|
+
} else if (marker === "-") {
|
|
221
|
+
if (out[target] !== text) {
|
|
222
|
+
throw new Error(`delete mismatch at line ${target + 1}: expected "${text}", got "${out[target] ?? "<eof>"}"`);
|
|
223
|
+
}
|
|
224
|
+
out.splice(target, 1);
|
|
225
|
+
cursorOffset -= 1;
|
|
226
|
+
} else if (marker === "+") {
|
|
227
|
+
out.splice(target, 0, text);
|
|
228
|
+
target += 1;
|
|
229
|
+
cursorOffset += 1;
|
|
230
|
+
} else if (marker === "\\") {
|
|
231
|
+
} else {
|
|
232
|
+
throw new Error(`unknown diff marker '${marker ?? "<empty>"}' at line: ${line}`);
|
|
233
|
+
}
|
|
234
|
+
i += 1;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return out.join("\n");
|
|
238
|
+
}
|
|
239
|
+
function inferAdaptedFilename(originalPath, attemptIndex) {
|
|
240
|
+
const base = basename(originalPath);
|
|
241
|
+
const dot = base.lastIndexOf(".");
|
|
242
|
+
if (dot < 0)
|
|
243
|
+
return `${base}.adapt${attemptIndex}`;
|
|
244
|
+
return `${base.slice(0, dot)}.adapt${attemptIndex}${base.slice(dot)}`;
|
|
245
|
+
}
|
|
246
|
+
function defaultAgent() {
|
|
247
|
+
return async () => {
|
|
248
|
+
return "";
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function renderAdaptationPrompt(input) {
|
|
252
|
+
const lines = [];
|
|
253
|
+
lines.push(`# CVE adaptation request \u2014 ${input.cveId}`);
|
|
254
|
+
lines.push(`Attempt ${input.attemptIndex} did not reproduce the bug.`);
|
|
255
|
+
lines.push("");
|
|
256
|
+
lines.push("Your job: produce a **unified diff** against the PoC source below that", "makes the reproducer trigger on the target kernel. Output ONLY the diff,", "starting with `--- a/poc.<ext>` and `+++ b/poc.<ext>` headers. If you have", "no useful adaptation, output an empty diff (no `@@` hunks).");
|
|
257
|
+
lines.push("");
|
|
258
|
+
lines.push(`## PoC source (language=${input.pocLanguage})`);
|
|
259
|
+
lines.push("```");
|
|
260
|
+
lines.push(input.pocSource);
|
|
261
|
+
lines.push("```");
|
|
262
|
+
if (input.writeupText) {
|
|
263
|
+
lines.push("");
|
|
264
|
+
lines.push("## Writeup excerpt");
|
|
265
|
+
lines.push(input.writeupText);
|
|
266
|
+
}
|
|
267
|
+
if (input.targetKernelSubsystemSource) {
|
|
268
|
+
lines.push("");
|
|
269
|
+
lines.push("## Target kernel subsystem source (relevant slice)");
|
|
270
|
+
lines.push("```c");
|
|
271
|
+
lines.push(input.targetKernelSubsystemSource);
|
|
272
|
+
lines.push("```");
|
|
273
|
+
}
|
|
274
|
+
lines.push("");
|
|
275
|
+
lines.push("## Error log (build / dmesg from last attempt)");
|
|
276
|
+
lines.push("```");
|
|
277
|
+
lines.push(input.errorLog);
|
|
278
|
+
lines.push("```");
|
|
279
|
+
return lines.join("\n");
|
|
280
|
+
}
|
|
281
|
+
function nowMs(clock) {
|
|
282
|
+
return clock ? clock() : Date.now();
|
|
283
|
+
}
|
|
284
|
+
async function adaptAndVerify(cveId, opts) {
|
|
285
|
+
const attemptsBudget = opts.attempts ?? DEFAULT_ATTEMPTS;
|
|
286
|
+
const wallClockMs = opts.wallClockMs ?? DEFAULT_WALL_CLOCK_MS;
|
|
287
|
+
const fetcher = opts.fetcher ?? fetchPoc;
|
|
288
|
+
const runner = opts.runner ?? verifyKernelFinding;
|
|
289
|
+
const agent = opts.agent ?? defaultAgent();
|
|
290
|
+
const clock = opts.clock;
|
|
291
|
+
const startedAt = nowMs(clock);
|
|
292
|
+
const deadline = startedAt + wallClockMs;
|
|
293
|
+
const runId = opts.runId ?? `cve-adapt-${cveId.toLowerCase()}-${startedAt}`;
|
|
294
|
+
const journal = (opts.journalFactory ?? ((id) => createJournalWriter({ runId: id })))(runId);
|
|
295
|
+
journal.append({
|
|
296
|
+
kind: "dispatch",
|
|
297
|
+
targetAgent: "cve-adapt-loop",
|
|
298
|
+
objective: `adapt + verify PoC for ${cveId}`,
|
|
299
|
+
context: { kernelTree: opts.kernelTree, kernelConfig: opts.kernelConfig ?? "kasan", attemptsBudget, wallClockMs }
|
|
300
|
+
});
|
|
301
|
+
let artifacts;
|
|
302
|
+
try {
|
|
303
|
+
artifacts = await opts.artifactProvider(cveId);
|
|
304
|
+
} catch (err) {
|
|
305
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
306
|
+
journal.append({ kind: "error", message: `artifact-provider failed: ${message}` });
|
|
307
|
+
journal.append({ kind: "done", status: "failed", summary: "artifact provider failed" });
|
|
308
|
+
return {
|
|
309
|
+
status: "no_artifact",
|
|
310
|
+
cveId,
|
|
311
|
+
attempts: [],
|
|
312
|
+
total_ms: nowMs(clock) - startedAt
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
if (!artifacts.pocCandidates || artifacts.pocCandidates.length === 0) {
|
|
316
|
+
journal.append({
|
|
317
|
+
kind: "observation",
|
|
318
|
+
source: "cve-adapt-loop",
|
|
319
|
+
summary: "no PoC candidates returned by artifact provider",
|
|
320
|
+
data: { cveId }
|
|
321
|
+
});
|
|
322
|
+
journal.append({ kind: "done", status: "failed", summary: "no_artifact" });
|
|
323
|
+
return {
|
|
324
|
+
status: "no_artifact",
|
|
325
|
+
cveId,
|
|
326
|
+
attempts: [],
|
|
327
|
+
total_ms: nowMs(clock) - startedAt
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const ordered = [...artifacts.pocCandidates].sort((a, b) => b.confidence - a.confidence);
|
|
331
|
+
const attempts = [];
|
|
332
|
+
let totalAttempts = 0;
|
|
333
|
+
let lastPocPath;
|
|
334
|
+
for (const candidate of ordered) {
|
|
335
|
+
if (totalAttempts >= attemptsBudget)
|
|
336
|
+
break;
|
|
337
|
+
if (nowMs(clock) >= deadline)
|
|
338
|
+
break;
|
|
339
|
+
journal.append({
|
|
340
|
+
kind: "observation",
|
|
341
|
+
source: "cve-adapt-loop",
|
|
342
|
+
summary: "attempt_started",
|
|
343
|
+
data: { candidate, attemptIndex: totalAttempts }
|
|
344
|
+
});
|
|
345
|
+
const attemptStart = nowMs(clock);
|
|
346
|
+
let fetched = null;
|
|
347
|
+
try {
|
|
348
|
+
fetched = await fetcher(candidate, {});
|
|
349
|
+
} catch (err) {
|
|
350
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
351
|
+
journal.append({ kind: "error", message: `poc_fetch_failed: ${message}` });
|
|
352
|
+
attempts.push({
|
|
353
|
+
candidate,
|
|
354
|
+
attemptIndex: totalAttempts,
|
|
355
|
+
fetched: null,
|
|
356
|
+
verification: null,
|
|
357
|
+
diffApplied: false,
|
|
358
|
+
error: message,
|
|
359
|
+
durationMs: nowMs(clock) - attemptStart
|
|
360
|
+
});
|
|
361
|
+
totalAttempts += 1;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
journal.append({
|
|
365
|
+
kind: "observation",
|
|
366
|
+
source: "cve-adapt-loop",
|
|
367
|
+
summary: "poc_fetched",
|
|
368
|
+
data: { sha256: fetched.sha256, source_url: fetched.source_url, local_path: fetched.local_path }
|
|
369
|
+
});
|
|
370
|
+
let currentPath = fetched.local_path;
|
|
371
|
+
let confirmed = null;
|
|
372
|
+
while (totalAttempts < attemptsBudget && nowMs(clock) < deadline) {
|
|
373
|
+
const verify = await runVerifySafe(runner, currentPath, fetched.language, opts);
|
|
374
|
+
journal.append({
|
|
375
|
+
kind: "observation",
|
|
376
|
+
source: "cve-adapt-loop",
|
|
377
|
+
summary: "verify_run",
|
|
378
|
+
data: {
|
|
379
|
+
attemptIndex: totalAttempts,
|
|
380
|
+
status: verify.status,
|
|
381
|
+
signature: verify.signature ?? null,
|
|
382
|
+
dmesg_path: verify.dmesg_path
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
if (verify.status === "reproduced") {
|
|
386
|
+
confirmed = verify;
|
|
387
|
+
attempts.push({
|
|
388
|
+
candidate,
|
|
389
|
+
attemptIndex: totalAttempts,
|
|
390
|
+
fetched,
|
|
391
|
+
verification: verify,
|
|
392
|
+
diffApplied: currentPath !== fetched.local_path,
|
|
393
|
+
durationMs: nowMs(clock) - attemptStart
|
|
394
|
+
});
|
|
395
|
+
lastPocPath = currentPath;
|
|
396
|
+
totalAttempts += 1;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
attempts.push({
|
|
400
|
+
candidate,
|
|
401
|
+
attemptIndex: totalAttempts,
|
|
402
|
+
fetched,
|
|
403
|
+
verification: verify,
|
|
404
|
+
diffApplied: currentPath !== fetched.local_path,
|
|
405
|
+
durationMs: nowMs(clock) - attemptStart
|
|
406
|
+
});
|
|
407
|
+
totalAttempts += 1;
|
|
408
|
+
lastPocPath = currentPath;
|
|
409
|
+
if (totalAttempts >= attemptsBudget || nowMs(clock) >= deadline)
|
|
410
|
+
break;
|
|
411
|
+
const errorLog = existsSync2(verify.dmesg_path) ? readFileSync2(verify.dmesg_path, "utf-8") : "";
|
|
412
|
+
const pocSource = readFileSync2(currentPath, "utf-8");
|
|
413
|
+
const promptInput = {
|
|
414
|
+
cveId,
|
|
415
|
+
pocSource,
|
|
416
|
+
pocLanguage: fetched.language,
|
|
417
|
+
writeupText: artifacts.writeupText,
|
|
418
|
+
errorLog,
|
|
419
|
+
attemptIndex: totalAttempts
|
|
420
|
+
};
|
|
421
|
+
let diff = "";
|
|
422
|
+
try {
|
|
423
|
+
diff = await agent(promptInput);
|
|
424
|
+
} catch (err) {
|
|
425
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
426
|
+
journal.append({ kind: "error", message: `agent_failed: ${message}` });
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
if (!diff.trim() || !diff.includes("@@")) {
|
|
430
|
+
journal.append({
|
|
431
|
+
kind: "observation",
|
|
432
|
+
source: "cve-adapt-loop",
|
|
433
|
+
summary: "adapt_diff_empty",
|
|
434
|
+
data: { attemptIndex: totalAttempts }
|
|
435
|
+
});
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
let nextSource;
|
|
439
|
+
try {
|
|
440
|
+
nextSource = applyUnifiedDiff(pocSource, diff);
|
|
441
|
+
} catch (err) {
|
|
442
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
443
|
+
journal.append({
|
|
444
|
+
kind: "error",
|
|
445
|
+
message: `apply_diff_failed: ${message}`
|
|
446
|
+
});
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
const nextPath = inferAdaptedFilename(fetched.local_path, totalAttempts);
|
|
450
|
+
const nextAbs = currentPath.replace(basename(currentPath), basename(nextPath));
|
|
451
|
+
writeFileSync2(nextAbs, nextSource, "utf-8");
|
|
452
|
+
journal.append({
|
|
453
|
+
kind: "observation",
|
|
454
|
+
source: "cve-adapt-loop",
|
|
455
|
+
summary: "adapt_diff_applied",
|
|
456
|
+
data: { attemptIndex: totalAttempts, prev_path: currentPath, next_path: nextAbs },
|
|
457
|
+
artifacts: [
|
|
458
|
+
{ name: "adapt.diff", mediaType: "text/x-diff", content: diff }
|
|
459
|
+
]
|
|
460
|
+
});
|
|
461
|
+
currentPath = nextAbs;
|
|
462
|
+
lastPocPath = nextAbs;
|
|
463
|
+
}
|
|
464
|
+
if (confirmed) {
|
|
465
|
+
journal.append({ kind: "done", status: "success", summary: "reproduced" });
|
|
466
|
+
return {
|
|
467
|
+
status: "confirmed",
|
|
468
|
+
cveId,
|
|
469
|
+
attempts,
|
|
470
|
+
final_poc_path: lastPocPath,
|
|
471
|
+
signature: confirmed.signature,
|
|
472
|
+
total_ms: nowMs(clock) - startedAt
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const exhaustedByBudget = totalAttempts >= attemptsBudget || nowMs(clock) >= deadline;
|
|
477
|
+
const status = exhaustedByBudget ? "budget_exhausted" : "unreproduced";
|
|
478
|
+
journal.append({
|
|
479
|
+
kind: "done",
|
|
480
|
+
status: "failed",
|
|
481
|
+
summary: status
|
|
482
|
+
});
|
|
483
|
+
return {
|
|
484
|
+
status,
|
|
485
|
+
cveId,
|
|
486
|
+
attempts,
|
|
487
|
+
final_poc_path: lastPocPath,
|
|
488
|
+
total_ms: nowMs(clock) - startedAt
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
async function runVerifySafe(runner, reproducerPath, language, opts) {
|
|
492
|
+
const isSyz = language === "syz";
|
|
493
|
+
try {
|
|
494
|
+
return await runner({
|
|
495
|
+
kernelTree: opts.kernelTree,
|
|
496
|
+
kernelConfig: opts.kernelConfig,
|
|
497
|
+
reproducerPath: isSyz ? void 0 : reproducerPath,
|
|
498
|
+
syzProgramPath: isSyz ? reproducerPath : void 0
|
|
499
|
+
});
|
|
500
|
+
} catch (err) {
|
|
501
|
+
return {
|
|
502
|
+
status: "run_failed",
|
|
503
|
+
dmesg_path: "",
|
|
504
|
+
build_cache_hit: false,
|
|
505
|
+
signature: void 0
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export {
|
|
511
|
+
MAX_POC_BYTES,
|
|
512
|
+
PocFetchError,
|
|
513
|
+
extractInlineCodeBlock,
|
|
514
|
+
fetchPoc,
|
|
515
|
+
applyUnifiedDiff,
|
|
516
|
+
renderAdaptationPrompt,
|
|
517
|
+
adaptAndVerify
|
|
518
|
+
};
|