@blamejs/exceptd-skills 0.12.41 → 0.13.1
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/CHANGELOG.md +124 -0
- package/bin/exceptd.js +52 -44
- package/data/_indexes/_meta.json +49 -49
- package/data/_indexes/activity-feed.json +2 -2
- package/data/_indexes/catalog-summaries.json +2 -2
- package/data/_indexes/chains.json +1531 -575
- package/data/_indexes/jurisdiction-map.json +15 -4
- package/data/_indexes/section-offsets.json +1244 -1244
- package/data/_indexes/token-budget.json +173 -173
- package/data/atlas-ttps.json +55 -11
- package/data/attack-techniques.json +124 -19
- package/data/cve-catalog.json +194 -27
- package/data/cwe-catalog.json +15 -5
- package/data/framework-control-gaps.json +32 -10
- package/data/playbooks/ai-api.json +5 -0
- package/data/playbooks/cicd-pipeline-compromise.json +970 -0
- package/data/playbooks/cloud-iam-incident.json +4 -1
- package/data/playbooks/cred-stores.json +10 -0
- package/data/playbooks/framework.json +16 -0
- package/data/playbooks/hardening.json +4 -0
- package/data/playbooks/identity-sso-compromise.json +951 -0
- package/data/playbooks/idp-incident.json +3 -0
- package/data/playbooks/kernel.json +6 -0
- package/data/playbooks/llm-tool-use-exfil.json +963 -0
- package/data/playbooks/mcp.json +6 -0
- package/data/playbooks/runtime.json +4 -0
- package/data/playbooks/sbom.json +13 -0
- package/data/playbooks/secrets.json +6 -0
- package/data/playbooks/webhook-callback-abuse.json +916 -0
- package/data/zeroday-lessons.json +178 -0
- package/lib/cross-ref-api.js +33 -13
- package/lib/cve-curation.js +12 -1
- package/lib/exit-codes.js +29 -0
- package/lib/lint-skills.js +24 -2
- package/lib/refresh-external.js +17 -1
- package/lib/scoring.js +55 -0
- package/lib/source-advisories.js +281 -0
- package/manifest.json +83 -83
- package/orchestrator/index.js +207 -24
- package/package.json +1 -1
- package/sbom.cdx.json +134 -79
- package/scripts/predeploy.js +7 -13
- package/scripts/refresh-reverse-refs.js +86 -0
- package/scripts/refresh-sbom.js +21 -4
- package/skills/age-gates-child-safety/skill.md +1 -5
- package/skills/ai-attack-surface/skill.md +11 -4
- package/skills/ai-c2-detection/skill.md +11 -2
- package/skills/ai-risk-management/skill.md +4 -2
- package/skills/api-security/skill.md +7 -8
- package/skills/attack-surface-pentest/skill.md +2 -2
- package/skills/cloud-iam-incident/skill.md +1 -5
- package/skills/cloud-security/skill.md +0 -4
- package/skills/compliance-theater/skill.md +10 -2
- package/skills/container-runtime-security/skill.md +1 -3
- package/skills/dlp-gap-analysis/skill.md +3 -4
- package/skills/email-security-anti-phishing/skill.md +1 -8
- package/skills/exploit-scoring/skill.md +7 -2
- package/skills/framework-gap-analysis/skill.md +1 -1
- package/skills/fuzz-testing-strategy/skill.md +1 -2
- package/skills/global-grc/skill.md +3 -2
- package/skills/identity-assurance/skill.md +1 -3
- package/skills/idp-incident-response/skill.md +1 -4
- package/skills/incident-response-playbook/skill.md +1 -5
- package/skills/kernel-lpe-triage/skill.md +2 -2
- package/skills/mcp-agent-trust/skill.md +13 -3
- package/skills/mlops-security/skill.md +2 -3
- package/skills/ot-ics-security/skill.md +0 -3
- package/skills/policy-exception-gen/skill.md +11 -3
- package/skills/pqc-first/skill.md +4 -2
- package/skills/rag-pipeline-security/skill.md +2 -0
- package/skills/ransomware-response/skill.md +1 -5
- package/skills/researcher/skill.md +4 -3
- package/skills/sector-energy/skill.md +0 -4
- package/skills/sector-federal-government/skill.md +2 -3
- package/skills/sector-financial/skill.md +1 -4
- package/skills/sector-healthcare/skill.md +0 -5
- package/skills/sector-telecom/skill.md +0 -4
- package/skills/security-maturity-tiers/skill.md +1 -2
- package/skills/skill-update-loop/skill.md +4 -3
- package/skills/supply-chain-integrity/skill.md +4 -3
- package/skills/threat-model-currency/skill.md +1 -1
- package/skills/threat-modeling-methodology/skill.md +2 -1
- package/skills/webapp-security/skill.md +0 -5
package/orchestrator/index.js
CHANGED
|
@@ -27,6 +27,7 @@ const { dispatch, routeQuery, getSkillContext } = require('./dispatcher');
|
|
|
27
27
|
const { currencyCheck, initPipeline } = require('./pipeline');
|
|
28
28
|
const { bus, EVENT_TYPES } = require('./event-bus');
|
|
29
29
|
const { start: startScheduler, stop: stopScheduler, runCurrencyNow } = require('./scheduler');
|
|
30
|
+
const { EXIT_CODES, safeExit } = require('../lib/exit-codes');
|
|
30
31
|
|
|
31
32
|
const cmd = process.argv[2];
|
|
32
33
|
const args = process.argv.slice(3);
|
|
@@ -145,9 +146,11 @@ Examples:
|
|
|
145
146
|
exceptd framework-gap NIST-800-53 CVE-2026-31431
|
|
146
147
|
exceptd framework-gap PCI-DSS-4.0 "prompt injection"
|
|
147
148
|
exceptd framework-gap all CVE-2025-53773 --json`);
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
|
|
149
|
+
// v0.13 exit-code class fix: usage error is GENERIC_FAILURE (1),
|
|
150
|
+
// not DETECTED_ESCALATE (2). Pre-v0.13 the orchestrator emitted
|
|
151
|
+
// exit 2 for usage errors, colliding with CI gates that branch on
|
|
152
|
+
// exit 2 to mean "verb ran + detected escalation-worthy finding".
|
|
153
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
151
154
|
return;
|
|
152
155
|
}
|
|
153
156
|
|
|
@@ -158,7 +161,7 @@ Examples:
|
|
|
158
161
|
cveCatalog = JSON.parse(fs.readFileSync(path.join(root, 'data', 'cve-catalog.json'), 'utf8'));
|
|
159
162
|
} catch (err) {
|
|
160
163
|
console.error(`[framework-gap] cannot read catalog: ${err.message}`);
|
|
161
|
-
|
|
164
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
162
165
|
return;
|
|
163
166
|
}
|
|
164
167
|
|
|
@@ -296,23 +299,19 @@ async function runDispatch() {
|
|
|
296
299
|
|
|
297
300
|
function runSkillContext(skillName) {
|
|
298
301
|
if (!skillName) {
|
|
299
|
-
// v0.12.40: operator-facing surface uses the canonical `exceptd skill
|
|
300
|
-
// <name>` form, not the orchestrator path that's an implementation
|
|
301
|
-
// detail.
|
|
302
302
|
console.error('Usage: exceptd skill <skill-name>');
|
|
303
303
|
console.error(' (Lists available skills: exceptd brief --all)');
|
|
304
|
-
|
|
304
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
305
305
|
return;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
const context = getSkillContext(skillName);
|
|
309
309
|
if (!context) {
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
process.exitCode = 1;
|
|
310
|
+
// v0.13 envelope harmonization: ok:false bodies land on stdout
|
|
311
|
+
// alongside successful results so a single consumer can parse the
|
|
312
|
+
// verb's envelope without splitting across two streams.
|
|
313
|
+
process.stdout.write(JSON.stringify({ ok: false, verb: "skill", error: `Skill not found: ${skillName}`, hint: "Run `exceptd brief --all` or check skills/ for available skill IDs." }) + "\n");
|
|
314
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
316
315
|
return;
|
|
317
316
|
}
|
|
318
317
|
|
|
@@ -376,15 +375,17 @@ async function runReport(format) {
|
|
|
376
375
|
// string. Now: reject with structured JSON error matching other verbs.
|
|
377
376
|
const VALID_REPORT_FORMATS = ['executive', 'technical', 'compliance', 'csaf'];
|
|
378
377
|
if (!VALID_REPORT_FORMATS.includes(format)) {
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
|
|
378
|
+
// v0.13 envelope harmonization: ok:false body on stdout, exit 1
|
|
379
|
+
// (GENERIC_FAILURE) not 2 (DETECTED_ESCALATE). Pre-v0.13 the
|
|
380
|
+
// body went to stderr and exit was 2; both broke CI consumers
|
|
381
|
+
// that expected the dispatch-error vs verb-finding distinction.
|
|
382
|
+
process.stdout.write(JSON.stringify({
|
|
382
383
|
ok: false,
|
|
383
|
-
error: `report: format "${format}" not in accepted set ${JSON.stringify(VALID_REPORT_FORMATS)}.`,
|
|
384
384
|
verb: 'report',
|
|
385
|
+
error: `report: format "${format}" not in accepted set ${JSON.stringify(VALID_REPORT_FORMATS)}.`,
|
|
385
386
|
accepted_formats: VALID_REPORT_FORMATS,
|
|
386
387
|
}) + '\n');
|
|
387
|
-
|
|
388
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
388
389
|
return;
|
|
389
390
|
}
|
|
390
391
|
|
|
@@ -705,7 +706,8 @@ async function runValidateCves(rawArgs = []) {
|
|
|
705
706
|
catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
|
|
706
707
|
} catch (err) {
|
|
707
708
|
console.error(`[validate-cves] cannot read ${catalogPath}: ${err.message}`);
|
|
708
|
-
|
|
709
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
710
|
+
return;
|
|
709
711
|
}
|
|
710
712
|
|
|
711
713
|
// --since <ISO|YYYY-MM-DD>: scope-limit validation to CVEs whose
|
|
@@ -875,7 +877,7 @@ async function runValidateCves(rawArgs = []) {
|
|
|
875
877
|
}
|
|
876
878
|
if (driftFound > 0) {
|
|
877
879
|
console.log(`\n[validate-cves] DRIFT DETECTED on ${driftFound} CVE(s). Update data/cve-catalog.json and bump source_verified.`);
|
|
878
|
-
if (!noFail)
|
|
880
|
+
if (!noFail) { safeExit(EXIT_CODES.GENERIC_FAILURE); return; }
|
|
879
881
|
} else {
|
|
880
882
|
console.log('[validate-cves] No drift detected against reachable sources.');
|
|
881
883
|
}
|
|
@@ -926,7 +928,8 @@ async function runValidateRfcs(rawArgs = []) {
|
|
|
926
928
|
refs = JSON.parse(fs.readFileSync(refsPath, 'utf8'));
|
|
927
929
|
} catch (err) {
|
|
928
930
|
console.error(`[validate-rfcs] cannot read ${refsPath}: ${err.message}`);
|
|
929
|
-
|
|
931
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
932
|
+
return;
|
|
930
933
|
}
|
|
931
934
|
|
|
932
935
|
// --since <ISO|YYYY-MM-DD>: scope-limit (parity with validate-cves).
|
|
@@ -1055,7 +1058,7 @@ async function runValidateRfcs(rawArgs = []) {
|
|
|
1055
1058
|
console.log();
|
|
1056
1059
|
if (driftFound > 0) {
|
|
1057
1060
|
console.log(`[validate-rfcs] DRIFT DETECTED on ${driftFound} entry(ies). Update data/rfc-references.json and bump last_verified.`);
|
|
1058
|
-
if (!noFail)
|
|
1061
|
+
if (!noFail) { safeExit(EXIT_CODES.GENERIC_FAILURE); return; }
|
|
1059
1062
|
} else if (unreachable > 0) {
|
|
1060
1063
|
console.log(`[validate-rfcs] ${unreachable} entry(ies) unreachable. Network/IETF Datatracker is intermittent — re-run later.`);
|
|
1061
1064
|
} else if (!offline && validator) {
|
|
@@ -1082,15 +1085,26 @@ function runWatchlist(rawArgs = []) {
|
|
|
1082
1085
|
const { parseFrontmatter, extractFrontmatterBlock } = require('../lib/lint-skills.js');
|
|
1083
1086
|
|
|
1084
1087
|
const byskill = rawArgs.includes('--by-skill');
|
|
1088
|
+
const alertsMode = rawArgs.includes('--alerts');
|
|
1085
1089
|
const manifestPath = path.join(__dirname, '..', 'manifest.json');
|
|
1086
1090
|
const repoRoot = path.join(__dirname, '..');
|
|
1087
1091
|
|
|
1092
|
+
// v0.13.1: --alerts re-scopes watchlist from "skills forward_watch" to
|
|
1093
|
+
// "CVE-class alert patterns" — surfaces catalog entries matching
|
|
1094
|
+
// high-priority shape rules (kernel-LPE-with-PoC, supply-chain-family,
|
|
1095
|
+
// AI-discovered-KEV, recently-disclosed-with-active-exploitation).
|
|
1096
|
+
// The two modes are mutually exclusive; --alerts short-circuits the
|
|
1097
|
+
// forward-watch aggregation.
|
|
1098
|
+
if (alertsMode) {
|
|
1099
|
+
return runWatchlistAlerts(rawArgs);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1088
1102
|
let manifest;
|
|
1089
1103
|
try {
|
|
1090
1104
|
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
1091
1105
|
} catch (err) {
|
|
1092
1106
|
console.error(`[watchlist] cannot read ${manifestPath}: ${err.message}`);
|
|
1093
|
-
|
|
1107
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
1094
1108
|
return;
|
|
1095
1109
|
}
|
|
1096
1110
|
|
|
@@ -1138,7 +1152,12 @@ function runWatchlist(rawArgs = []) {
|
|
|
1138
1152
|
|
|
1139
1153
|
const jsonOut = rawArgs.includes('--json');
|
|
1140
1154
|
if (jsonOut) {
|
|
1155
|
+
// v0.13.0 envelope harmonization: top-level `ok: true` so every
|
|
1156
|
+
// verb's JSON body shares the contract whether emitted by
|
|
1157
|
+
// bin/exceptd.js emit() (which auto-defaults `ok`) or by the
|
|
1158
|
+
// orchestrator dispatch (which writes stdout directly).
|
|
1141
1159
|
const out = {
|
|
1160
|
+
ok: true,
|
|
1142
1161
|
generated_at: new Date().toISOString(),
|
|
1143
1162
|
skills_scanned: skills.length,
|
|
1144
1163
|
parse_errors: parseErrors,
|
|
@@ -1192,6 +1211,170 @@ function runWatchlist(rawArgs = []) {
|
|
|
1192
1211
|
console.log(`Run with --by-skill to invert the view.`);
|
|
1193
1212
|
}
|
|
1194
1213
|
|
|
1214
|
+
/**
|
|
1215
|
+
* v0.13.1 — runWatchlistAlerts surfaces CVE catalog entries matching
|
|
1216
|
+
* high-priority pattern rules. Closes the post-mortem gap from
|
|
1217
|
+
* CVE-2026-46333 (ssh-keysign-pwn) where the toolkit shipped a CVE
|
|
1218
|
+
* matching the kernel-LPE-with-public-PoC shape but had no programmatic
|
|
1219
|
+
* way for an operator to ask "what just landed that needs attention?".
|
|
1220
|
+
*
|
|
1221
|
+
* Patterns are evaluated against every catalog entry; multiple patterns
|
|
1222
|
+
* may fire on the same entry (the report carries the list). The age
|
|
1223
|
+
* filter — pattern.fresh_days — bounds the "recently disclosed" patterns;
|
|
1224
|
+
* older entries already had attention.
|
|
1225
|
+
*
|
|
1226
|
+
* Output (JSON mode):
|
|
1227
|
+
* {
|
|
1228
|
+
* ok: true,
|
|
1229
|
+
* verb: "watchlist",
|
|
1230
|
+
* mode: "alerts",
|
|
1231
|
+
* generated_at: "...",
|
|
1232
|
+
* patterns_evaluated: 5,
|
|
1233
|
+
* entries_scanned: 39,
|
|
1234
|
+
* alerts: [
|
|
1235
|
+
* { cve_id, name, rwep_score, patterns: ["kernel_lpe_class", ...],
|
|
1236
|
+
* disclosed: "...", source_verified: "...", links: [...] }
|
|
1237
|
+
* ]
|
|
1238
|
+
* }
|
|
1239
|
+
*/
|
|
1240
|
+
function runWatchlistAlerts(rawArgs = []) {
|
|
1241
|
+
const fs = require('fs');
|
|
1242
|
+
const path = require('path');
|
|
1243
|
+
const jsonOut = rawArgs.includes('--json');
|
|
1244
|
+
const cvePath = path.join(__dirname, '..', 'data', 'cve-catalog.json');
|
|
1245
|
+
let catalog;
|
|
1246
|
+
try {
|
|
1247
|
+
catalog = JSON.parse(fs.readFileSync(cvePath, 'utf8'));
|
|
1248
|
+
} catch (err) {
|
|
1249
|
+
console.error(`[watchlist --alerts] cannot read ${cvePath}: ${err.message}`);
|
|
1250
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// Pattern definitions. Each pattern is a predicate against a single
|
|
1255
|
+
// catalog entry plus a label + severity. Patterns are intentionally
|
|
1256
|
+
// narrow — false-positive flood would defeat the alert purpose.
|
|
1257
|
+
const today = new Date();
|
|
1258
|
+
function daysSince(iso) {
|
|
1259
|
+
if (typeof iso !== 'string') return Infinity;
|
|
1260
|
+
const t = Date.parse(iso + 'T00:00:00Z');
|
|
1261
|
+
if (Number.isNaN(t)) return Infinity;
|
|
1262
|
+
return Math.floor((today - t) / (24 * 60 * 60 * 1000));
|
|
1263
|
+
}
|
|
1264
|
+
const PATTERNS = [
|
|
1265
|
+
{
|
|
1266
|
+
id: 'kernel_lpe_with_poc',
|
|
1267
|
+
severity: 'high',
|
|
1268
|
+
description: 'Linux kernel LPE class with public PoC. The CVE-2026-46333 (ssh-keysign-pwn) / CVE-2026-46300 (Fragnesia) / CVE-2026-31431 (Copy Fail) shape.',
|
|
1269
|
+
match: (e) =>
|
|
1270
|
+
e && typeof e.vector === 'string' &&
|
|
1271
|
+
/kernel|linux|ptrace|pidfd/i.test(`${e.name || ''} ${e.vector}`) &&
|
|
1272
|
+
e.poc_available === true &&
|
|
1273
|
+
(e.rwep_factors?.blast_radius || 0) >= 25,
|
|
1274
|
+
},
|
|
1275
|
+
{
|
|
1276
|
+
id: 'supply_chain_family',
|
|
1277
|
+
severity: 'high',
|
|
1278
|
+
description: 'Malicious package or framework family (npm / PyPI registry-pivot). The MAL- entries + Shai-Hulud class.',
|
|
1279
|
+
match: (e, id) =>
|
|
1280
|
+
id.startsWith('MAL-') ||
|
|
1281
|
+
(typeof e?.type === 'string' && /malicious|supply.chain|registry-pivot/i.test(e.type)),
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
id: 'ai_discovered_kev',
|
|
1285
|
+
severity: 'high',
|
|
1286
|
+
description: 'AI-discovered CVE that also appears on CISA KEV — the operational-reality intersection Hard Rule #7 calls out.',
|
|
1287
|
+
match: (e) => e?.ai_discovered === true && e?.cisa_kev === true,
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
id: 'active_exploitation_unpatched',
|
|
1291
|
+
severity: 'critical',
|
|
1292
|
+
description: 'Confirmed in-the-wild exploitation AND no patch available. Defensive-posture-only window.',
|
|
1293
|
+
match: (e) => e?.active_exploitation === 'confirmed' && e?.patch_available !== true,
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
id: 'recent_poc_no_kev_yet',
|
|
1297
|
+
severity: 'medium',
|
|
1298
|
+
description: 'CVE with public PoC verified within the last 14 days but not yet on KEV. The "exploitation expected; KEV catch-up pending" window.',
|
|
1299
|
+
match: (e) =>
|
|
1300
|
+
e?.poc_available === true &&
|
|
1301
|
+
e?.cisa_kev !== true &&
|
|
1302
|
+
daysSince(e?.source_verified) <= 14,
|
|
1303
|
+
fresh_only: true,
|
|
1304
|
+
},
|
|
1305
|
+
];
|
|
1306
|
+
|
|
1307
|
+
const alerts = [];
|
|
1308
|
+
let scanned = 0;
|
|
1309
|
+
for (const [id, entry] of Object.entries(catalog)) {
|
|
1310
|
+
if (id === '_meta') continue;
|
|
1311
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
1312
|
+
scanned++;
|
|
1313
|
+
const matched = [];
|
|
1314
|
+
for (const p of PATTERNS) {
|
|
1315
|
+
try {
|
|
1316
|
+
if (p.match(entry, id)) matched.push({ id: p.id, severity: p.severity });
|
|
1317
|
+
} catch { /* defensive: pattern matcher must not throw on malformed entries */ }
|
|
1318
|
+
}
|
|
1319
|
+
if (matched.length === 0) continue;
|
|
1320
|
+
alerts.push({
|
|
1321
|
+
cve_id: id,
|
|
1322
|
+
name: entry.name || null,
|
|
1323
|
+
rwep_score: entry.rwep_score ?? null,
|
|
1324
|
+
cisa_kev: entry.cisa_kev ?? null,
|
|
1325
|
+
poc_available: entry.poc_available ?? null,
|
|
1326
|
+
active_exploitation: entry.active_exploitation ?? null,
|
|
1327
|
+
patch_available: entry.patch_available ?? null,
|
|
1328
|
+
source_verified: entry.source_verified || null,
|
|
1329
|
+
patterns: matched,
|
|
1330
|
+
links: Array.isArray(entry.verification_sources) ? entry.verification_sources.slice(0, 3) : [],
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// Sort: critical-severity matches first, then high, then medium; within
|
|
1335
|
+
// each band, highest RWEP first; finally CVE-id ascending for stability.
|
|
1336
|
+
const severityWeight = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
1337
|
+
alerts.sort((a, b) => {
|
|
1338
|
+
const sa = Math.min(...a.patterns.map((p) => severityWeight[p.severity] ?? 9));
|
|
1339
|
+
const sb = Math.min(...b.patterns.map((p) => severityWeight[p.severity] ?? 9));
|
|
1340
|
+
if (sa !== sb) return sa - sb;
|
|
1341
|
+
const ra = a.rwep_score ?? -1;
|
|
1342
|
+
const rb = b.rwep_score ?? -1;
|
|
1343
|
+
if (ra !== rb) return rb - ra;
|
|
1344
|
+
return a.cve_id.localeCompare(b.cve_id);
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1347
|
+
if (jsonOut) {
|
|
1348
|
+
process.stdout.write(JSON.stringify({
|
|
1349
|
+
ok: true,
|
|
1350
|
+
verb: 'watchlist',
|
|
1351
|
+
mode: 'alerts',
|
|
1352
|
+
generated_at: today.toISOString(),
|
|
1353
|
+
patterns_evaluated: PATTERNS.length,
|
|
1354
|
+
entries_scanned: scanned,
|
|
1355
|
+
alert_count: alerts.length,
|
|
1356
|
+
alerts,
|
|
1357
|
+
}) + '\n');
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
console.log(`\nCVE-class Alerts — ${today.toISOString()}`);
|
|
1362
|
+
console.log(`Entries scanned: ${scanned} patterns evaluated: ${PATTERNS.length} alerts: ${alerts.length}\n`);
|
|
1363
|
+
if (alerts.length === 0) {
|
|
1364
|
+
console.log('No alert-pattern matches in current catalog.');
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
for (const a of alerts) {
|
|
1368
|
+
const labels = a.patterns.map((p) => `[${p.severity}] ${p.id}`).join(', ');
|
|
1369
|
+
console.log(`${a.cve_id} RWEP=${a.rwep_score ?? '?'} KEV=${a.cisa_kev ? 'Y' : 'N'} PoC=${a.poc_available ? 'Y' : 'N'} patch=${a.patch_available ? 'Y' : 'N'} active=${a.active_exploitation ?? '?'}`);
|
|
1370
|
+
console.log(` ${a.name || '(no name)'}`);
|
|
1371
|
+
console.log(` patterns: ${labels}`);
|
|
1372
|
+
if (a.links.length > 0) console.log(` ${a.links[0]}`);
|
|
1373
|
+
console.log('');
|
|
1374
|
+
}
|
|
1375
|
+
console.log('Run with --json to consume programmatically.');
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1195
1378
|
/**
|
|
1196
1379
|
* Cache-first variant of validateAllCves. For each catalog CVE, reads the
|
|
1197
1380
|
* NVD + EPSS payload from the prefetch cache (cacheDir/nvd/<id>.json +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamejs/exceptd-skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 10 catalogs, 34 jurisdictions, pre-computed indexes, Ed25519-signed.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-security",
|