@blamejs/exceptd-skills 0.14.28 → 0.15.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/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +14 -0
- package/README.md +5 -1
- package/bin/exceptd.js +0 -147
- package/data/_indexes/_meta.json +3 -3
- package/data/zeroday-lessons.json +1 -1
- package/lib/gap-detectors.js +8 -2
- package/lib/lint-skills.js +13 -2
- package/lib/playbook-runner.js +0 -2
- package/lib/validate-cve-catalog.js +35 -12
- package/manifest.json +44 -44
- package/orchestrator/index.js +49 -5
- package/package.json +2 -2
- package/sbom.cdx.json +37 -37
- package/scripts/check-catalog-gap-budget.js +5 -1
- package/scripts/check-test-coverage.js +9 -0
- package/scripts/predeploy.js +8 -4
package/ARCHITECTURE.md
CHANGED
|
@@ -176,7 +176,7 @@ Tracks PoC status, weaponization stage, and AI-assist factor per CVE. Updated wh
|
|
|
176
176
|
|
|
177
177
|
### `data/cwe-catalog.json`
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
173 CWE entries pinned to **CWE v4.20**. Covers the Top 25 Most Dangerous Software Weaknesses (2024 release) plus AI- and supply-chain-relevant weakness classes (prompt-injection-as-trust-boundary failure, training data integrity, dependency confusion, untrusted artifact ingestion). Each entry records root-cause description, common consequences, mitigation patterns, and the CVEs in `cve-catalog.json` that instantiate the weakness. Skills cite CWE IDs in `cwe_refs` to anchor a finding to a stable weakness taxonomy rather than to a single CVE; the CWE provides the durable root-cause lens that survives across exploit generations.
|
|
180
180
|
|
|
181
181
|
`_meta.cwe_version` pins the version; on a CWE release, audit IDs for renames or deprecations, bump `last_threat_review` on affected skills, and update `_meta`.
|
|
182
182
|
|
|
@@ -188,7 +188,7 @@ Tracks PoC status, weaponization stage, and AI-assist factor per CVE. Updated wh
|
|
|
188
188
|
|
|
189
189
|
### `data/rfc-references.json`
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
8888 IETF RFC / Internet-Draft references — the full RFC index, including obsoleted and Historic entries so that a superseded RFC still resolves offline. Coverage spans the security-relevant standards skills depend on — authentication and authorization (OAuth 2.0 Security BCP RFC 9700, JWT BCP, FIDO/WebAuthn-related drafts), cryptography (TLS 1.3 RFC 8446, hybrid PQC drafts), disclosure (security.txt RFC 9116) — alongside the rest of the published RFC series. Each entry tracks: title, status (Internet Standard / Proposed Standard / Best Current Practice / Internet-Draft / Historic), errata count, replaces / replaced-by chains, IESG / IRTF stream, and a `last_verified` date. Skills cite RFC IDs in `rfc_refs`. Per Hard Rule #12, RFC references are version-pinned: when an RFC is obsoleted or a draft is published as an RFC, the catalog entry's `replaced_by` field is updated, `last_verified` is refreshed, and affected skills bump `last_threat_review`. Frameworks lag RFCs; RFCs lag attacker innovation — this catalog makes that middle layer auditable.
|
|
192
192
|
|
|
193
193
|
### `data/dlp-controls.json`
|
|
194
194
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.0 — 2026-05-28
|
|
4
|
+
|
|
5
|
+
Validation and gate hardening. Several catalog and skill integrity checks that had been deferred as non-blocking warnings now hard-fail the predeploy gate, and two latent gate weaknesses are closed.
|
|
6
|
+
|
|
7
|
+
Catalog, skill, and playbook validation now runs in strict mode in the release gate: an unresolved cross-catalog reference (a CVE citing a CWE / ATT&CK / ATLAS / D3FEND id or framework control that doesn't exist), a CVSS vector that doesn't match a known `CVSS:(2.0|3.0|3.1|4.0)/` prefix, a `cisa_kev: true` entry missing its listing date, a public-PoC entry missing IOCs (Hard Rule #14), an incomplete skill body, or playbook enum/symmetry drift now block a release instead of scrolling past as warnings. Auto-imported drafts remain exempt.
|
|
8
|
+
|
|
9
|
+
The `_meta.entry_count` drift guard now covers every catalog that declares the field, not only framework-control-gaps — closing the hole that had let the zero-day-lessons counter drift to 68 while the file held 422 (now corrected). The RWEP scoring invariants (Shape A/B/mixed factor detection and recomputed-vs-stored divergence) are now enforced against the shipped catalog by a test gate, where previously they ran only against synthetic fixtures.
|
|
10
|
+
|
|
11
|
+
The `temporal-staleness` catalog-gap class now counts curated entries only. A CISA KEV due-date passing by calendar drift on the un-curated bulk-import backlog is expected and no longer accrues against the budget — which had crept to one slot of headroom and would have failed a future release with no code change. The budget is retightened to reflect the curated-only count.
|
|
12
|
+
|
|
13
|
+
CLI surface cleanup. The dead handlers behind the verbs removed in 0.13.0 (`govern`, `direct`, `look`, `ingest`) are gone, and those verbs are no longer surfaced as "did you mean" suggestions. Unknown-flag rejection now covers `report`, `watch`, `framework-gap`, and `skill` (an unrecognized flag on these hard-fails with a structured envelope, matching the rest of the surface). The local-only `scan`, `dispatch`, and `currency` verbs now accept `--air-gap` / `--offline` / `--no-network` instead of rejecting the tool's own global flags. The `--format summary` evidence bundle no longer carries always-null `feeds_into` / `jurisdiction_clocks_active` keys (the populated values come from the close phase).
|
|
14
|
+
|
|
15
|
+
Documentation: corrected catalog counts (427 CVEs, 173 CWEs, the full 8888-entry RFC index) and documented the `report` verb in the CLI reference.
|
|
16
|
+
|
|
3
17
|
## 0.14.28 — 2026-05-28
|
|
4
18
|
|
|
5
19
|
Catalog expansion — 2025 actively-exploited perimeter and file-transfer RCE cluster. Four CISA KEV-listed, ransomware-associated entries are now fully curated with RWEP scoring, IOCs, zero-day lessons, and reverse-referenced CWE/ATT&CK/framework mappings:
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ This platform surfaces what is actually happening right now. Every skill explici
|
|
|
30
30
|
|
|
31
31
|
## Status
|
|
32
32
|
|
|
33
|
-
Pre-1.0. Latest release lives on [GitHub Releases](https://github.com/blamejs/exceptd-skills/releases) and on npm as [`@blamejs/exceptd-skills`](https://www.npmjs.com/package/@blamejs/exceptd-skills) with signed npm provenance attestation and Ed25519-signed skill bodies. The package ships 42 skills across kernel LPE, MCP supply chain, AI-as-C2, prompt injection, post-quantum crypto, SBOM integrity, identity-incident response, and 35 other AI/security domains, plus
|
|
33
|
+
Pre-1.0. Latest release lives on [GitHub Releases](https://github.com/blamejs/exceptd-skills/releases) and on npm as [`@blamejs/exceptd-skills`](https://www.npmjs.com/package/@blamejs/exceptd-skills) with signed npm provenance attestation and Ed25519-signed skill bodies. The package ships 42 skills across kernel LPE, MCP supply chain, AI-as-C2, prompt injection, post-quantum crypto, SBOM integrity, identity-incident response, and 35 other AI/security domains, plus 11 intelligence catalogs (CVE / ATLAS / ATT&CK / CWE / D3FEND / DLP / RFC / framework gaps / global frameworks / zero-day lessons / exploit availability) covering 35 jurisdictions — the CVE catalog has grown past 400 entries, its size anchored by a v0.13.17 CISA KEV bulk-intake of `dateAdded >= 2024-01-01` actively-exploited vulnerabilities that took it from 68 to 312 in a single pass. 24 investigation playbooks (kernel, MCP, AI-API, framework, SBOM, runtime, hardening, secrets, cred-stores, containers, crypto, plus `webhook-callback-abuse`, `cicd-pipeline-compromise`, `identity-sso-compromise`, `llm-tool-use-exfil`, `post-quantum-migration`, `ai-discovered-cve-triage`, `supply-chain-recovery`, `citation-hygiene`, and more), a CLI for discovery and seven-phase investigation runs (`govern → direct → look → detect → analyze → validate → close`), and a nightly auto-refresh job that pulls KEV / NVD / EPSS / GHSA / OSV / IETF deltas plus 15 primary-source advisory + research-blog + tech-press feeds (Qualys TRU, Red Hat RHSA, Ubuntu USN, ZDI, kernel.org, oss-security, JFrog, CISA, Microsoft Security Blog, Sysdig, Trail of Bits, Embrace the Red, BleepingComputer security, The Hacker News, and a GitLab activity-feed tracker for the Nightmare-Eclipse researcher handle that anchors NEW-CTRL-073) into auto-PRs for editorial review. v0.13.17 also ships `lib/cve-regression-watcher.js` (NEW-CTRL-074) — a complementary detection method that surfaces poller-diff historical-CVE references as candidate silent-regression cases, the class anchored by MiniPlasma (a 2026 PoC drop that re-broke CVE-2020-17103 without any new ID being assigned).
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
@@ -488,6 +488,10 @@ exceptd skill <name> Show context for one skill.
|
|
|
488
488
|
exceptd framework-gap <FW> <ref> One framework + one CVE/scenario, JSON
|
|
489
489
|
or human. (Operates outside the seven-
|
|
490
490
|
phase contract for ad-hoc gap analysis.)
|
|
491
|
+
exceptd report [executive] Structured posture report. Bare `report`
|
|
492
|
+
emits the full posture; the optional
|
|
493
|
+
`executive` argument emits the
|
|
494
|
+
executive-summary view.
|
|
491
495
|
exceptd path Absolute path to the installed package.
|
|
492
496
|
exceptd version Package version.
|
|
493
497
|
exceptd help This help.
|
package/bin/exceptd.js
CHANGED
|
@@ -159,12 +159,7 @@ const COMMANDS = {
|
|
|
159
159
|
cve: () => path.join(PKG_ROOT, "lib", "cve-cli.js"),
|
|
160
160
|
rfc: () => path.join(PKG_ROOT, "lib", "rfc-cli.js"),
|
|
161
161
|
// Seven-phase playbook verbs — handled in-process via lib/playbook-runner.js.
|
|
162
|
-
plan: null,
|
|
163
|
-
govern: null,
|
|
164
|
-
direct: null,
|
|
165
|
-
look: null,
|
|
166
162
|
run: null,
|
|
167
|
-
ingest: null,
|
|
168
163
|
reattest: null,
|
|
169
164
|
};
|
|
170
165
|
|
|
@@ -1724,12 +1719,7 @@ function dispatchPlaybook(cmd, argv) {
|
|
|
1724
1719
|
|
|
1725
1720
|
try {
|
|
1726
1721
|
switch (cmd) {
|
|
1727
|
-
case "plan": return cmdPlan(runner, args, runOpts, pretty);
|
|
1728
|
-
case "govern": return cmdGovern(runner, args, runOpts, pretty);
|
|
1729
|
-
case "direct": return cmdDirect(runner, args, pretty);
|
|
1730
|
-
case "look": return cmdLook(runner, args, runOpts, pretty);
|
|
1731
1722
|
case "run": return cmdRun(runner, args, runOpts, pretty);
|
|
1732
|
-
case "ingest": return cmdIngest(runner, args, runOpts, pretty);
|
|
1733
1723
|
case "reattest": return cmdReattest(runner, args, runOpts, pretty);
|
|
1734
1724
|
case "list-attestations": return cmdListAttestations(runner, args, runOpts, pretty);
|
|
1735
1725
|
case "attest": return cmdAttest(runner, args, runOpts, pretty);
|
|
@@ -3179,36 +3169,6 @@ function detectScopes() {
|
|
|
3179
3169
|
return detected.length ? detected : ["cross-cutting"];
|
|
3180
3170
|
}
|
|
3181
3171
|
|
|
3182
|
-
function cmdGovern(runner, args, runOpts, pretty) {
|
|
3183
|
-
const playbookId = args._[0];
|
|
3184
|
-
if (!playbookId) return emitError("govern: missing <playbookId> positional argument.", null, pretty);
|
|
3185
|
-
if (refuseInvalidPlaybookId("govern", playbookId, pretty)) return;
|
|
3186
|
-
const pb = runner.loadPlaybook(playbookId);
|
|
3187
|
-
const directiveId = args.directive || (pb.directives[0] && pb.directives[0].id);
|
|
3188
|
-
if (!directiveId) return refuseNoDirectives("govern", playbookId, pretty);
|
|
3189
|
-
emit(runner.govern(playbookId, directiveId, runOpts), pretty);
|
|
3190
|
-
}
|
|
3191
|
-
|
|
3192
|
-
function cmdDirect(runner, args, pretty) {
|
|
3193
|
-
const playbookId = args._[0];
|
|
3194
|
-
if (!playbookId) return emitError("direct: missing <playbookId> positional argument.", null, pretty);
|
|
3195
|
-
if (refuseInvalidPlaybookId("direct", playbookId, pretty)) return;
|
|
3196
|
-
const pb = runner.loadPlaybook(playbookId);
|
|
3197
|
-
const directiveId = args.directive || (pb.directives[0] && pb.directives[0].id);
|
|
3198
|
-
if (!directiveId) return refuseNoDirectives("direct", playbookId, pretty);
|
|
3199
|
-
emit(runner.direct(playbookId, directiveId), pretty);
|
|
3200
|
-
}
|
|
3201
|
-
|
|
3202
|
-
function cmdLook(runner, args, runOpts, pretty) {
|
|
3203
|
-
const playbookId = args._[0];
|
|
3204
|
-
if (!playbookId) return emitError("look: missing <playbookId> positional argument.", null, pretty);
|
|
3205
|
-
if (refuseInvalidPlaybookId("look", playbookId, pretty)) return;
|
|
3206
|
-
const pb = runner.loadPlaybook(playbookId);
|
|
3207
|
-
const directiveId = args.directive || (pb.directives[0] && pb.directives[0].id);
|
|
3208
|
-
if (!directiveId) return refuseNoDirectives("look", playbookId, pretty);
|
|
3209
|
-
emit(runner.look(playbookId, directiveId, runOpts), pretty);
|
|
3210
|
-
}
|
|
3211
|
-
|
|
3212
3172
|
function cmdRun(runner, args, runOpts, pretty) {
|
|
3213
3173
|
const positional = args._[0];
|
|
3214
3174
|
|
|
@@ -4351,113 +4311,6 @@ function cmdRunMulti(runner, ids, args, runOpts, pretty, meta) {
|
|
|
4351
4311
|
if (anyBlocked) { process.exitCode = EXIT_CODES.GENERIC_FAILURE; return; }
|
|
4352
4312
|
}
|
|
4353
4313
|
|
|
4354
|
-
function cmdIngest(runner, args, runOpts, pretty) {
|
|
4355
|
-
// `ingest` matches the AGENTS.md ingest contract. The submission JSON may
|
|
4356
|
-
// carry playbook_id + directive_id; --domain/--directive flags override.
|
|
4357
|
-
let submission = {};
|
|
4358
|
-
// Auto-detect piped stdin (parity with cmdRun) so
|
|
4359
|
-
// `echo '{...}' | exceptd ingest` reads the routing JSON instead of
|
|
4360
|
-
// failing with "no playbook resolved" because args.evidence stays
|
|
4361
|
-
// undefined.
|
|
4362
|
-
// Route stdin auto-detection through hasReadableStdin() (see cmdRun for
|
|
4363
|
-
// rationale). Wrapped-stdin test harnesses (Mocha/Jest, Docker
|
|
4364
|
-
// stdin-passthrough) would otherwise block here forever on the
|
|
4365
|
-
// readFileSync(0) call when isTTY === undefined.
|
|
4366
|
-
if (!args.evidence && hasReadableStdin()) {
|
|
4367
|
-
args.evidence = "-";
|
|
4368
|
-
}
|
|
4369
|
-
if (args.evidence) {
|
|
4370
|
-
try {
|
|
4371
|
-
submission = readEvidence(args.evidence);
|
|
4372
|
-
} catch (e) {
|
|
4373
|
-
return emitError(`ingest: failed to read evidence: ${e.message}`, { evidence: args.evidence }, pretty);
|
|
4374
|
-
}
|
|
4375
|
-
}
|
|
4376
|
-
const playbookId = args.domain || submission.playbook_id || submission.domain;
|
|
4377
|
-
if (!playbookId) return emitError("ingest: no playbook resolved — pass --domain <id> or include playbook_id in evidence JSON.", null, pretty);
|
|
4378
|
-
if (refuseInvalidPlaybookId("ingest", playbookId, pretty)) return;
|
|
4379
|
-
const pb = runner.loadPlaybook(playbookId);
|
|
4380
|
-
const directiveId = args.directive
|
|
4381
|
-
|| submission.directive_id
|
|
4382
|
-
|| (pb.directives[0] && pb.directives[0].id);
|
|
4383
|
-
if (!directiveId) return refuseNoDirectives("ingest", playbookId, pretty);
|
|
4384
|
-
|
|
4385
|
-
// Strip the routing keys so the runner only sees the contract shape it
|
|
4386
|
-
// expects. precondition_checks travel on the submission (not lifted into
|
|
4387
|
-
// runOpts) so run()'s mergedPCs derives them with correct "submission"
|
|
4388
|
-
// provenance — the same path cmdRun uses.
|
|
4389
|
-
const cleanedSubmission = {
|
|
4390
|
-
artifacts: submission.artifacts || {},
|
|
4391
|
-
signal_overrides: submission.signal_overrides || {},
|
|
4392
|
-
signals: submission.signals || {},
|
|
4393
|
-
...(submission.precondition_checks ? { precondition_checks: submission.precondition_checks } : {}),
|
|
4394
|
-
};
|
|
4395
|
-
|
|
4396
|
-
const result = runner.run(playbookId, directiveId, cleanedSubmission, runOpts);
|
|
4397
|
-
|
|
4398
|
-
// v0.12.8: route ingest's attestation persistence through persistAttestation
|
|
4399
|
-
// — the same path cmdRun + cmdRunMulti use — so the session-id collision
|
|
4400
|
-
// refusal AND the Ed25519 sidecar signing both apply. Pre-v0.12.8 ingest
|
|
4401
|
-
// had its own inline writeFileSync with neither check, meaning two ingest
|
|
4402
|
-
// calls with the same session-id silently clobbered the audit trail and no
|
|
4403
|
-
// .sig sidecar was written.
|
|
4404
|
-
if (result && result.ok && result.session_id) {
|
|
4405
|
-
// Mirror cmdRun / cmdRunMulti: gate operator_consent persistence on
|
|
4406
|
-
// classification === 'detected'. --ack is meaningful only when a
|
|
4407
|
-
// jurisdiction clock is at stake; persisting consent on a
|
|
4408
|
-
// not-detected ingest forges audit-trail consent for a clock that
|
|
4409
|
-
// never started.
|
|
4410
|
-
const ingestClassification = result.phases && result.phases.detect ? result.phases.detect.classification : null;
|
|
4411
|
-
const ingestConsentApplies = ingestClassification === "detected";
|
|
4412
|
-
if (runOpts.operator_consent && !ingestConsentApplies) {
|
|
4413
|
-
result.ack = true;
|
|
4414
|
-
result.ack_applied = false;
|
|
4415
|
-
result.ack_skipped_reason = `classification=${ingestClassification || "unknown"}; consent only persisted when classification=detected (jurisdiction clock at stake).`;
|
|
4416
|
-
}
|
|
4417
|
-
const persisted = persistAttestation({
|
|
4418
|
-
sessionId: result.session_id,
|
|
4419
|
-
playbookId: result.playbook_id,
|
|
4420
|
-
directiveId: result.directive_id,
|
|
4421
|
-
evidenceHash: result.evidence_hash,
|
|
4422
|
-
operator: runOpts.operator,
|
|
4423
|
-
operatorConsent: ingestConsentApplies ? runOpts.operator_consent : null,
|
|
4424
|
-
submission: cleanedSubmission,
|
|
4425
|
-
runOpts,
|
|
4426
|
-
forceOverwrite: !!args["force-overwrite"],
|
|
4427
|
-
filename: "attestation.json",
|
|
4428
|
-
});
|
|
4429
|
-
if (!persisted.ok) {
|
|
4430
|
-
// Route every persist-failure shape through emitError so the
|
|
4431
|
-
// emit() ok:false → exitCode contract applies uniformly. Three
|
|
4432
|
-
// exit classes: LOCK_CONTENTION (transient), STORAGE_EXHAUSTED
|
|
4433
|
-
// (infra), SESSION_ID_COLLISION (operator decision).
|
|
4434
|
-
const ctx = { session_id: result.session_id, existing_path: persisted.existingPath };
|
|
4435
|
-
if (persisted.lock_contention) {
|
|
4436
|
-
ctx.lock_contention = true;
|
|
4437
|
-
ctx.exit_code = EXIT_CODES.LOCK_CONTENTION;
|
|
4438
|
-
}
|
|
4439
|
-
if (persisted.storage_exhausted) {
|
|
4440
|
-
ctx.storage_exhausted = true;
|
|
4441
|
-
ctx.exit_code = EXIT_CODES.STORAGE_EXHAUSTED;
|
|
4442
|
-
}
|
|
4443
|
-
emitError(persisted.error, ctx, pretty);
|
|
4444
|
-
if (persisted.lock_contention) process.exitCode = EXIT_CODES.LOCK_CONTENTION;
|
|
4445
|
-
else if (persisted.storage_exhausted) process.exitCode = EXIT_CODES.STORAGE_EXHAUSTED;
|
|
4446
|
-
else process.exitCode = EXIT_CODES.SESSION_ID_COLLISION;
|
|
4447
|
-
return;
|
|
4448
|
-
}
|
|
4449
|
-
if (persisted.prior_session_id) {
|
|
4450
|
-
result.attestation_persist = { ok: true, prior_session_id: persisted.prior_session_id, overwrote_at: persisted.overwrote_at };
|
|
4451
|
-
}
|
|
4452
|
-
}
|
|
4453
|
-
|
|
4454
|
-
if (result && result.ok === false) {
|
|
4455
|
-
emit(result, pretty);
|
|
4456
|
-
return;
|
|
4457
|
-
}
|
|
4458
|
-
emit(result, pretty);
|
|
4459
|
-
}
|
|
4460
|
-
|
|
4461
4314
|
/**
|
|
4462
4315
|
* Resolve the attestation root for a given run. Resolution order (most-specific
|
|
4463
4316
|
* first):
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-05-
|
|
3
|
+
"generated_at": "2026-05-29T00:05:09.817Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
5
|
"source_count": 54,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
7
|
+
"manifest.json": "2075a9d45002fe52785b0aa326d3bf6531f6b9b611c4c8ae166b413ca5f06b8c",
|
|
8
8
|
"data/atlas-ttps.json": "878b4a08bb73c8d20396d85cf433a88f2bc5e7a8cbf7f6ab773ce7ede0a11251",
|
|
9
9
|
"data/attack-techniques.json": "57b8a1b4e1c3f524a76b4bded09b3082b36b783db3df116f863892072e0f65e9",
|
|
10
10
|
"data/cve-catalog.json": "5849b48dd5489ba6d10cbd3b0b25c9d8412e3932e0ae2304364ec95cf254ec97",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"data/framework-control-gaps.json": "29e7b6aa841ddf2530ca5971bdb60d7a715684b2f6264141ad49f0de9a039d78",
|
|
16
16
|
"data/global-frameworks.json": "9ba563a85f7f8d6c3c957de64945e20925a89d0ed6ea6fc561cf093811acf558",
|
|
17
17
|
"data/rfc-references.json": "b21d03b948c41bc8a854e2f057948ecf844bd8c105848aeb141d1eadf8192c31",
|
|
18
|
-
"data/zeroday-lessons.json": "
|
|
18
|
+
"data/zeroday-lessons.json": "e8202ffa99ed7c7d40b89ee5eedbea33839048cbff482e09329292e6700a5157",
|
|
19
19
|
"skills/kernel-lpe-triage/skill.md": "08b3e9815ba481c57c80f5fc0ccbf5bb7cbb41f570c235ba6ff9596b8c07354d",
|
|
20
20
|
"skills/ai-attack-surface/skill.md": "c4c1eb22a38ca7a959b5725222bab8fbd4f4044a548a93f3e288e6f698334b72",
|
|
21
21
|
"skills/mcp-agent-trust/skill.md": "89ac89084391d2341b6513fefb1be2d36b93de1c130f057696219c1c59440f13",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"rebuild_after_days": 365,
|
|
18
18
|
"note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
|
|
19
19
|
},
|
|
20
|
-
"entry_count":
|
|
20
|
+
"entry_count": 422
|
|
21
21
|
},
|
|
22
22
|
"CVE-2026-31431": {
|
|
23
23
|
"name": "Copy Fail",
|
package/lib/gap-detectors.js
CHANGED
|
@@ -158,8 +158,14 @@ function temporalStalenessFindings(loaded, opts = {}) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// CISA KEV due-date passed without remediation status — surfaces
|
|
161
|
-
// operationally-stale entries the operator should re-verify.
|
|
162
|
-
|
|
161
|
+
// operationally-stale CURATED entries the operator should re-verify.
|
|
162
|
+
// Auto-imported drafts are excluded: a KEV due-date passing with wall-clock
|
|
163
|
+
// time on the un-curated bulk-import backlog is expected (and grows the
|
|
164
|
+
// count purely by calendar drift, which would mechanically breach the
|
|
165
|
+
// budget gate on a no-op release). The finding is actionable only once the
|
|
166
|
+
// entry is curated, so it is scoped to non-draft entries.
|
|
167
|
+
const isDraft = e._auto_imported === true || e._draft === true;
|
|
168
|
+
if (!isDraft && e.cisa_kev === true && typeof e.cisa_kev_due_date === "string") {
|
|
163
169
|
const sinceDue = daysSince(e.cisa_kev_due_date, now);
|
|
164
170
|
if (sinceDue !== null && sinceDue > 0) {
|
|
165
171
|
out.push({ class: "temporal-staleness", catalog: "cve-catalog", id,
|
package/lib/lint-skills.js
CHANGED
|
@@ -119,7 +119,7 @@ const KEBAB_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
|
119
119
|
const JSON_FILENAME_RE = /^[A-Za-z0-9._-]+\.json$/;
|
|
120
120
|
|
|
121
121
|
function parseArgs(argv) {
|
|
122
|
-
const opts = { skill: null, quiet: false };
|
|
122
|
+
const opts = { skill: null, quiet: false, strict: false };
|
|
123
123
|
for (let i = 2; i < argv.length; i++) {
|
|
124
124
|
const a = argv[i];
|
|
125
125
|
if (a === '--skill') {
|
|
@@ -128,6 +128,11 @@ function parseArgs(argv) {
|
|
|
128
128
|
opts.skill = a.slice('--skill='.length);
|
|
129
129
|
} else if (a === '--quiet' || a === '-q') {
|
|
130
130
|
opts.quiet = true;
|
|
131
|
+
} else if (a === '--strict') {
|
|
132
|
+
// Promote warnings (header-only sections, unresolved draft refs,
|
|
133
|
+
// playbook air-gap gaps) to release-blocking failures. Used by the
|
|
134
|
+
// predeploy gate so a warned regression cannot scroll past.
|
|
135
|
+
opts.strict = true;
|
|
131
136
|
} else if (a === '--help' || a === '-h') {
|
|
132
137
|
printHelp();
|
|
133
138
|
process.exit(0);
|
|
@@ -856,7 +861,13 @@ function main() {
|
|
|
856
861
|
console.log(
|
|
857
862
|
`\n${passed}/${total} skills passed${warnSummary}${failed ? `, ${failed} failed` : ''}${orphanSummary}${airGapSummary}.`,
|
|
858
863
|
);
|
|
859
|
-
|
|
864
|
+
// --strict treats any warning (per-skill or playbook air-gap) as a
|
|
865
|
+
// release-blocking failure so a warned regression cannot ship silently.
|
|
866
|
+
const strictFail = opts.strict && (warned > 0 || (airGapWarnings && airGapWarnings.length > 0));
|
|
867
|
+
if (strictFail) {
|
|
868
|
+
console.log(`[lint-skills] --strict: ${warned + (airGapWarnings ? airGapWarnings.length : 0)} warning(s) treated as failures.`);
|
|
869
|
+
}
|
|
870
|
+
process.exit(failed === 0 && orphans.length === 0 && !strictFail ? 0 : 1);
|
|
860
871
|
}
|
|
861
872
|
|
|
862
873
|
// Export the minimal frontmatter parser for downstream consumers
|
package/lib/playbook-runner.js
CHANGED
|
@@ -2912,8 +2912,6 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
|
|
|
2912
2912
|
rwep_adjusted: analyze.rwep?.adjusted || 0,
|
|
2913
2913
|
rwep_threshold_escalate: analyze.rwep?.threshold?.escalate || null,
|
|
2914
2914
|
blast_radius_score: analyze.blast_radius_score || 0,
|
|
2915
|
-
feeds_into: null, // populated by close()
|
|
2916
|
-
jurisdiction_clocks_active: null, // populated by close()
|
|
2917
2915
|
remediation_recommended: validate.selected_remediation?.id || null,
|
|
2918
2916
|
}
|
|
2919
2917
|
};
|
|
@@ -272,6 +272,21 @@ function additionalChecks(key, entry, ctx) {
|
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
// V5 — KEV status must carry its date (AGENTS.md Hard Rule #1: a KEV flag
|
|
276
|
+
// without a date is an incomplete threat-intel claim; RWEP scoring and the
|
|
277
|
+
// jurisdiction-clock SLAs key off the KEV listing date). cisa_kev=true with a
|
|
278
|
+
// null/missing/invalid cisa_kev_date is flagged; promoted to a hard error
|
|
279
|
+
// under --strict (predeploy). 0 violations in the shipped catalog today.
|
|
280
|
+
if (entry.cisa_kev === true) {
|
|
281
|
+
const d = entry.cisa_kev_date;
|
|
282
|
+
const dateOk = typeof d === 'string' && isUsableDate(d).ok;
|
|
283
|
+
if (!dateOk) {
|
|
284
|
+
warnings.push(
|
|
285
|
+
`${key}: cisa_kev=true but cisa_kev_date is ${JSON.stringify(d)} (KEV status must carry a valid listing date — Hard Rule #1)`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
275
290
|
// V4 — Impossible-date guard.
|
|
276
291
|
for (const f of DATE_FIELDS) {
|
|
277
292
|
const v = entry[f];
|
|
@@ -352,18 +367,26 @@ function main() {
|
|
|
352
367
|
}
|
|
353
368
|
}
|
|
354
369
|
|
|
355
|
-
// Guard
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
370
|
+
// Guard hand-maintained _meta.entry_count fields against silent drift. The
|
|
371
|
+
// framework-control-gaps counter once declared 184 while the file held 192
|
|
372
|
+
// and nothing caught it; the zeroday-lessons counter drifted to 68 while the
|
|
373
|
+
// file held 422 because only framework-control-gaps was gated. This now
|
|
374
|
+
// checks EVERY loaded catalog that declares a numeric _meta.entry_count, so a
|
|
375
|
+
// new catalog with the field is covered automatically.
|
|
376
|
+
const ENTRY_COUNT_CATALOGS = [
|
|
377
|
+
{ name: 'framework-control-gaps', catalog: frameworks },
|
|
378
|
+
{ name: 'zeroday-lessons', catalog: lessons },
|
|
379
|
+
];
|
|
380
|
+
for (const { name, catalog: cat } of ENTRY_COUNT_CATALOGS) {
|
|
381
|
+
if (cat && cat._meta && typeof cat._meta.entry_count === 'number') {
|
|
382
|
+
const actual = Object.keys(cat).filter((k) => !k.startsWith('_')).length;
|
|
383
|
+
if (cat._meta.entry_count !== actual) {
|
|
384
|
+
process.stderr.write(
|
|
385
|
+
`[validate-cve-catalog] FAIL: ${name} _meta.entry_count (${cat._meta.entry_count}) ` +
|
|
386
|
+
`!= actual entry count (${actual}). Update _meta.entry_count to ${actual}.\n`,
|
|
387
|
+
);
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
367
390
|
}
|
|
368
391
|
}
|
|
369
392
|
|
package/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exceptd-security",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation",
|
|
5
5
|
"homepage": "https://exceptd.com",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
],
|
|
54
54
|
"last_threat_review": "2026-05-15",
|
|
55
55
|
"signature": "lXhZgoIrrVloO3XaTvo/43AxZn4mwErstd7DR0O/oVhD3AOGODM4HqrageYEou9WKOdMEGP5mJNTjJsXdP5NDA==",
|
|
56
|
-
"signed_at": "2026-05-
|
|
56
|
+
"signed_at": "2026-05-28T23:31:33.979Z",
|
|
57
57
|
"cwe_refs": [
|
|
58
58
|
"CWE-125",
|
|
59
59
|
"CWE-362",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
],
|
|
124
124
|
"last_threat_review": "2026-05-17",
|
|
125
125
|
"signature": "ztSKk/zFMFbT12qRcEeBKpydBn7fTT86KxMmor0DTCoKQWk5fJ0fSInfP1XMSB6rFk4/SuSjKVxQRMKVJ5a+Cg==",
|
|
126
|
-
"signed_at": "2026-05-
|
|
126
|
+
"signed_at": "2026-05-28T23:31:33.981Z",
|
|
127
127
|
"cwe_refs": [
|
|
128
128
|
"CWE-1039",
|
|
129
129
|
"CWE-1426",
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
],
|
|
197
197
|
"last_threat_review": "2026-05-17",
|
|
198
198
|
"signature": "K6QdPHNK5c4K5QFjrW0QsUhjp71D7SOisSoulwPNSvKRdi2rY+yg0kdckijBMkLMsVPyUvcC9giu93mKJ1OZDg==",
|
|
199
|
-
"signed_at": "2026-05-
|
|
199
|
+
"signed_at": "2026-05-28T23:31:33.981Z",
|
|
200
200
|
"cwe_refs": [
|
|
201
201
|
"CWE-22",
|
|
202
202
|
"CWE-345",
|
|
@@ -248,7 +248,7 @@
|
|
|
248
248
|
"framework_gaps": [],
|
|
249
249
|
"last_threat_review": "2026-05-22",
|
|
250
250
|
"signature": "zbr8CbOe2rIPuxj2bCl8mVc3YgLsRHnx3/UU7MmyMLNg4c13q6LEBglC1A7cjhfblfHsX/1rxNLaukDzc65lAw==",
|
|
251
|
-
"signed_at": "2026-05-
|
|
251
|
+
"signed_at": "2026-05-28T23:31:33.981Z"
|
|
252
252
|
},
|
|
253
253
|
{
|
|
254
254
|
"name": "compliance-theater",
|
|
@@ -279,7 +279,7 @@
|
|
|
279
279
|
],
|
|
280
280
|
"last_threat_review": "2026-05-22",
|
|
281
281
|
"signature": "F2Shxae0ua0gPtvwzTRVzzHaIgJcFDRT3/akLUAZ4aaMQhkleKkcTaTpkjp+pTVEdPfLeLGNCeAOMs+whVYOBg==",
|
|
282
|
-
"signed_at": "2026-05-
|
|
282
|
+
"signed_at": "2026-05-28T23:31:33.982Z"
|
|
283
283
|
},
|
|
284
284
|
{
|
|
285
285
|
"name": "exploit-scoring",
|
|
@@ -308,7 +308,7 @@
|
|
|
308
308
|
],
|
|
309
309
|
"last_threat_review": "2026-05-18",
|
|
310
310
|
"signature": "NA1hoQycvQhSUoG5rwlXX0mOVmGxoXRVezkELGEA2nZOdGis4gXkHT3O6Sfw7zxE4JuMrsCb65TEeOWk9WEPDg==",
|
|
311
|
-
"signed_at": "2026-05-
|
|
311
|
+
"signed_at": "2026-05-28T23:31:33.982Z"
|
|
312
312
|
},
|
|
313
313
|
{
|
|
314
314
|
"name": "rag-pipeline-security",
|
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
],
|
|
346
346
|
"last_threat_review": "2026-05-22",
|
|
347
347
|
"signature": "W3pS8lnaCP96TQzsJpG5d5yv5IwgaQyS4Z2Ctcz5BOJf6LbajSIgeDgTZ4f4Bhr5m4E7KsgWGjZS4x7Fwd33BQ==",
|
|
348
|
-
"signed_at": "2026-05-
|
|
348
|
+
"signed_at": "2026-05-28T23:31:33.982Z",
|
|
349
349
|
"cwe_refs": [
|
|
350
350
|
"CWE-1395",
|
|
351
351
|
"CWE-1426"
|
|
@@ -405,7 +405,7 @@
|
|
|
405
405
|
],
|
|
406
406
|
"last_threat_review": "2026-05-17",
|
|
407
407
|
"signature": "/WDGygh1Ck4yWlBWDGtEUVCqKB8d+UaJXoAoBXujtt+GAl8JbMNpaN1TvI0WkEltQ9dTxaAzSn20/eVDqv8iDQ==",
|
|
408
|
-
"signed_at": "2026-05-
|
|
408
|
+
"signed_at": "2026-05-28T23:31:33.983Z",
|
|
409
409
|
"d3fend_refs": [
|
|
410
410
|
"D3-CA",
|
|
411
411
|
"D3-CSPP",
|
|
@@ -440,7 +440,7 @@
|
|
|
440
440
|
"framework_gaps": [],
|
|
441
441
|
"last_threat_review": "2026-05-22",
|
|
442
442
|
"signature": "za1NKBpy9LC91F/ESO/qhUfmvVr8GNItQOjR5OJLeHm+2dQ9HHiFWQK2eo53V/n/0uhubuggURA3yS6kJuWwBg==",
|
|
443
|
-
"signed_at": "2026-05-
|
|
443
|
+
"signed_at": "2026-05-28T23:31:33.983Z",
|
|
444
444
|
"cwe_refs": [
|
|
445
445
|
"CWE-1188"
|
|
446
446
|
],
|
|
@@ -474,7 +474,7 @@
|
|
|
474
474
|
"framework_gaps": [],
|
|
475
475
|
"last_threat_review": "2026-05-18",
|
|
476
476
|
"signature": "1853tUUOO35GCXP4vn4qYdBDCdGJu2/WXCNOShhEt69G6BUoq9C3gYGjKWwEPxRKjwjyMtb2nmsL2BZFHRIADw==",
|
|
477
|
-
"signed_at": "2026-05-
|
|
477
|
+
"signed_at": "2026-05-28T23:31:33.983Z",
|
|
478
478
|
"forward_watch": [
|
|
479
479
|
"New AI attack classes as ATLAS v6 publishes",
|
|
480
480
|
"Post-quantum adversary capability timeline",
|
|
@@ -513,7 +513,7 @@
|
|
|
513
513
|
"framework_gaps": [],
|
|
514
514
|
"last_threat_review": "2026-05-01",
|
|
515
515
|
"signature": "ZoFh0msOLToS9Xntp9e6zAcg8mE5TOsm7Tq9EYybKL7midjQ5z+aue1Uhp4+xjMRRqVtj8F2W0PvIBTYkYlyAg==",
|
|
516
|
-
"signed_at": "2026-05-
|
|
516
|
+
"signed_at": "2026-05-28T23:31:33.984Z"
|
|
517
517
|
},
|
|
518
518
|
{
|
|
519
519
|
"name": "zeroday-gap-learn",
|
|
@@ -540,7 +540,7 @@
|
|
|
540
540
|
"framework_gaps": [],
|
|
541
541
|
"last_threat_review": "2026-05-18",
|
|
542
542
|
"signature": "igRqYyU1unRFH40BsPyAR62SPrk8QZv8dPGb8S9O9EvLCNOZAzm3t+HdT/NKqzWHwrpomOzkkkyLfYI/0qTUDA==",
|
|
543
|
-
"signed_at": "2026-05-
|
|
543
|
+
"signed_at": "2026-05-28T23:31:33.984Z",
|
|
544
544
|
"forward_watch": [
|
|
545
545
|
"New CISA KEV entries",
|
|
546
546
|
"New ATLAS TTP additions in each ATLAS release",
|
|
@@ -604,7 +604,7 @@
|
|
|
604
604
|
],
|
|
605
605
|
"last_threat_review": "2026-05-22",
|
|
606
606
|
"signature": "XJFnPtu9tG23qlHGUJulQ0/Vu/p41qFrQng0fE3+GpCPD35+oipdlDXvf9zFo6A6i4OVj601qNuXKh9/kfcHDw==",
|
|
607
|
-
"signed_at": "2026-05-
|
|
607
|
+
"signed_at": "2026-05-28T23:31:33.984Z",
|
|
608
608
|
"cwe_refs": [
|
|
609
609
|
"CWE-327"
|
|
610
610
|
],
|
|
@@ -652,7 +652,7 @@
|
|
|
652
652
|
],
|
|
653
653
|
"last_threat_review": "2026-05-22",
|
|
654
654
|
"signature": "QuOVaQ4E2Sl39TClbhZ7HA9XrYAyRrDL44HY3RTE7aWLue0hV2cxaBt40ALGmHS++631QGFDlZTLZI77Tr6nAA==",
|
|
655
|
-
"signed_at": "2026-05-
|
|
655
|
+
"signed_at": "2026-05-28T23:31:33.985Z"
|
|
656
656
|
},
|
|
657
657
|
{
|
|
658
658
|
"name": "security-maturity-tiers",
|
|
@@ -689,7 +689,7 @@
|
|
|
689
689
|
],
|
|
690
690
|
"last_threat_review": "2026-05-01",
|
|
691
691
|
"signature": "8Px1s2lDj10/Q6erwEQlXgUHM1+OTruUR8qAHPX7Oo3k/l69N6P9sm0PsafS9wDFtj9l5C/OiLiFgzMlMt6vBw==",
|
|
692
|
-
"signed_at": "2026-05-
|
|
692
|
+
"signed_at": "2026-05-28T23:31:33.985Z",
|
|
693
693
|
"cwe_refs": [
|
|
694
694
|
"CWE-1188"
|
|
695
695
|
]
|
|
@@ -724,7 +724,7 @@
|
|
|
724
724
|
"framework_gaps": [],
|
|
725
725
|
"last_threat_review": "2026-05-11",
|
|
726
726
|
"signature": "urRcataVWg6/utyEkSiOWoNxTL8sABRjPR7ShyDfZGnAozFph/yDktSoaPVxQDXwu9EfJE+qhUW5OYR/yJECBQ==",
|
|
727
|
-
"signed_at": "2026-05-
|
|
727
|
+
"signed_at": "2026-05-28T23:31:33.985Z"
|
|
728
728
|
},
|
|
729
729
|
{
|
|
730
730
|
"name": "attack-surface-pentest",
|
|
@@ -796,7 +796,7 @@
|
|
|
796
796
|
"Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — Microsoft Edge 4-bug sandbox escape by Orange Tsai (DEVCORE); forward-watch only (browser sandbox, out of current playbook scope); track Microsoft Edge security advisory and KEV add"
|
|
797
797
|
],
|
|
798
798
|
"signature": "C7lv65/Ecm8JJgSKxrX5lxx0YFzKWtrIQSKp+vy50I5e8945s1JmifGUUrnQwRQhq/Pkv7EmfiH5XSO8h75bDg==",
|
|
799
|
-
"signed_at": "2026-05-
|
|
799
|
+
"signed_at": "2026-05-28T23:31:33.986Z"
|
|
800
800
|
},
|
|
801
801
|
{
|
|
802
802
|
"name": "fuzz-testing-strategy",
|
|
@@ -856,7 +856,7 @@
|
|
|
856
856
|
"OSS-Fuzz-Gen / AI-assisted harness generation becoming the default expectation for OSS maintainers"
|
|
857
857
|
],
|
|
858
858
|
"signature": "Z7ypCUnXx8JpLtgxxB6RHNi39w74AmrGY1N4ofAGCXhkuM2EaFVm1AU0dvl9UQ1bVLfHKEDGqMO/TwlIY7RABg==",
|
|
859
|
-
"signed_at": "2026-05-
|
|
859
|
+
"signed_at": "2026-05-28T23:31:33.986Z"
|
|
860
860
|
},
|
|
861
861
|
{
|
|
862
862
|
"name": "dlp-gap-analysis",
|
|
@@ -931,7 +931,7 @@
|
|
|
931
931
|
"Quebec Law 25, India DPDPA, KSA PDPL enforcement actions naming AI-tool prompt data as in-scope personal information"
|
|
932
932
|
],
|
|
933
933
|
"signature": "IgEnpHOhCftAyfUNdKsjbrd169T9pJkk/rRM2ZEna+H18y7p5x48+1kME2sJMZjJuyAdQFBJi8PJXZFwLGI+DQ==",
|
|
934
|
-
"signed_at": "2026-05-
|
|
934
|
+
"signed_at": "2026-05-28T23:31:33.986Z"
|
|
935
935
|
},
|
|
936
936
|
{
|
|
937
937
|
"name": "supply-chain-integrity",
|
|
@@ -1010,7 +1010,7 @@
|
|
|
1010
1010
|
"Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — NVIDIA Megatron Bridge path traversal by haehae; AI training-stack file-system trust boundary; track patch and SBOM-attestation impact"
|
|
1011
1011
|
],
|
|
1012
1012
|
"signature": "pcLrM98A3vUSZRjwNAk0aZ9umvOwB41XCLLsCOy/IebB2F/06oIrGUKkMHtHwm4pTVPShMMcKdZQQ3jz30FnCg==",
|
|
1013
|
-
"signed_at": "2026-05-
|
|
1013
|
+
"signed_at": "2026-05-28T23:31:33.987Z"
|
|
1014
1014
|
},
|
|
1015
1015
|
{
|
|
1016
1016
|
"name": "defensive-countermeasure-mapping",
|
|
@@ -1067,7 +1067,7 @@
|
|
|
1067
1067
|
],
|
|
1068
1068
|
"last_threat_review": "2026-05-11",
|
|
1069
1069
|
"signature": "G5q5elh7Q7eu2xcwTVQJGDTGfvZR0OGQaLSLJPb2wjzCHFF8PWuZfCHZdjjqisiRzRWPyLlzgfHeMJqOdy7cBw==",
|
|
1070
|
-
"signed_at": "2026-05-
|
|
1070
|
+
"signed_at": "2026-05-28T23:31:33.987Z"
|
|
1071
1071
|
},
|
|
1072
1072
|
{
|
|
1073
1073
|
"name": "identity-assurance",
|
|
@@ -1134,7 +1134,7 @@
|
|
|
1134
1134
|
"d3fend_refs": [],
|
|
1135
1135
|
"last_threat_review": "2026-05-11",
|
|
1136
1136
|
"signature": "Wv5hGMeHjlaQK1zwicVCA7AvdKgJBgvcjdpGM9Ywahh9tagAKhbkOjybowDQZzu7OZ3bDkbh6pBYc1Sdwr6NAA==",
|
|
1137
|
-
"signed_at": "2026-05-
|
|
1137
|
+
"signed_at": "2026-05-28T23:31:33.987Z"
|
|
1138
1138
|
},
|
|
1139
1139
|
{
|
|
1140
1140
|
"name": "ot-ics-security",
|
|
@@ -1190,7 +1190,7 @@
|
|
|
1190
1190
|
"d3fend_refs": [],
|
|
1191
1191
|
"last_threat_review": "2026-05-11",
|
|
1192
1192
|
"signature": "8t5qKHd3yWi57dvG36YQkLN/X9bQWqtEiYjay4IfSmqhJpM/xXPaQVKNGz3wscrO8OLKUZ0OaX7Mj5kzpgBKBQ==",
|
|
1193
|
-
"signed_at": "2026-05-
|
|
1193
|
+
"signed_at": "2026-05-28T23:31:33.987Z"
|
|
1194
1194
|
},
|
|
1195
1195
|
{
|
|
1196
1196
|
"name": "coordinated-vuln-disclosure",
|
|
@@ -1242,7 +1242,7 @@
|
|
|
1242
1242
|
"NYDFS 23 NYCRR 500 amendments potentially adding explicit CVD program requirements"
|
|
1243
1243
|
],
|
|
1244
1244
|
"signature": "GDGt4UPqBa04PjlpSmpyihGzd3OgfBN7jaAK5tfwp+LRSs3ygKOdbeivUCCHNagTY1hE6hG2Ou40ADfBFuXeAg==",
|
|
1245
|
-
"signed_at": "2026-05-
|
|
1245
|
+
"signed_at": "2026-05-28T23:31:33.988Z"
|
|
1246
1246
|
},
|
|
1247
1247
|
{
|
|
1248
1248
|
"name": "threat-modeling-methodology",
|
|
@@ -1292,7 +1292,7 @@
|
|
|
1292
1292
|
"PASTA v2 updates incorporating AI/ML application threats"
|
|
1293
1293
|
],
|
|
1294
1294
|
"signature": "t6IgW+ciQMuY5uvLD0HDDB/U1TjZmgAy76bvH0526QcqPMCz27CWX3cdyJ6nxpg3rYrx93xAawu0opw/ZZckCw==",
|
|
1295
|
-
"signed_at": "2026-05-
|
|
1295
|
+
"signed_at": "2026-05-28T23:31:33.988Z"
|
|
1296
1296
|
},
|
|
1297
1297
|
{
|
|
1298
1298
|
"name": "webapp-security",
|
|
@@ -1366,7 +1366,7 @@
|
|
|
1366
1366
|
"d3fend_refs": [],
|
|
1367
1367
|
"last_threat_review": "2026-05-11",
|
|
1368
1368
|
"signature": "ux85YI4t2mVHOyt744Yin1HHy+z11JIFygjKfFfQOBBl5QVV3A267jeIy7utix85irMcpZm/T3yx/ooqiK2tBA==",
|
|
1369
|
-
"signed_at": "2026-05-
|
|
1369
|
+
"signed_at": "2026-05-28T23:31:33.988Z",
|
|
1370
1370
|
"forward_watch": [
|
|
1371
1371
|
"NGINX Rift CVE-2026-42945 (disclosed 2026-05-13, source depthfirst) — KEV-watch predicted CISA KEV listing by 2026-05-29; AI-assisted discovery angle; track for active-exploitation confirmation and patch advisory affecting front-door web app deployments"
|
|
1372
1372
|
]
|
|
@@ -1419,7 +1419,7 @@
|
|
|
1419
1419
|
"d3fend_refs": [],
|
|
1420
1420
|
"last_threat_review": "2026-05-15",
|
|
1421
1421
|
"signature": "IIXnkZ5ZNqFwOto5KfytADTLLZLoyXNZACD1ORZ40P1HUAQxe6u2uyXFzzsfuob4Uy06jNkRGr2FFgCphUH1Cw==",
|
|
1422
|
-
"signed_at": "2026-05-
|
|
1422
|
+
"signed_at": "2026-05-28T23:31:33.989Z"
|
|
1423
1423
|
},
|
|
1424
1424
|
{
|
|
1425
1425
|
"name": "sector-healthcare",
|
|
@@ -1479,7 +1479,7 @@
|
|
|
1479
1479
|
"d3fend_refs": [],
|
|
1480
1480
|
"last_threat_review": "2026-05-11",
|
|
1481
1481
|
"signature": "AhF9KF8ZBlDteciV+F8IBSmFVYCvQOn44GmD4rZjgLoPxfIv/QE1/vSkK32zyqDKtHWkLSXExbkkPkxA/V6dDw==",
|
|
1482
|
-
"signed_at": "2026-05-
|
|
1482
|
+
"signed_at": "2026-05-28T23:31:33.989Z"
|
|
1483
1483
|
},
|
|
1484
1484
|
{
|
|
1485
1485
|
"name": "sector-financial",
|
|
@@ -1560,7 +1560,7 @@
|
|
|
1560
1560
|
"TIBER-EU framework v2.0 alignment with DORA TLPT RTS (JC 2024/40); cross-recognition with CBEST and iCAST"
|
|
1561
1561
|
],
|
|
1562
1562
|
"signature": "HQgZvb4ReziEz5rNFr8i/O8/rJEZR+iHRROT7m/D2QUqhrcNISPkYXENsUZlG8xapzy/Ik92ehkseyj4hdmhCQ==",
|
|
1563
|
-
"signed_at": "2026-05-
|
|
1563
|
+
"signed_at": "2026-05-28T23:31:33.990Z"
|
|
1564
1564
|
},
|
|
1565
1565
|
{
|
|
1566
1566
|
"name": "sector-federal-government",
|
|
@@ -1629,7 +1629,7 @@
|
|
|
1629
1629
|
"Australia PSPF 2024 revision and ISM quarterly updates — track for Essential Eight Maturity Level requirements for federal entities"
|
|
1630
1630
|
],
|
|
1631
1631
|
"signature": "linxmsXZiOYtcs71sSWgGCrvb8xQfmxmtTY5PRvZJ0/8FgJulo0tQtejzexYG775s7XhjAmGsDP238BQTQ8ADA==",
|
|
1632
|
-
"signed_at": "2026-05-
|
|
1632
|
+
"signed_at": "2026-05-28T23:31:33.990Z"
|
|
1633
1633
|
},
|
|
1634
1634
|
{
|
|
1635
1635
|
"name": "sector-energy",
|
|
@@ -1694,7 +1694,7 @@
|
|
|
1694
1694
|
"ICS-CERT advisory feed (https://www.cisa.gov/news-events/cybersecurity-advisories/ics-advisories) for vendor CVEs in Siemens, Rockwell, Schneider Electric, ABB, GE Vernova, Hitachi Energy, AVEVA / OSIsoft PI"
|
|
1695
1695
|
],
|
|
1696
1696
|
"signature": "JjBfc0ovta560Clk0x3QGRM5osFJDwcvpy3rT7QEGdCIL827jzE8QCow1C8deXq+4JhY2sA/d7/8IsxikdlkCg==",
|
|
1697
|
-
"signed_at": "2026-05-
|
|
1697
|
+
"signed_at": "2026-05-28T23:31:33.990Z"
|
|
1698
1698
|
},
|
|
1699
1699
|
{
|
|
1700
1700
|
"name": "sector-telecom",
|
|
@@ -1780,7 +1780,7 @@
|
|
|
1780
1780
|
"O-RAN SFG / WG11 security specifications"
|
|
1781
1781
|
],
|
|
1782
1782
|
"signature": "QpfWU8d2FFbo/Kt0Be2jcyDM9JPhKOiwKSi+RWmtPa2SrBVzFNYdC3T23fQXxHDEC2iyApEbXnwk8KN4d6PuBQ==",
|
|
1783
|
-
"signed_at": "2026-05-
|
|
1783
|
+
"signed_at": "2026-05-28T23:31:33.991Z"
|
|
1784
1784
|
},
|
|
1785
1785
|
{
|
|
1786
1786
|
"name": "api-security",
|
|
@@ -1849,7 +1849,7 @@
|
|
|
1849
1849
|
"d3fend_refs": [],
|
|
1850
1850
|
"last_threat_review": "2026-05-18",
|
|
1851
1851
|
"signature": "BmCRCestWqr55+fCynEhtAl5NWLT+xLTkpwS0Icp3SaoZOw/ce3Y6TtqjHRSKn4CBJq7YDiLRWxmhO3MStvOAA==",
|
|
1852
|
-
"signed_at": "2026-05-
|
|
1852
|
+
"signed_at": "2026-05-28T23:31:33.991Z",
|
|
1853
1853
|
"forward_watch": [
|
|
1854
1854
|
"NGINX Rift CVE-2026-42945 (disclosed 2026-05-13, source depthfirst) — KEV-watch predicted CISA KEV listing by 2026-05-29; track for active-exploitation confirmation and patch advisory affecting API gateway / reverse-proxy deployments",
|
|
1855
1855
|
"Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — LiteLLM 3-bug SSRF + Code Injection chain by k3vg3n; LLM-proxy API surface; track upstream patch and CVE assignments",
|
|
@@ -1935,7 +1935,7 @@
|
|
|
1935
1935
|
"CISA KEV additions for cloud-control-plane CVEs (IMDSv1 abuses, federation token mishandling, cross-tenant boundary failures); CISA Cybersecurity Advisories for cross-cloud advisories"
|
|
1936
1936
|
],
|
|
1937
1937
|
"signature": "/DV3pmZwrRySrk1OCbyI+0BQESacjupJfUX3eC2NGtXuYOBro0vndIP+z27heFxumnjU3a9sfla7/U9X+pqnDw==",
|
|
1938
|
-
"signed_at": "2026-05-
|
|
1938
|
+
"signed_at": "2026-05-28T23:31:33.991Z"
|
|
1939
1939
|
},
|
|
1940
1940
|
{
|
|
1941
1941
|
"name": "container-runtime-security",
|
|
@@ -1997,7 +1997,7 @@
|
|
|
1997
1997
|
"d3fend_refs": [],
|
|
1998
1998
|
"last_threat_review": "2026-05-15",
|
|
1999
1999
|
"signature": "E2UGSf9ATyYgzBr8uM/0ubOUmDqo1jVA7f9mVxv6LHfWGCNuQNXDyuNou9VAmUCeeXEeUYIi3AFjXkJqpOkxDA==",
|
|
2000
|
-
"signed_at": "2026-05-
|
|
2000
|
+
"signed_at": "2026-05-28T23:31:33.992Z",
|
|
2001
2001
|
"forward_watch": [
|
|
2002
2002
|
"Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — NVIDIA Container Toolkit container escape ($50K award) by chompie / IBM X-Force XOR; high-severity container/hypervisor boundary break; track patch and KEV add post-embargo"
|
|
2003
2003
|
]
|
|
@@ -2071,7 +2071,7 @@
|
|
|
2071
2071
|
"MITRE ATLAS v5.6.0 (released May 2026) shipped the AML.T0010 sub-technique expansion this forecast tracked plus new techniques (\"Publish Poisoned AI Agent Tool\", \"Escape to Host\"); inventory now 16 tactics, 84 techniques, 56 sub-techniques. Forward watch: subsequent ATLAS minor and major releases — track next-cadence updates to agentic-AI TTPs and MLOps-pipeline-specific techniques"
|
|
2072
2072
|
],
|
|
2073
2073
|
"signature": "IL+DlRCDJN/p08iiJCFkasKcoyjcB0uWrJ6ORLjQcS1HrUa5Xt62QxVjYPHzaevlm5y36ZdmfESqsZJmzK3lCg==",
|
|
2074
|
-
"signed_at": "2026-05-
|
|
2074
|
+
"signed_at": "2026-05-28T23:31:33.992Z"
|
|
2075
2075
|
},
|
|
2076
2076
|
{
|
|
2077
2077
|
"name": "incident-response-playbook",
|
|
@@ -2133,7 +2133,7 @@
|
|
|
2133
2133
|
"NYDFS 23 NYCRR 500.17 amendments tightening ransom-payment 24h disclosure operationalization"
|
|
2134
2134
|
],
|
|
2135
2135
|
"signature": "MmjLjlmOMLjhJJ4ZfR8MYlHam+ZB+eSqfh6Nv+DecaG4O5zeo9DBP/iL3cbyDVZxmhnhivgJild2ccYeWTeZAg==",
|
|
2136
|
-
"signed_at": "2026-05-
|
|
2136
|
+
"signed_at": "2026-05-28T23:31:33.992Z"
|
|
2137
2137
|
},
|
|
2138
2138
|
{
|
|
2139
2139
|
"name": "ransomware-response",
|
|
@@ -2213,7 +2213,7 @@
|
|
|
2213
2213
|
],
|
|
2214
2214
|
"last_threat_review": "2026-05-22",
|
|
2215
2215
|
"signature": "ssueL03g9fWlhXpTe+IiY5l7RqQkunN4DTN5QETKE+VOX+qggdjAR8PONxk77ol4xWYmHrM/VcH8CNtXUEvgBA==",
|
|
2216
|
-
"signed_at": "2026-05-
|
|
2216
|
+
"signed_at": "2026-05-28T23:31:33.993Z"
|
|
2217
2217
|
},
|
|
2218
2218
|
{
|
|
2219
2219
|
"name": "email-security-anti-phishing",
|
|
@@ -2266,7 +2266,7 @@
|
|
|
2266
2266
|
"d3fend_refs": [],
|
|
2267
2267
|
"last_threat_review": "2026-05-18",
|
|
2268
2268
|
"signature": "oAFeZmci6BN7Xlu0oOThrazZNBQBIWeCQnoZDqjXo9cs1hIe/6ZijSEr3R3hFQSXH3c1KAbhPw+InR9Gc0HIBQ==",
|
|
2269
|
-
"signed_at": "2026-05-
|
|
2269
|
+
"signed_at": "2026-05-28T23:31:33.993Z"
|
|
2270
2270
|
},
|
|
2271
2271
|
{
|
|
2272
2272
|
"name": "age-gates-child-safety",
|
|
@@ -2334,7 +2334,7 @@
|
|
|
2334
2334
|
"US state adult-site age-verification laws — 19+ states by mid-2026 (TX HB 18 upheld by SCOTUS June 2025 in Free Speech Coalition v. Paxton); track ongoing challenges in remaining states"
|
|
2335
2335
|
],
|
|
2336
2336
|
"signature": "Rgho5TOFUL1txOzcVR0kASCNdovSU4yt99JlGilJlJRyg0A+BdeeQYrZrhPF6Vx2reUAVG0BeHfcZtSbi+cwCg==",
|
|
2337
|
-
"signed_at": "2026-05-
|
|
2337
|
+
"signed_at": "2026-05-28T23:31:33.994Z"
|
|
2338
2338
|
},
|
|
2339
2339
|
{
|
|
2340
2340
|
"name": "cloud-iam-incident",
|
|
@@ -2414,7 +2414,7 @@
|
|
|
2414
2414
|
],
|
|
2415
2415
|
"last_threat_review": "2026-05-15",
|
|
2416
2416
|
"signature": "e/kij7GtKaytROyIj7V5RH+FC9WtmVFzrmG2kIlNDNn29ep/CRNlIQKwXLpzo/81AIf634pmdr1qy/+vwIuUDA==",
|
|
2417
|
-
"signed_at": "2026-05-
|
|
2417
|
+
"signed_at": "2026-05-28T23:31:33.994Z",
|
|
2418
2418
|
"forward_watch": [
|
|
2419
2419
|
"AWS IAM Identity Center session-policy refresh and step-up-on-admin enforcement (anticipated 2026-H2 release)",
|
|
2420
2420
|
"GCP Workload Identity Federation principal-set attribute mapping tightening (post-2026 Q3 Federation hardening guide)",
|
|
@@ -2508,7 +2508,7 @@
|
|
|
2508
2508
|
],
|
|
2509
2509
|
"last_threat_review": "2026-05-15",
|
|
2510
2510
|
"signature": "ew9Kglc9fAZzbn0ZIfGP7WSK/j4eV2VhSvpy+s5bEfNEVYIMa2kZjnGBapgUsyGDLes9H9K2ovjQyX17+GKiBw==",
|
|
2511
|
-
"signed_at": "2026-05-
|
|
2511
|
+
"signed_at": "2026-05-28T23:31:33.994Z",
|
|
2512
2512
|
"forward_watch": [
|
|
2513
2513
|
"Entra ID conditional access evolution post-Midnight Blizzard — Microsoft's 2025-2026 commitments on legacy-tenant MFA enforcement and OAuth-app consent gating",
|
|
2514
2514
|
"Okta IPSIE (Interoperability Profile for Secure Identity in the Enterprise) OpenID Foundation working-group output and adoption timeline",
|
|
@@ -2526,6 +2526,6 @@
|
|
|
2526
2526
|
],
|
|
2527
2527
|
"manifest_signature": {
|
|
2528
2528
|
"algorithm": "Ed25519",
|
|
2529
|
-
"signature_base64": "
|
|
2529
|
+
"signature_base64": "NsU29V6sBT+3b6VzUTJTkEOAad5kJac5OU+eXj+Dh6jX7FNYjLaeaWSS2HXNffMetsy4uMfXYooKp+0ZHBWiDQ=="
|
|
2530
2530
|
}
|
|
2531
2531
|
}
|
package/orchestrator/index.js
CHANGED
|
@@ -88,9 +88,28 @@ async function main() {
|
|
|
88
88
|
case 'skill':
|
|
89
89
|
runSkillContext(args);
|
|
90
90
|
break;
|
|
91
|
-
case 'pipeline':
|
|
92
|
-
|
|
91
|
+
case 'pipeline': {
|
|
92
|
+
// pipeline is not dispatched by the bin/ CLI; it's reachable only via a
|
|
93
|
+
// direct orchestrator invocation. Guard the findings JSON.parse so
|
|
94
|
+
// malformed input emits a structured ok:false envelope instead of an
|
|
95
|
+
// uncaught SyntaxError stack trace.
|
|
96
|
+
let findings = {};
|
|
97
|
+
if (args[1]) {
|
|
98
|
+
try {
|
|
99
|
+
findings = JSON.parse(args[1]);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
process.stdout.write(JSON.stringify({
|
|
102
|
+
ok: false,
|
|
103
|
+
verb: 'pipeline',
|
|
104
|
+
error: `pipeline: findings argument is not valid JSON: ${err.message}`,
|
|
105
|
+
}) + '\n');
|
|
106
|
+
safeExit(EXIT_CODES.GENERIC_FAILURE);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
runPipeline(args[0] || 'manual', findings);
|
|
93
111
|
break;
|
|
112
|
+
}
|
|
94
113
|
case 'currency':
|
|
95
114
|
runCurrency();
|
|
96
115
|
break;
|
|
@@ -136,6 +155,10 @@ function runFrameworkGap(rawArgs) {
|
|
|
136
155
|
const path = require('path');
|
|
137
156
|
const { gapReport, theaterCheck } = require('../lib/framework-gap');
|
|
138
157
|
|
|
158
|
+
// Reject unknown flags with the shared structured envelope. framework-gap
|
|
159
|
+
// consumes only --json; the global air-gap flags are accepted-and-ignored
|
|
160
|
+
// (the analytical path reads local catalogs, no egress).
|
|
161
|
+
if (rejectUnknownFlags('framework-gap', rawArgs, ['--json', '--air-gap', '--offline', '--no-network'])) return;
|
|
139
162
|
const args = rawArgs.filter(a => !a.startsWith('--'));
|
|
140
163
|
const flags = new Set(rawArgs.filter(a => a.startsWith('--')));
|
|
141
164
|
const jsonOut = flags.has('--json');
|
|
@@ -270,7 +293,10 @@ async function runScan() {
|
|
|
270
293
|
// other verbs (validate-cves, watchlist, etc.). Previously this was a
|
|
271
294
|
// bare `process.argv.includes('--json')`, which differed in style from
|
|
272
295
|
// the verbs below and could miss `--json=true` or similar future forms.
|
|
273
|
-
|
|
296
|
+
// --air-gap / --offline / --no-network are global flags; scan does only
|
|
297
|
+
// local filesystem probing, so they're accepted-and-ignored rather than
|
|
298
|
+
// rejected (no network I/O to suppress).
|
|
299
|
+
if (rejectUnknownFlags('scan', args, ['--json', '--air-gap', '--offline', '--no-network'])) return;
|
|
274
300
|
const { flags } = parseFlags(process.argv.slice(2), []);
|
|
275
301
|
const jsonOut = flags.has('--json');
|
|
276
302
|
if (!jsonOut) console.log('[orchestrator] Scanning environment...\n');
|
|
@@ -308,7 +334,9 @@ async function runScan() {
|
|
|
308
334
|
}
|
|
309
335
|
|
|
310
336
|
async function runDispatch() {
|
|
311
|
-
|
|
337
|
+
// --air-gap / --offline / --no-network: local-only verb, accepted-and-ignored
|
|
338
|
+
// (see runScan).
|
|
339
|
+
if (rejectUnknownFlags('dispatch', args, ['--json', '--air-gap', '--offline', '--no-network'])) return;
|
|
312
340
|
const jsonOut = process.argv.includes('--json');
|
|
313
341
|
if (!jsonOut) console.log('[orchestrator] Scanning then dispatching...\n');
|
|
314
342
|
const scanResult = await scan();
|
|
@@ -356,6 +384,10 @@ function runSkillContext(rawArgs) {
|
|
|
356
384
|
// `skill --json` passed "--json" through as args[0] and reported
|
|
357
385
|
// "Skill not found: --json".
|
|
358
386
|
const argList = Array.isArray(rawArgs) ? rawArgs : (rawArgs == null ? [] : [rawArgs]);
|
|
387
|
+
// Reject unknown flags with the shared structured envelope. skill consumes
|
|
388
|
+
// only --json; the global air-gap flags are accepted-and-ignored (skill
|
|
389
|
+
// context is read from local skill files, no egress).
|
|
390
|
+
if (rejectUnknownFlags('skill', argList, ['--json', '--air-gap', '--offline', '--no-network'])) return;
|
|
359
391
|
const jsonOut = argList.includes('--json');
|
|
360
392
|
const positionals = argList.filter(a => typeof a === 'string' && !a.startsWith('--'));
|
|
361
393
|
const skillName = positionals[0];
|
|
@@ -415,7 +447,9 @@ function runPipeline(triggerType, payload) {
|
|
|
415
447
|
}
|
|
416
448
|
|
|
417
449
|
function runCurrency() {
|
|
418
|
-
|
|
450
|
+
// --air-gap / --offline / --no-network: local-only verb, accepted-and-ignored
|
|
451
|
+
// (see runScan).
|
|
452
|
+
if (rejectUnknownFlags('currency', args, ['--json', '--air-gap', '--offline', '--no-network'])) return;
|
|
419
453
|
const jsonOut = process.argv.includes('--json');
|
|
420
454
|
const result = runCurrencyNow();
|
|
421
455
|
const { currency_report, action_required, critical_count } = currencyCheck();
|
|
@@ -442,6 +476,11 @@ function runCurrency() {
|
|
|
442
476
|
}
|
|
443
477
|
|
|
444
478
|
async function runReport(format) {
|
|
479
|
+
// Reject unknown flags with the same structured envelope the other verbs
|
|
480
|
+
// emit. report takes only a format positional; --json is accepted for
|
|
481
|
+
// parity, and the global air-gap flags are accepted-and-ignored (the
|
|
482
|
+
// report path's scan() is local-only).
|
|
483
|
+
if (rejectUnknownFlags('report', args, ['--json', '--air-gap', '--offline', '--no-network'])) return;
|
|
445
484
|
// v0.11.6 (#98): validate format positional. Pre-0.11.6 unknown formats
|
|
446
485
|
// emitted a generic "# exceptd Report" header — silently accepted any
|
|
447
486
|
// string. Now: reject with structured JSON error matching other verbs.
|
|
@@ -655,6 +694,11 @@ function _acquireWatchLock() {
|
|
|
655
694
|
}
|
|
656
695
|
|
|
657
696
|
async function runWatch() {
|
|
697
|
+
// Reject unknown flags before acquiring the watch lock / starting the
|
|
698
|
+
// scheduler. --log-file is the value-taking option this verb consumes;
|
|
699
|
+
// --json is accepted for parity; the global air-gap flags are
|
|
700
|
+
// accepted-and-ignored (watch does no egress of its own).
|
|
701
|
+
if (rejectUnknownFlags('watch', args, ['--log-file', '--json', '--air-gap', '--offline', '--no-network'])) return;
|
|
658
702
|
const { flags, options } = parseFlags(args, ['--log-file']);
|
|
659
703
|
const logFilePath = options.get('--log-file');
|
|
660
704
|
let logStream = null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamejs/exceptd-skills",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (
|
|
3
|
+
"version": "0.15.0",
|
|
4
|
+
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (427 CVEs / 173 CWEs / 805 ATT&CK + ICS / 170 ATLAS / 468 D3FEND / 8888 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-security",
|
|
7
7
|
"ai-skills",
|
package/sbom.cdx.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.6",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:779a30c1-027c-4fca-b5e9-b9ddbfd4f383",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "
|
|
7
|
+
"timestamp": "2089-08-02T11:08:49.000Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "blamejs",
|
|
11
11
|
"name": "scripts/refresh-sbom.js",
|
|
12
|
-
"version": "0.
|
|
12
|
+
"version": "0.15.0"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"component": {
|
|
16
|
-
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.
|
|
16
|
+
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.15.0",
|
|
17
17
|
"type": "application",
|
|
18
18
|
"name": "@blamejs/exceptd-skills",
|
|
19
|
-
"version": "0.
|
|
20
|
-
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (
|
|
19
|
+
"version": "0.15.0",
|
|
20
|
+
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (427 CVEs / 173 CWEs / 805 ATT&CK + ICS / 170 ATLAS / 468 D3FEND / 8888 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
|
|
21
21
|
"licenses": [
|
|
22
22
|
{
|
|
23
23
|
"license": {
|
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.
|
|
28
|
+
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.15.0",
|
|
29
29
|
"hashes": [
|
|
30
30
|
{
|
|
31
31
|
"alg": "SHA-256",
|
|
32
|
-
"content": "
|
|
32
|
+
"content": "958178dbd6a92b6863edbb5d1e77004b11bbf65850e70d1d1a010ada78d2ca61"
|
|
33
33
|
}
|
|
34
34
|
],
|
|
35
35
|
"externalReferences": [
|
|
36
36
|
{
|
|
37
37
|
"type": "distribution",
|
|
38
|
-
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.
|
|
38
|
+
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.15.0"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"type": "vcs",
|
|
@@ -101,11 +101,11 @@
|
|
|
101
101
|
"hashes": [
|
|
102
102
|
{
|
|
103
103
|
"alg": "SHA-256",
|
|
104
|
-
"content": "
|
|
104
|
+
"content": "0ec6914dd27c3c015bbd1a6c6e534853f5aaef25ac7299b6e40f309553e7546c"
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"alg": "SHA3-512",
|
|
108
|
-
"content": "
|
|
108
|
+
"content": "2b00b049ab8261131963e476fcc40486d684b4e95a0dcbdc20affd978dc073e8b151af6b75c9cfe2d292b9b8cda65aed1b27b36588ca40f7299538f09b093a40"
|
|
109
109
|
}
|
|
110
110
|
]
|
|
111
111
|
},
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
"hashes": [
|
|
117
117
|
{
|
|
118
118
|
"alg": "SHA-256",
|
|
119
|
-
"content": "
|
|
119
|
+
"content": "5f6f30b13d184ad89b9ec37b646a415efe9c3b321950ead58173d96151561fe8"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"alg": "SHA3-512",
|
|
123
|
-
"content": "
|
|
123
|
+
"content": "9e279065a57a02b74c37e8f0c9c239ef05976769493d49b172a0f6a77ae49023b23de61f0215e608dcb14e80b2ebfae9a492d8b3afecde0bf4de5dacbeb48e49"
|
|
124
124
|
}
|
|
125
125
|
]
|
|
126
126
|
},
|
|
@@ -176,11 +176,11 @@
|
|
|
176
176
|
"hashes": [
|
|
177
177
|
{
|
|
178
178
|
"alg": "SHA-256",
|
|
179
|
-
"content": "
|
|
179
|
+
"content": "45236da64a3811cc1301477352b6c8aa29b91907f5b9c988176002648284bc52"
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"alg": "SHA3-512",
|
|
183
|
-
"content": "
|
|
183
|
+
"content": "9f1f6812a30abc0c973f7b67a428029bb97726dfac7da51c4cf984e01ee5373c2422f2b3d3512427d182fb0b14ec248ef5bba07e21ff72f5fb6893180d9a7498"
|
|
184
184
|
}
|
|
185
185
|
]
|
|
186
186
|
},
|
|
@@ -281,11 +281,11 @@
|
|
|
281
281
|
"hashes": [
|
|
282
282
|
{
|
|
283
283
|
"alg": "SHA-256",
|
|
284
|
-
"content": "
|
|
284
|
+
"content": "95b02d8293463cbf429a6ec57285c73ab83e9c93172391d019187ae68fe3a924"
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
"alg": "SHA3-512",
|
|
288
|
-
"content": "
|
|
288
|
+
"content": "8a3367e81fe6e0ac89cc430aa7b55da3fd52e30ccdc2b790f0e36a2db699ab0f60cd1027afe7d48aba4f38101a419495e1f03edd25f87eee64d67a8b68f53aa0"
|
|
289
289
|
}
|
|
290
290
|
]
|
|
291
291
|
},
|
|
@@ -806,11 +806,11 @@
|
|
|
806
806
|
"hashes": [
|
|
807
807
|
{
|
|
808
808
|
"alg": "SHA-256",
|
|
809
|
-
"content": "
|
|
809
|
+
"content": "e8202ffa99ed7c7d40b89ee5eedbea33839048cbff482e09329292e6700a5157"
|
|
810
810
|
},
|
|
811
811
|
{
|
|
812
812
|
"alg": "SHA3-512",
|
|
813
|
-
"content": "
|
|
813
|
+
"content": "6a606c36b778ec60e04d59aa02fe08fa6c845c2131f264b7c739f881a89a738bdddd32274b1a0c9d2d27ef829529f5392db2fd803bb9075455d4214f3d3b01ff"
|
|
814
814
|
}
|
|
815
815
|
]
|
|
816
816
|
},
|
|
@@ -1256,11 +1256,11 @@
|
|
|
1256
1256
|
"hashes": [
|
|
1257
1257
|
{
|
|
1258
1258
|
"alg": "SHA-256",
|
|
1259
|
-
"content": "
|
|
1259
|
+
"content": "ec24ab0571195a679fda6d7fd23c9c577e9616ba655fd04e4231817ca725f7e4"
|
|
1260
1260
|
},
|
|
1261
1261
|
{
|
|
1262
1262
|
"alg": "SHA3-512",
|
|
1263
|
-
"content": "
|
|
1263
|
+
"content": "7f083e86fa43a70e514d2b62d7337ccfcc644ba5a225936ec972cd11540e6d262321c092acb271897881d2803c9e0b40896aa8af5b272ac0f101c9ef24491bf2"
|
|
1264
1264
|
}
|
|
1265
1265
|
]
|
|
1266
1266
|
},
|
|
@@ -1301,11 +1301,11 @@
|
|
|
1301
1301
|
"hashes": [
|
|
1302
1302
|
{
|
|
1303
1303
|
"alg": "SHA-256",
|
|
1304
|
-
"content": "
|
|
1304
|
+
"content": "2b956a7a8d528e93e6cce8c2b09e7b228495c4d688c1403f7de5f60bc62d71b1"
|
|
1305
1305
|
},
|
|
1306
1306
|
{
|
|
1307
1307
|
"alg": "SHA3-512",
|
|
1308
|
-
"content": "
|
|
1308
|
+
"content": "ea8d4ff719262d817b2892dbec6e14f687afde27b7ab31ca621c6e9b0a4ccf98eb3aa5a4753281efd06f188b6d0e34bf98aaf995758cf9c3a9379bae5bb17313"
|
|
1309
1309
|
}
|
|
1310
1310
|
]
|
|
1311
1311
|
},
|
|
@@ -1316,11 +1316,11 @@
|
|
|
1316
1316
|
"hashes": [
|
|
1317
1317
|
{
|
|
1318
1318
|
"alg": "SHA-256",
|
|
1319
|
-
"content": "
|
|
1319
|
+
"content": "60bd6a680557bc848a723a20fc50049941b658f08c72205b4afb0ae19584d837"
|
|
1320
1320
|
},
|
|
1321
1321
|
{
|
|
1322
1322
|
"alg": "SHA3-512",
|
|
1323
|
-
"content": "
|
|
1323
|
+
"content": "8648247dd05f0dcb9da52f2795da8ebda8e95e2652ddf3b1aff5c8cb4d530b7d8bbe4949e061641ace658445baa4f1f5ad1ff8444a0bbb6b964e8adf0e4cd30e"
|
|
1324
1324
|
}
|
|
1325
1325
|
]
|
|
1326
1326
|
},
|
|
@@ -1586,11 +1586,11 @@
|
|
|
1586
1586
|
"hashes": [
|
|
1587
1587
|
{
|
|
1588
1588
|
"alg": "SHA-256",
|
|
1589
|
-
"content": "
|
|
1589
|
+
"content": "d3e8dbc27d2a48759a4f40d7e075801065814bc3d971775c2272bc1f26dc1a89"
|
|
1590
1590
|
},
|
|
1591
1591
|
{
|
|
1592
1592
|
"alg": "SHA3-512",
|
|
1593
|
-
"content": "
|
|
1593
|
+
"content": "73ef5fd057c8257b4187cd429499e7494d290e0c9792f8e779d8909a9f77e13f8d1d282489ed79c855fa83a45464eb5acbccc603d4baf86325d2bc136ae60a11"
|
|
1594
1594
|
}
|
|
1595
1595
|
]
|
|
1596
1596
|
},
|
|
@@ -1751,11 +1751,11 @@
|
|
|
1751
1751
|
"hashes": [
|
|
1752
1752
|
{
|
|
1753
1753
|
"alg": "SHA-256",
|
|
1754
|
-
"content": "
|
|
1754
|
+
"content": "2075a9d45002fe52785b0aa326d3bf6531f6b9b611c4c8ae166b413ca5f06b8c"
|
|
1755
1755
|
},
|
|
1756
1756
|
{
|
|
1757
1757
|
"alg": "SHA3-512",
|
|
1758
|
-
"content": "
|
|
1758
|
+
"content": "f6d432a3b181644811b9e5a8ba646cfa750996451ab408ad34c3bc6fc2d47f3dc3d85e88dda762fe69f63024bfc700d4a58fd19765052eeb77364544eb790988"
|
|
1759
1759
|
}
|
|
1760
1760
|
]
|
|
1761
1761
|
},
|
|
@@ -1811,11 +1811,11 @@
|
|
|
1811
1811
|
"hashes": [
|
|
1812
1812
|
{
|
|
1813
1813
|
"alg": "SHA-256",
|
|
1814
|
-
"content": "
|
|
1814
|
+
"content": "e16aa7a99b3ea873c3977e8649665f4704aa890595654b8f851ba0f5565d0518"
|
|
1815
1815
|
},
|
|
1816
1816
|
{
|
|
1817
1817
|
"alg": "SHA3-512",
|
|
1818
|
-
"content": "
|
|
1818
|
+
"content": "168c82da32d91b2a9b012948939dea72523b1c6ebe4da06cb49ca27dbefc8ead66c834f1be68a684f049114ebd106903cac6ef5b98e0a7830b8b3e1170f4e865"
|
|
1819
1819
|
}
|
|
1820
1820
|
]
|
|
1821
1821
|
},
|
|
@@ -2171,11 +2171,11 @@
|
|
|
2171
2171
|
"hashes": [
|
|
2172
2172
|
{
|
|
2173
2173
|
"alg": "SHA-256",
|
|
2174
|
-
"content": "
|
|
2174
|
+
"content": "79b3d1d5795a64f30ef65a9ec383a212d2eda581cdec4b6b261671b8ef8ad82b"
|
|
2175
2175
|
},
|
|
2176
2176
|
{
|
|
2177
2177
|
"alg": "SHA3-512",
|
|
2178
|
-
"content": "
|
|
2178
|
+
"content": "175a1243ad5d375a4d75467073214620021498f5439763581563ee4ded3a03344d1ea824735eb38c200bf5d0dd59c79c6fa0ba0d082d6966f92fb8387769bced"
|
|
2179
2179
|
}
|
|
2180
2180
|
]
|
|
2181
2181
|
},
|
|
@@ -2246,11 +2246,11 @@
|
|
|
2246
2246
|
"hashes": [
|
|
2247
2247
|
{
|
|
2248
2248
|
"alg": "SHA-256",
|
|
2249
|
-
"content": "
|
|
2249
|
+
"content": "1d3b7bd15af17a88afb82dada50494433299455e76206b23a895f1ff9ca8a696"
|
|
2250
2250
|
},
|
|
2251
2251
|
{
|
|
2252
2252
|
"alg": "SHA3-512",
|
|
2253
|
-
"content": "
|
|
2253
|
+
"content": "d4c3a7dc1af799ed3d8f28ba47b87a3ef4781cb28f688e76322d15404772e5199cc292bcee979eaefb19d0bb519635353a328412c59fd5baa412548282637ec8"
|
|
2254
2254
|
}
|
|
2255
2255
|
]
|
|
2256
2256
|
},
|
|
@@ -2291,11 +2291,11 @@
|
|
|
2291
2291
|
"hashes": [
|
|
2292
2292
|
{
|
|
2293
2293
|
"alg": "SHA-256",
|
|
2294
|
-
"content": "
|
|
2294
|
+
"content": "5a7fa5b528b49ca506043e5e3a0ce4a240059a9ff57a187f36750529a8dc64fc"
|
|
2295
2295
|
},
|
|
2296
2296
|
{
|
|
2297
2297
|
"alg": "SHA3-512",
|
|
2298
|
-
"content": "
|
|
2298
|
+
"content": "c122f45cc73ffd2a9cd3e72d0a398cd2297bd73b80b2ca540c4d9a8457c199cf7607bd34b60c9535557cf19f5b6d619014a6b14577909c69d11101785bc3fc56"
|
|
2299
2299
|
}
|
|
2300
2300
|
]
|
|
2301
2301
|
},
|
|
@@ -51,7 +51,11 @@ function loadAll() {
|
|
|
51
51
|
// in tests/shipped-catalog-integrity.test.js.
|
|
52
52
|
const BUDGET = {
|
|
53
53
|
"content-quality": 12,
|
|
54
|
-
|
|
54
|
+
// temporal-staleness counts CURATED entries only — auto-imported draft
|
|
55
|
+
// KEV-due-passed findings are calendar-drift noise (they once saturated a
|
|
56
|
+
// 260 budget). Curated actual ~20; 35 leaves headroom without the count
|
|
57
|
+
// breaching the budget purely as the calendar advances.
|
|
58
|
+
"temporal-staleness": 35,
|
|
55
59
|
"logical-consistency": 5,
|
|
56
60
|
"cross-ref-completeness": 5,
|
|
57
61
|
"schema-evolution": 0,
|
|
@@ -272,6 +272,15 @@ function extractCliSurface(content) {
|
|
|
272
272
|
let m;
|
|
273
273
|
while ((m = re.exec(playbookBlock[1])) !== null) verbs.add(m[1]);
|
|
274
274
|
}
|
|
275
|
+
// REMOVED_VERBS keys are still part of the CLI surface: invoking one returns
|
|
276
|
+
// a structured refusal envelope (a real, test-covered contract). Counting
|
|
277
|
+
// them keeps a verb in the surface set when its vestigial COMMANDS entry is
|
|
278
|
+
// dropped — otherwise removing dead COMMANDS table rows for already-retired
|
|
279
|
+
// verbs reads as a fresh "removed-but-test-remains" against the refusal test.
|
|
280
|
+
const removedBlock = content.match(/const REMOVED_VERBS = \{([\s\S]*?)\n\};/);
|
|
281
|
+
if (removedBlock) {
|
|
282
|
+
for (const m of removedBlock[1].matchAll(/^\s*"?([a-zA-Z][\w-]+)"?\s*:/gm)) verbs.add(m[1]);
|
|
283
|
+
}
|
|
275
284
|
const flagRe = /(--[a-zA-Z][\w-]+)/g;
|
|
276
285
|
let m;
|
|
277
286
|
while ((m = flagRe.exec(content)) !== null) flags.add(m[1]);
|
package/scripts/predeploy.js
CHANGED
|
@@ -68,7 +68,11 @@ const GATES = [
|
|
|
68
68
|
{
|
|
69
69
|
name: "Validate CVE catalog schema + zero-day learning coverage",
|
|
70
70
|
command: process.execPath,
|
|
71
|
-
|
|
71
|
+
// --strict promotes the deferred warning checks (cross-catalog ref
|
|
72
|
+
// resolution, strict CVSS-vector prefix, KEV-date-required, Hard-Rule-#14
|
|
73
|
+
// IoCs) to hard failures so they block a release rather than scrolling
|
|
74
|
+
// past. Auto-imported drafts stay exempt.
|
|
75
|
+
args: [path.join(ROOT, "lib", "validate-cve-catalog.js"), "--strict"],
|
|
72
76
|
ciJobName: "Data integrity (catalog + manifest snapshot)",
|
|
73
77
|
},
|
|
74
78
|
// the "validate-cves --offline --no-fail" and
|
|
@@ -89,7 +93,7 @@ const GATES = [
|
|
|
89
93
|
{
|
|
90
94
|
name: "Lint skill files",
|
|
91
95
|
command: process.execPath,
|
|
92
|
-
args: [path.join(ROOT, "lib", "lint-skills.js")],
|
|
96
|
+
args: [path.join(ROOT, "lib", "lint-skills.js"), "--strict"],
|
|
93
97
|
ciJobName: "Lint skill files",
|
|
94
98
|
},
|
|
95
99
|
{
|
|
@@ -113,7 +117,7 @@ const GATES = [
|
|
|
113
117
|
{
|
|
114
118
|
name: "Validate catalog _meta (tlp + source_confidence + freshness_policy)",
|
|
115
119
|
command: process.execPath,
|
|
116
|
-
args: [path.join(ROOT, "lib", "validate-catalog-meta.js")],
|
|
120
|
+
args: [path.join(ROOT, "lib", "validate-catalog-meta.js"), "--strict"],
|
|
117
121
|
ciJobName: "Data integrity (catalog + manifest snapshot)",
|
|
118
122
|
},
|
|
119
123
|
{
|
|
@@ -174,7 +178,7 @@ const GATES = [
|
|
|
174
178
|
// v0.13.0 additions) all validate cleanly.
|
|
175
179
|
name: "Validate playbooks (schema + cross-refs)",
|
|
176
180
|
command: process.execPath,
|
|
177
|
-
args: [path.join(ROOT, "lib", "validate-playbooks.js")],
|
|
181
|
+
args: [path.join(ROOT, "lib", "validate-playbooks.js"), "--strict"],
|
|
178
182
|
ciJobName: "Validate playbooks",
|
|
179
183
|
},
|
|
180
184
|
{
|