@blamejs/exceptd-skills 0.12.22 → 0.12.24
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/AGENTS.md +18 -12
- package/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +152 -2
- package/CONTEXT.md +126 -69
- package/README.md +21 -8
- package/bin/exceptd.js +972 -464
- package/data/_indexes/_meta.json +3 -3
- package/data/_indexes/stale-content.json +10 -3
- package/data/playbooks/ai-api.json +1 -1
- package/data/playbooks/containers.json +1 -1
- package/data/playbooks/cred-stores.json +1 -1
- package/data/playbooks/crypto-codebase.json +1 -1
- package/data/playbooks/crypto.json +1 -1
- package/data/playbooks/framework.json +1 -1
- package/data/playbooks/hardening.json +1 -1
- package/data/playbooks/kernel.json +1 -1
- package/data/playbooks/library-author.json +1 -1
- package/data/playbooks/mcp.json +1 -1
- package/data/playbooks/runtime.json +1 -1
- package/data/playbooks/sbom.json +1 -1
- package/data/playbooks/secrets.json +39 -1
- package/lib/auto-discovery.js +28 -4
- package/lib/cross-ref-api.js +12 -11
- package/lib/cve-curation.js +18 -19
- package/lib/exit-codes.js +72 -0
- package/lib/flag-suggest.js +130 -0
- package/lib/id-validation.js +95 -0
- package/lib/lint-skills.js +73 -6
- package/lib/playbook-runner.js +617 -343
- package/lib/prefetch.js +134 -21
- package/lib/refresh-external.js +205 -26
- package/lib/refresh-network.js +64 -16
- package/lib/schemas/cve-catalog.schema.json +7 -1
- package/lib/schemas/playbook.schema.json +51 -0
- package/lib/scoring.js +49 -7
- package/lib/sign.js +10 -11
- package/lib/source-osv.js +7 -7
- package/lib/upstream-check-cli.js +16 -1
- package/lib/upstream-check.js +9 -0
- package/lib/validate-catalog-meta.js +1 -1
- package/lib/validate-cve-catalog.js +1 -1
- package/lib/verify.js +56 -30
- package/manifest.json +40 -40
- package/package.json +8 -2
- package/sbom.cdx.json +6 -6
- package/scripts/check-test-coverage.js +67 -0
- package/scripts/verify-shipped-tarball.js +27 -18
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-15T18:45:49.423Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
5
|
"source_count": 50,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
7
|
+
"manifest.json": "557bf7b459de4fb6af3d0bbad86626bf76581f44e31c30e240682ca87dbb9f69",
|
|
8
8
|
"data/atlas-ttps.json": "20339e0ae3cd89c06f1385be31c50f408f827edc2e8ab8aef026ade3bcf0a917",
|
|
9
9
|
"data/attack-techniques.json": "6db08a8e8a4d03d9309b1d185112de7f3c9595d2cd3d24566b7ce0b3b8aa5d1a",
|
|
10
10
|
"data/cve-catalog.json": "7936ba3c8f27156235bf327830e8f1a684658865e97f089aed98b2a7cdbb88ef",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"frequency_fields": 7,
|
|
84
84
|
"activity_feed_events": 50,
|
|
85
85
|
"catalog_summaries": 11,
|
|
86
|
-
"stale_content_findings":
|
|
86
|
+
"stale_content_findings": 1
|
|
87
87
|
},
|
|
88
88
|
"invalidation_note": "If any source file in source_hashes has a different SHA-256 than recorded here, the indexes are stale. Re-run `npm run build-indexes`."
|
|
89
89
|
}
|
|
@@ -3,12 +3,19 @@
|
|
|
3
3
|
"schema_version": "1.0.0",
|
|
4
4
|
"reference_date": "2026-05-01",
|
|
5
5
|
"note": "Stale-content snapshot derived from audit-cross-skill checks. Re-runs of build-indexes against the same inputs produce byte-identical output (reference_date is manifest.threat_review_date, not 'now'). audit-cross-skill.js remains the canonical interactive audit.",
|
|
6
|
-
"finding_count":
|
|
6
|
+
"finding_count": 1,
|
|
7
7
|
"by_severity": {
|
|
8
8
|
"high": 0,
|
|
9
|
-
"medium":
|
|
9
|
+
"medium": 1,
|
|
10
10
|
"low": 0
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
-
"findings": [
|
|
13
|
+
"findings": [
|
|
14
|
+
{
|
|
15
|
+
"severity": "medium",
|
|
16
|
+
"category": "badge_drift",
|
|
17
|
+
"artifact": "README.md",
|
|
18
|
+
"detail": "jurisdictions badge shows 35, live count is 34"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
14
21
|
}
|
|
@@ -297,7 +297,7 @@
|
|
|
297
297
|
"monitor": 40,
|
|
298
298
|
"close": 25
|
|
299
299
|
},
|
|
300
|
-
"framework_lag_declaration": "NIST 800-53 SI-3/SC-7/AC-2, ISO 27001:2022 A.8.16, SOC 2 CC6/CC7, EU AI Act Art.15 are all structurally insufficient for AI-API C2. The shared failure: each control treats the AI API endpoint as a legitimate authorized SaaS, the service account using it as authorized, and the traffic over it as business-as-usual. The SesameOp pattern operates entirely inside that authorized envelope. Until frameworks add 'AI-API egress baseline + content inspection + bearer-token-to-process attribution' controls, anomaly-detection audit opinions provide zero signal about AI-as-C2 exposure. Companion gap: secrets-management controls (IA-5, A.8.5) focus on repository-committed keys and ignore the dotfile credential surface where most developer keys actually live. Lag = ~190 days behind the SesameOp pattern's first documentation; no framework body has issued draft language as of 2026-05-11.",
|
|
300
|
+
"framework_lag_declaration": "NIST 800-53 SI-3/SC-7/AC-2, ISO 27001:2022 A.8.16, SOC 2 CC6/CC7, EU AI Act Art.15 are all structurally insufficient for AI-API C2. The shared failure: each control treats the AI API endpoint as a legitimate authorized SaaS, the service account using it as authorized, and the traffic over it as business-as-usual. The SesameOp pattern operates entirely inside that authorized envelope. Until frameworks add 'AI-API egress baseline + content inspection + bearer-token-to-process attribution' controls, anomaly-detection audit opinions provide zero signal about AI-as-C2 exposure. Companion gap: secrets-management controls (IA-5, A.8.5) focus on repository-committed keys and ignore the dotfile credential surface where most developer keys actually live. Lag = ~190 days behind the SesameOp pattern's first documentation; no framework body has issued draft language as of 2026-05-11. UK CAF Principles B3 (Data Security) and B4 (System Security) treat egress monitoring and trust-boundary enforcement as outcomes without naming AI-API endpoints as a distinct exfiltration channel — B3/B4 evidence passes while authorized-SaaS C2 channels stay invisible. AU Essential 8 ML2 Application Control (E8 M.1) + Restrict Admin Privileges (E8 M.5) and ACSC ISM-1546 cover process-lineage and admin-privilege restriction, but Essential 8 does not yet enumerate AI-API endpoints in its allowlist guidance — operators who allow AI SaaS at the proxy retain no per-token attribution.",
|
|
301
301
|
"skill_chain": [
|
|
302
302
|
{
|
|
303
303
|
"skill": "ai-c2-detection",
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
"monitor": 65,
|
|
258
258
|
"close": 35
|
|
259
259
|
},
|
|
260
|
-
"framework_lag_declaration": "NIST 800-190 + CIS K8s Benchmark + NIST 800-53 CM-7 + ISO A.8.9 + PCI Req.2.2 collectively cover container hardening but accept cluster-wide attestation as evidence. None require per-manifest analysis as a control. Manifest drift in GitOps repos can introduce privileged: true / hostPID / unscoped capabilities faster than any attestation cadence. EU CRA + NIST SSDF + SLSA cover supply-chain posture but most orgs are at SLSA L1-L2 without digest pinning. Gap = ~21 days between manifest drift and review cadence; per-CVE gap (e.g. Leaky Vessels) is hours from public PoC to weaponization vs. weeks of node-staging windows.",
|
|
260
|
+
"framework_lag_declaration": "NIST 800-190 + CIS K8s Benchmark + NIST 800-53 CM-7 + ISO A.8.9 + PCI Req.2.2 collectively cover container hardening but accept cluster-wide attestation as evidence. None require per-manifest analysis as a control. Manifest drift in GitOps repos can introduce privileged: true / hostPID / unscoped capabilities faster than any attestation cadence. EU CRA + NIST SSDF + SLSA cover supply-chain posture but most orgs are at SLSA L1-L2 without digest pinning. Gap = ~21 days between manifest drift and review cadence; per-CVE gap (e.g. Leaky Vessels) is hours from public PoC to weaponization vs. weeks of node-staging windows. UK CAF Principles B4 (System Security) and B5 (Resilient Networks & Systems) treat container-runtime hardening as an outcome assessable via attestation, without binding evidence to per-manifest privileged-flag or host-namespace exposure. AU Essential 8 Strategy 1 (Application Control, E8 M.1 ML2) covers execution allowlisting on workstations/servers but does not bind to Kubernetes admission-controller policy: privileged-pod denial via OPA/Kyverno or PodSecurity `restricted` admission is the operational analogue, and Essential 8 has no maturity-level requirement enforcing it. ACSC ISM-1546 and ISM-1683 (workload isolation) name workload separation but stop short of banning privileged + hostPID + hostNetwork in cluster manifests as an attested admission-time gate.",
|
|
261
261
|
"skill_chain": [
|
|
262
262
|
{
|
|
263
263
|
"skill": "container-runtime-security",
|
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
"monitor": 60,
|
|
237
237
|
"close": 30
|
|
238
238
|
},
|
|
239
|
-
"framework_lag_declaration": "NIST 800-63b AAL specs, NIST 800-53 IA-2/IA-5, ISO A.5.16/A.5.17, NIS2 Art.21(2)(j), PCI-DSS Req.8 collectively specify target authenticator properties (phishing-resistant, short-lived, MFA-gated). They do not require enumeration of credentials actually present on developer endpoints. SSO + MFA attestation is accepted as evidence even when local credential stores hold long-lived bearer tokens that bypass SSO. Gap = ~25 days between credential-store drift (new PAT issued, new long-lived AWS key created) and any framework's review cadence.",
|
|
239
|
+
"framework_lag_declaration": "NIST 800-63b AAL specs, NIST 800-53 IA-2/IA-5, ISO A.5.16/A.5.17, NIS2 Art.21(2)(j), PCI-DSS Req.8 collectively specify target authenticator properties (phishing-resistant, short-lived, MFA-gated). They do not require enumeration of credentials actually present on developer endpoints. SSO + MFA attestation is accepted as evidence even when local credential stores hold long-lived bearer tokens that bypass SSO. Gap = ~25 days between credential-store drift (new PAT issued, new long-lived AWS key created) and any framework's review cadence. UK CAF Principle B2 (Identity and Access Control) defines authentication outcomes and credential-management practice, but does not require enumeration of credentials persisted in local credential stores outside the SSO/IdP envelope. AU Essential 8 ML2 MFA (E8 M.4) + Restrict Admin Privileges (E8 M.5) and ACSC ISM-1546 cover authenticator strength and admin-privilege scope but treat long-lived bearer tokens stored in dotfiles / OS keychains as out of frame — Essential 8 evidence passes while landed-attacker credential harvest stays trivial.",
|
|
240
240
|
"skill_chain": [
|
|
241
241
|
{
|
|
242
242
|
"skill": "identity-assurance",
|
|
@@ -322,7 +322,7 @@
|
|
|
322
322
|
"monitor": 45,
|
|
323
323
|
"close": 25
|
|
324
324
|
},
|
|
325
|
-
"framework_lag_declaration": "Frameworks structurally bind the OPERATING organization, not the library author. NIST 800-53 SC-13, ISO 27001:2022 A.8.24/A.8.28, PCI DSS 4.0 §3.6/§8.3.2 all push obligations downstream to consumers who inherit the shipped defaults. EU CRA Annex I §1 is the first framework with direct upstream obligations on manufacturers of products with digital elements, but its binding date for vulnerability-handling and SBOM provisions is 2026-09-11 (four months from now) with full compliance 2027-12-11. NIS2 Art.21(2)(g) exerts indirect pressure via essential-entity consumers. UK CAF C.5 + UK PSTI covers connected products only. ISO 27001:2022 A.8.28 'secure coding' is silent on PQC, KDF iteration minimums, RNG-source requirements, constant-time-implementation requirements — the longest structural laggard. NIST IR 8547 + OMB M-23-02 + CNSA 2.0 push federal-system PQC migration through 2030 but rely on procurement pressure to flow upstream. Gap = ~365 days from operational PQC readiness (2024-08-13 FIPS finalization) to binding library-author obligations (2026-09-11 EU CRA partial bind). Compensating controls (downstream-consumer adoption pressure, supply-chain auditing tools, voluntary SECURITY.md disclosure of cryptographic provenance) must close this gap pending EU CRA enforcement.",
|
|
325
|
+
"framework_lag_declaration": "Frameworks structurally bind the OPERATING organization, not the library author. NIST 800-53 SC-13, ISO 27001:2022 A.8.24/A.8.28, PCI DSS 4.0 §3.6/§8.3.2 all push obligations downstream to consumers who inherit the shipped defaults. EU CRA Annex I §1 is the first framework with direct upstream obligations on manufacturers of products with digital elements, but its binding date for vulnerability-handling and SBOM provisions is 2026-09-11 (four months from now) with full compliance 2027-12-11. NIS2 Art.21(2)(g) exerts indirect pressure via essential-entity consumers. UK CAF C.5 + UK PSTI covers connected products only. ISO 27001:2022 A.8.28 'secure coding' is silent on PQC, KDF iteration minimums, RNG-source requirements, constant-time-implementation requirements — the longest structural laggard. NIST IR 8547 + OMB M-23-02 + CNSA 2.0 push federal-system PQC migration through 2030 but rely on procurement pressure to flow upstream. Gap = ~365 days from operational PQC readiness (2024-08-13 FIPS finalization) to binding library-author obligations (2026-09-11 EU CRA partial bind). Compensating controls (downstream-consumer adoption pressure, supply-chain auditing tools, voluntary SECURITY.md disclosure of cryptographic provenance) must close this gap pending EU CRA enforcement. AU Essential 8 ML2 Patch Applications (E8 M.2) plus ACSC ISM-1138 (cryptographic-algorithm transition), ISM-0467 (approved cryptographic algorithms), and ISM-0471 (cryptographic protocol selection) reference downstream consumer migration but place no obligation on upstream library authors to ship PQC-by-default, KDF iteration minima, or constant-time implementations — Essential 8 evidence flows from operating-org posture, not from library-publisher posture. UK CAF Principle C.5 (System Security — outcome-tested cryptographic deployments) and UK PSTI together lag because CAF C.5 mandates outcome-tested cryptographic deployments at the operator but does not require library authors to ship PQC-by-default, constant-time implementations, or KDF iteration minima; PSTI scope is connected products only and excludes upstream library distribution entirely. The library-author surface therefore sits in a coverage seam between CAF (operator-side) and PSTI (finished-product-side) — neither binds the upstream publisher.",
|
|
326
326
|
"skill_chain": [
|
|
327
327
|
{
|
|
328
328
|
"skill": "pqc-first",
|
|
@@ -288,7 +288,7 @@
|
|
|
288
288
|
"monitor": 45,
|
|
289
289
|
"close": 25
|
|
290
290
|
},
|
|
291
|
-
"framework_lag_declaration": "Every framework in scope is structurally insufficient for HNDL. NIST 800-53 SC-8/SC-13, ISO 27001:2022 A.8.24/A.8.25, PCI DSS 4.0 §3.6/§4.2.1, NIS2 Art.21(2)(h), DORA Art.9, EU CRA Annex I all permit fully-classical cryptographic posture as 'strong cryptography'. NIST itself is the exception: FIPS 203/204/205 are finalized, NIST IR 8547 is a published migration roadmap, OMB M-23-02 mandates federal PQC inventory — but the 800-53 control catalog is unchanged. ISO 27001:2022 was published before PQC finalization and has no scheduled amendment. PCI Council and EU regulators are publicly aware but have not amended binding controls. Lag = ~180 days behind operational readiness (PQC has been production-ready since 2024-08-13) and 4-8+ years behind the CRQC horizon that drives the harvest-now-decrypt-later attack. Compensating controls (crypto-agility, hybrid algorithms, layered encryption envelopes) must close this gap before SLA-only compliance can be accepted.",
|
|
291
|
+
"framework_lag_declaration": "Every framework in scope is structurally insufficient for HNDL. NIST 800-53 SC-8/SC-13, ISO 27001:2022 A.8.24/A.8.25, PCI DSS 4.0 §3.6/§4.2.1, NIS2 Art.21(2)(h), DORA Art.9, EU CRA Annex I all permit fully-classical cryptographic posture as 'strong cryptography'. NIST itself is the exception: FIPS 203/204/205 are finalized, NIST IR 8547 is a published migration roadmap, OMB M-23-02 mandates federal PQC inventory — but the 800-53 control catalog is unchanged. ISO 27001:2022 was published before PQC finalization and has no scheduled amendment. PCI Council and EU regulators are publicly aware but have not amended binding controls. Lag = ~180 days behind operational readiness (PQC has been production-ready since 2024-08-13) and 4-8+ years behind the CRQC horizon that drives the harvest-now-decrypt-later attack. Compensating controls (crypto-agility, hybrid algorithms, layered encryption envelopes) must close this gap before SLA-only compliance can be accepted. UK CAF Principles B3 (Data Security) and B4 (System Security) treat cryptography as an outcome (data-in-transit + data-at-rest protection) without naming PQC migration or HNDL exposure as a B3/B4 requirement — CAF evidence passes against fully-classical posture. AU Essential 8 ML2 Patch Applications + Patch Operating Systems (E8 M.2) plus ACSC ISM-1138 (cryptographic-algorithm transition) and ISM-0467 (approved cryptographic algorithms) reference algorithm-suite policy, but ISM transition guidance remains advisory in 2026-05 and Essential 8 maturity levels do not bind PQC inventory + migration deadlines.",
|
|
292
292
|
"skill_chain": [
|
|
293
293
|
{
|
|
294
294
|
"skill": "pqc-first",
|
|
@@ -320,7 +320,7 @@
|
|
|
320
320
|
"monitor": 40,
|
|
321
321
|
"close": 20
|
|
322
322
|
},
|
|
323
|
-
"framework_lag_declaration": "All 20 frameworks listed in domain.frameworks_in_scope are structurally insufficient for at least one upstream-playbook threat class. ISO 27001:2022, SOC 2 TSC, and PCI DSS 4.0 are the longest-laggard for AI/MCP/PQC threats (no scheduled amendments). NIST 800-53, NIS2, DORA, EU AI Act, and EU CRA have publishing cadences but lag the threat tempo by 90-365 days. UK CAF (outcome-based) and AU Essential 8 are partially forward-compatible but inconsistent across regulators/sectors. SG MAS TRM, JP FISC, IN CERT-In, CA OSFI B-10 are sector- and jurisdiction-specific with tempo varying by sector. Compound effect: an org running all current threat-class exposures under a single audit opinion is the modal state in mid-2026, not an outlier.",
|
|
323
|
+
"framework_lag_declaration": "All 20 frameworks listed in domain.frameworks_in_scope are structurally insufficient for at least one upstream-playbook threat class. ISO 27001:2022, SOC 2 TSC, and PCI DSS 4.0 are the longest-laggard for AI/MCP/PQC threats (no scheduled amendments). NIST 800-53, NIS2, DORA, EU AI Act, and EU CRA have publishing cadences but lag the threat tempo by 90-365 days. UK CAF (outcome-based) and AU Essential 8 are partially forward-compatible but inconsistent across regulators/sectors. SG MAS TRM, JP FISC, IN CERT-In, CA OSFI B-10 are sector- and jurisdiction-specific with tempo varying by sector. Compound effect: an org running all current threat-class exposures under a single audit opinion is the modal state in mid-2026, not an outlier. Named per-framework lags: NIST 800-53 CA-7 (Continuous Monitoring) lags because it is designed for control-effectiveness assessment and does not require correlation across frameworks to surface paper-vs-actual evidence. EU NIS2 Art.21(2) lags because it enumerates 10 risk-management categories but does not require operators to correlate their asset inventory against EU AI Act risk classifications when AI systems are present. UK CAF Principle A (Governance) lags because it is designed to assess governance-of-cyber-resilience outcomes and does not require correlation of `outcome-test` failures across CAF outcomes to surface compliance-theater. AU Essential 8 Strategy 1 (Application Control, ML2) lags because it is designed for execution allowlisting on workstations/servers and does not reach AI-tool-and-MCP-server allowlisting as a sub-class of executable trust. ISO/IEC 27001:2022 A.5.1 (Policies for information security) lags because it requires top-level policy presence and does not require cross-framework gap analysis as part of policy maintenance.",
|
|
324
324
|
"skill_chain": [
|
|
325
325
|
{
|
|
326
326
|
"skill": "framework-gap-analysis",
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
"monitor": 65,
|
|
227
227
|
"close": 30
|
|
228
228
|
},
|
|
229
|
-
"framework_lag_declaration": "NIST CM-6, ISO A.8.9, Essential 8 OS Hardening, CMMC CM.L2-3.4.1/2 all permit baseline + change-management evidence without requiring continuous attestation of the specific kernel hardening flags (kptr_restrict, unprivileged_userns_clone, unprivileged_bpf_disabled, yama.ptrace_scope, dmesg_restrict, kernel.lockdown, MAC enforcement mode) that determine whether a vulnerable kernel is actually exploitable. Gap = ~21 days at typical orgs between drift event and review. For environments using GitOps-deployed kernel parameters, drift can occur faster than monitoring catches.",
|
|
229
|
+
"framework_lag_declaration": "NIST CM-6, ISO A.8.9, Essential 8 OS Hardening, CMMC CM.L2-3.4.1/2 all permit baseline + change-management evidence without requiring continuous attestation of the specific kernel hardening flags (kptr_restrict, unprivileged_userns_clone, unprivileged_bpf_disabled, yama.ptrace_scope, dmesg_restrict, kernel.lockdown, MAC enforcement mode) that determine whether a vulnerable kernel is actually exploitable. Gap = ~21 days at typical orgs between drift event and review. For environments using GitOps-deployed kernel parameters, drift can occur faster than monitoring catches. UK CAF Principles B4 (System Security) and B5 (Resilient Networks & Systems) treat OS hardening as a system-security outcome assessable via baseline-and-change-management evidence, without binding to continuous attestation of specific kernel hardening flags (kptr_restrict, unprivileged_userns_clone, unprivileged_bpf_disabled, yama.ptrace_scope, dmesg_restrict, kernel.lockdown, MAC enforcement) — CAF evidence passes against a host whose flags drifted hours after the last review. AU Essential 8 ML2 Patch Operating Systems (E8 M.2) + Restrict Admin Privileges (E8 M.5) and ACSC ISM-1144 / ISM-1493 (vulnerability management) reference OS-hardening posture but do not enumerate the per-sysctl flag set whose state determines whether a vulnerable kernel is actually exploitable. EU NIS2 Art.21(2)(c) `secure development lifecycle` and DORA Art.9(4) `secure configuration` both reference hardening posture obligation but stop short of requiring per-flag kernel-hardening attestation; the gap is that operators can claim compliance with `vm.mmap_min_addr` defaulted-but-not-attested, because neither framework requires the per-sysctl evidence that distinguishes a hardened kernel from a default-shipped one.",
|
|
230
230
|
"skill_chain": [
|
|
231
231
|
{
|
|
232
232
|
"skill": "kernel-lpe-triage",
|
|
@@ -224,7 +224,7 @@
|
|
|
224
224
|
"monitor": 70,
|
|
225
225
|
"close": 30
|
|
226
226
|
},
|
|
227
|
-
"framework_lag_declaration": "NIST SI-2 + ISO A.8.8 + NIS2 Art.21(2)(c) permit 30-day patch SLAs that are inadequate for KEV-listed kernel LPEs with confirmed exploitation. NIST 800-53 Rev. 5.1.1 does not require KEV-aware prioritization, only risk-based — leaving it to implementer interpretation. Real-world tempo: weaponization in hours, patch SLA in weeks. Gap = ~28 days. Compensating controls (live-patch, MAC, kernel hardening) MUST close this gap before SLA-only compliance can be accepted.",
|
|
227
|
+
"framework_lag_declaration": "NIST SI-2 + ISO A.8.8 + NIS2 Art.21(2)(c) permit 30-day patch SLAs that are inadequate for KEV-listed kernel LPEs with confirmed exploitation. NIST 800-53 Rev. 5.1.1 does not require KEV-aware prioritization, only risk-based — leaving it to implementer interpretation. Real-world tempo: weaponization in hours, patch SLA in weeks. Gap = ~28 days. Compensating controls (live-patch, MAC, kernel hardening) MUST close this gap before SLA-only compliance can be accepted. UK CAF Principles B4 (System Security) and B5 (Resilient Networks & Systems) treat patching as an outcome without binding to KEV-aware tempo; CAF evidence passes against the 30-day SLA that real-world weaponization beats. AU Essential 8 ML2 / ML3 Patch Operating Systems (E8 M.2) sets a 48-hour patch target for internet-facing services with known exploitation, and ACSC ISM-1144 / ISM-1493 (vulnerability management + critical patching) name kernel patching specifically — but Essential 8 maturity adoption surveys show ML3 attainment <15% across regulated AU entities, leaving the structural gap operational even where the framework anticipates it.",
|
|
228
228
|
"skill_chain": [
|
|
229
229
|
{
|
|
230
230
|
"skill": "kernel-lpe-triage",
|
|
@@ -477,7 +477,7 @@
|
|
|
477
477
|
"monitor": 50,
|
|
478
478
|
"close": 30
|
|
479
479
|
},
|
|
480
|
-
"framework_lag_declaration": "NIST 800-53 SR-3/4/5, NIST 800-218 SSDF PS.3.2 / PW.4 / RV.2, ISO 27001:2022 A.8.30 / A.5.20, SOC 2 CC8.1 / CC9.2, NIS2 Art.21(2)(d), DORA Art.28,
|
|
480
|
+
"framework_lag_declaration": "NIST 800-53 SR-3/4/5, NIST 800-218 SSDF PS.3.2 / PW.4 / RV.2, ISO 27001:2022 A.8.30 / A.5.20, SOC 2 CC8.1 / CC9.2, NIS2 Art.21(2)(d), DORA Art.28, and CMMC SI.L2-3.4.4 all permit publisher posture that omits (a) OIDC-based publish, (b) Sigstore / Rekor transparency-log entries per release, (c) signed + distributed SBOM, (d) transitive completeness, (e) VEX feed for filed CVEs, (f) coordinated-disclosure intake at /.well-known/security.txt, (g) tag-protection on release refs, (h) reproducible builds, (i) skill / plugin signature verification gated in the install path. Operational tooling for (a)-(i) shipped 2023-2025; framework lag = ~540 days behind tooling and ~18 months ahead of EU CRA Art.10/13/14 binding (2027). UK CAF C1.b (Identity and Access Management — Privileged user management) is designed to govern human privileged users and does not require SLSA L3+ provenance attestation on every release artifact; the gap is that a compromised publisher token (no human-MFA gate, no provenance attestation) yields an attacker-signed release that passes C1.b's evidence surface. AU Essential 8 Strategy 5 (Restrict Admin Privileges, E8 M.5) is designed for runtime admin-account scoping and does not reach build-time admin privileges on signing-key material: a CI runner holding the publish credential is a build-time admin that E8 M.5 evidence does not enumerate. Compensating controls MUST close (a)-(i) before SSDF-only compliance can be accepted as CRA-readiness.",
|
|
481
481
|
"skill_chain": [
|
|
482
482
|
{
|
|
483
483
|
"skill": "supply-chain-integrity",
|
package/data/playbooks/mcp.json
CHANGED
|
@@ -326,7 +326,7 @@
|
|
|
326
326
|
"monitor": 50,
|
|
327
327
|
"close": 25
|
|
328
328
|
},
|
|
329
|
-
"framework_lag_declaration": "Every framework in scope (NIST 800-53 SA-12/CM-7, ISO 27001:2022 A.5.19/A.5.20/A.8.30, SOC 2 CC9, NIS2 Art.21(2)(d), EU AI Act Art.15, EU CRA Art.13) is structurally insufficient. The shared structural failure: every control treats third-party software risk as a procurement event with a discrete vendor list, while MCP servers are continuously sideloaded by developers from package registries with no procurement signal, no enforced signing, and no allowlist that survives version drift. Until frameworks add a 'developer-installed AI tool plugin' control category with mandatory manifest signature verification + version pinning with integrity hash + provenance attestation, vendor-management audit opinions provide zero signal about MCP exposure. Gap = ~210 days behind operational reality; no framework body has issued draft language as of 2026-05-11.",
|
|
329
|
+
"framework_lag_declaration": "Every framework in scope (NIST 800-53 SA-12/CM-7, ISO 27001:2022 A.5.19/A.5.20/A.8.30, SOC 2 CC9, NIS2 Art.21(2)(d), EU AI Act Art.15, EU CRA Art.13) is structurally insufficient. The shared structural failure: every control treats third-party software risk as a procurement event with a discrete vendor list, while MCP servers are continuously sideloaded by developers from package registries with no procurement signal, no enforced signing, and no allowlist that survives version drift. Until frameworks add a 'developer-installed AI tool plugin' control category with mandatory manifest signature verification + version pinning with integrity hash + provenance attestation, vendor-management audit opinions provide zero signal about MCP exposure. Gap = ~210 days behind operational reality; no framework body has issued draft language as of 2026-05-11. UK CAF Principles B3 (Data Security) and B4 (System Security) treat third-party tooling as a procurement-and-supply-chain assurance outcome, with no Principle that names developer-installed AI tool plugins as a distinct surface — CAF B3/B4 evidence remains silent on MCP-server sideload. AU Essential 8 ML2 Application Control (E8 M.1) + Restrict Admin Privileges (E8 M.5) plus ACSC ISM-1490 (application control) and ISM-1657 (supply chain) cover application-execution allowlisting in principle, but Essential 8 maturity guidance assumes a discrete vendor list and does not enumerate registry-pulled MCP servers as application-control scope.",
|
|
330
330
|
"skill_chain": [
|
|
331
331
|
{
|
|
332
332
|
"skill": "mcp-agent-trust",
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
"monitor": 65,
|
|
231
231
|
"close": 35
|
|
232
232
|
},
|
|
233
|
-
"framework_lag_declaration": "NIST CM-7 + AC-6, ISO A.8.2 + A.8.18, and NIS2 Art.21(2)(i) treat least-privilege and least-functionality as policy outcomes reviewable annually. They do not require programmatic enumeration of the specific primitives (sudoers NOPASSWD, non-baseline SUID, world-writable cron, host-mounted systemd services, listening sockets on non-loopback) that attackers use to convert foothold into root. A host can be CM-7/AC-6 compliant on paper while presenting dozens of LPE primitives to a landed attacker. Gap = ~21 days between drift and policy review at typical orgs; for fast-moving environments (containerized + GitOps-deployed), drift accumulates faster than annual review can catch.",
|
|
233
|
+
"framework_lag_declaration": "NIST CM-7 + AC-6, ISO A.8.2 + A.8.18, and NIS2 Art.21(2)(i) treat least-privilege and least-functionality as policy outcomes reviewable annually. They do not require programmatic enumeration of the specific primitives (sudoers NOPASSWD, non-baseline SUID, world-writable cron, host-mounted systemd services, listening sockets on non-loopback) that attackers use to convert foothold into root. A host can be CM-7/AC-6 compliant on paper while presenting dozens of LPE primitives to a landed attacker. Gap = ~21 days between drift and policy review at typical orgs; for fast-moving environments (containerized + GitOps-deployed), drift accumulates faster than annual review can catch. UK CAF Principles B4 (System Security) and B5 (Resilient Networks & Systems) treat least-privilege as a system-security outcome assessable through control-design evidence, without binding to per-host enumeration of LPE primitives (sudoers NOPASSWD, non-baseline SUID, writable cron, host-mount services). AU Essential 8 ML2 Restrict Admin Privileges (E8 M.5) + Patch Operating Systems (E8 M.2) plus ACSC ISM-1175 (privilege management) and ISM-1490 (application control) cover admin-account and application-execution policy, but Essential 8 maturity evidence does not enumerate the specific runtime primitives that convert foothold into root.",
|
|
234
234
|
"skill_chain": [
|
|
235
235
|
{
|
|
236
236
|
"skill": "attack-surface-pentest",
|
package/data/playbooks/sbom.json
CHANGED
|
@@ -390,7 +390,7 @@
|
|
|
390
390
|
"monitor": 50,
|
|
391
391
|
"close": 30
|
|
392
392
|
},
|
|
393
|
-
"framework_lag_declaration": "Supply-chain frameworks have advanced fastest of any class in this exceptd release, but implementation gaps are pervasive. NIST 800-53 SA-12, NIST 800-218 SSDF, ISO 27001:2022 A.8.30/A.5.20, SOC 2 CC9.2, PCI DSS 4.0 sect.6.3, NIS2 Art.21(2)(d), DORA Art.28, EU CRA Art.13/14 all permit (a) SBOM as deliverable without continuous correlation, (b) lockfile-pinning without integrity hash, (c) direct-deps-only SBOM, (d) absence of VEX statements, (e) absence of AI-generated-code provenance, (f) model weights treated as content blobs. Lag = ~90 days behind operational tooling (SLSA / Sigstore / in-toto / VEX-CSAF) and ~210 days behind AI-coding-assistant adoption tempo. Compensating controls (continuous CVE correlation, integrity-pinned lockfiles, transitive SBOM completeness, VEX maintenance, AI-code provenance, model weight signature verification + safetensors-only loading) MUST close the gap before SBOM-as-deliverable compliance can be accepted.",
|
|
393
|
+
"framework_lag_declaration": "Supply-chain frameworks have advanced fastest of any class in this exceptd release, but implementation gaps are pervasive. NIST 800-53 SA-12, NIST 800-218 SSDF, ISO 27001:2022 A.8.30/A.5.20, SOC 2 CC9.2, PCI DSS 4.0 sect.6.3, NIS2 Art.21(2)(d), DORA Art.28, EU CRA Art.13/14 all permit (a) SBOM as deliverable without continuous correlation, (b) lockfile-pinning without integrity hash, (c) direct-deps-only SBOM, (d) absence of VEX statements, (e) absence of AI-generated-code provenance, (f) model weights treated as content blobs. Lag = ~90 days behind operational tooling (SLSA / Sigstore / in-toto / VEX-CSAF) and ~210 days behind AI-coding-assistant adoption tempo. Compensating controls (continuous CVE correlation, integrity-pinned lockfiles, transitive SBOM completeness, VEX maintenance, AI-code provenance, model weight signature verification + safetensors-only loading) MUST close the gap before SBOM-as-deliverable compliance can be accepted. UK CAF Principles B5 (Resilient Networks & Systems) and D1 (Response and Recovery Planning) treat supply-chain assurance and recovery readiness as outcomes, with no Principle binding continuous CVE correlation against the shipped SBOM or VEX maintenance as evidence. AU Essential 8 ML2 Patch Applications (E8 M.2) plus ACSC ISM-0717 (supply-chain risk management) and ISM-1657 (vendor assessment) cover supply-chain due diligence, but Essential 8 patch-tempo evidence accepts vendor advisories without requiring SBOM-driven transitive-CVE correlation or AI-generated-code provenance.",
|
|
394
394
|
"skill_chain": [
|
|
395
395
|
{
|
|
396
396
|
"skill": "supply-chain-integrity",
|
|
@@ -76,6 +76,8 @@
|
|
|
76
76
|
"nis2",
|
|
77
77
|
"dora",
|
|
78
78
|
"uk-caf",
|
|
79
|
+
"au-ism",
|
|
80
|
+
"au-essential-8",
|
|
79
81
|
"hipaa"
|
|
80
82
|
]
|
|
81
83
|
},
|
|
@@ -138,6 +140,18 @@
|
|
|
138
140
|
"california_resident_records_affected",
|
|
139
141
|
"containment_record"
|
|
140
142
|
]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"jurisdiction": "AU",
|
|
146
|
+
"regulation": "Privacy Act 1988 — Notifiable Data Breaches scheme (s26WK)",
|
|
147
|
+
"obligation": "notify_regulator",
|
|
148
|
+
"window_hours": 720,
|
|
149
|
+
"clock_starts": "analyze_complete",
|
|
150
|
+
"evidence_required": [
|
|
151
|
+
"secret_inventory",
|
|
152
|
+
"australian_resident_records_affected",
|
|
153
|
+
"remediation_completed_evidence"
|
|
154
|
+
]
|
|
141
155
|
}
|
|
142
156
|
],
|
|
143
157
|
"theater_fingerprints": [
|
|
@@ -210,6 +224,30 @@
|
|
|
210
224
|
"control_id": "B3 — Data security",
|
|
211
225
|
"designed_for": "NCSC CAF outcome that data important to the essential function is protected from compromise, including credential and key material.",
|
|
212
226
|
"insufficient_because": "Outcome can be assessed against managed secret-store contents. Plaintext credentials leaked into source-code repositories, CI logs, IaC files, and AI assistant context windows do not register on the outcome's evidence surface."
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"framework": "au-essential-8",
|
|
230
|
+
"control_id": "Strategy 4 — Multi-factor authentication (E8 M.4)",
|
|
231
|
+
"designed_for": "MFA on privileged + internet-facing accounts.",
|
|
232
|
+
"insufficient_because": "MFA defends the interactive auth flow; bearer tokens / API keys / OAuth refresh tokens committed to source artifacts bypass MFA entirely — scraper-bot exploitation against cloud-provider APIs uses the static credential and never reaches an interactive auth surface. Compliance-theater test: audit the last 90 days of admin actions and count those executed by service-account tokens vs human-MFA sessions; if service tokens dominate, Strategy 4 compliance is paper."
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"framework": "au-essential-8",
|
|
236
|
+
"control_id": "Strategy 1 — Application Control (E8 M.1)",
|
|
237
|
+
"designed_for": "Execution allowlisting on workstations and servers.",
|
|
238
|
+
"insufficient_because": "Application Control governs runtime execution but does not constrain build agents reading from arbitrary secret stores or environment variables at build time — CI runners with broad env-var access defeat the intent. Compliance-theater test: inventory CI runner environment variables; any token-shaped string (regex /[A-Za-z0-9]{32,}/) means ML2 compliance leaks at build time."
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"framework": "au-ism",
|
|
242
|
+
"control_id": "ISM-1546",
|
|
243
|
+
"designed_for": "Multi-factor authentication for privileged users and remote access.",
|
|
244
|
+
"insufficient_because": "ISM-1546 covers human authentication; CI/CD service identities (which now hold the actual privileges) are out of scope. Stolen GitHub Actions OIDC tokens grant identical blast radius without ever crossing the human-MFA gate. Compliance-theater test: list CI runner OIDC token TTLs and audience scopes; any token with TTL > 1h or wildcard audience defeats ISM-1546's intent."
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"framework": "au-ism",
|
|
248
|
+
"control_id": "ISM-1559",
|
|
249
|
+
"designed_for": "Privileged account credential management.",
|
|
250
|
+
"insufficient_because": "ISM-1559 names key/credential storage but treats source-code-embedded credentials as a policy violation, not a detection control. Modern exfiltration reads credentials post-decryption from /proc, container layers, or build artifacts. Compliance-theater test: run a process listing inside one production container and grep env for tokens; any hit means storage encryption was bypassed at the runtime boundary."
|
|
213
251
|
}
|
|
214
252
|
]
|
|
215
253
|
},
|
|
@@ -227,7 +265,7 @@
|
|
|
227
265
|
"monitor": 60,
|
|
228
266
|
"close": 30
|
|
229
267
|
},
|
|
230
|
-
"framework_lag_declaration": "GDPR Art.32, ISO A.8.24, PCI-DSS Req.3, SOC 2 CC6.1 collectively cover encryption + key management + access control in production storage backends. None of them name 'secret in source artifact' as a distinct exposure category, despite this being the dominant 2025-2026 cloud-compromise vector. NIS2 Art.21(2)(j) names cryptography but not development-workflow exposure. Gap = ~30 days between developer commit and framework's quarterly access-review cadence; in practice, scraper bots exploit faster than any framework cadence.",
|
|
268
|
+
"framework_lag_declaration": "GDPR Art.32, ISO A.8.24, PCI-DSS Req.3, SOC 2 CC6.1 collectively cover encryption + key management + access control in production storage backends. None of them name 'secret in source artifact' as a distinct exposure category, despite this being the dominant 2025-2026 cloud-compromise vector. NIST 800-53 IA-5 (Authenticator Management) lags because it mandates authenticator protection in storage but does not require detection of authenticators committed to source repositories or build artifacts — IA-5 evidence is satisfied by a vault inventory while bearer tokens leak through `.env`, IaC tfvars, and CI logs unscanned. NIS2 Art.21(2)(j) names cryptography but not development-workflow exposure. Gap = ~30 days between developer commit and framework's quarterly access-review cadence; in practice, scraper bots exploit faster than any framework cadence. UK CAF Principle B2 (Identity and Access Control) treats credential lifecycle as an outcome reviewable on cycle, not as a per-commit scanning obligation — secrets embedded in source artifacts do not register against B2 evidence. AU Essential 8 Strategy 1 (Application Control, E8 M.1) is the relevant lever — restricting build agents from accessing arbitrary secret stores or environment variables at build time — but Essential 8 evidence does not enumerate CI-runner secret-store reachability as a sub-control; ISM-1546 covers MFA + admin-account hygiene but bearer tokens / API keys / OAuth refresh tokens in source artifacts bypass MFA entirely, never crossing the interactive auth surface ISM-1546 protects.",
|
|
231
269
|
"skill_chain": [
|
|
232
270
|
{
|
|
233
271
|
"skill": "dlp-gap-analysis",
|
package/lib/auto-discovery.js
CHANGED
|
@@ -31,7 +31,7 @@ const fs = require("fs");
|
|
|
31
31
|
const path = require("path");
|
|
32
32
|
const { scoreCustom, RWEP_WEIGHTS, ACTIVE_EXPLOITATION_LADDER } = require("./scoring");
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Stored rwep_factors must reproduce the stored rwep_score.
|
|
35
35
|
// `buildScoringInputs` is the single source of truth for both — it captures
|
|
36
36
|
// the conservative defaults applied to a freshly-imported KEV draft (CISA
|
|
37
37
|
// only lists vulnerabilities with documented exploitation, so we assume a
|
|
@@ -199,7 +199,7 @@ function buildKevDraftEntry(kevEntry, nvdPayload, epssPayload) {
|
|
|
199
199
|
const knownRansomware =
|
|
200
200
|
String(kevEntry.knownRansomwareCampaignUse || "").toLowerCase() === "known";
|
|
201
201
|
|
|
202
|
-
//
|
|
202
|
+
// Stored rwep_factors and computed rwep_score MUST agree.
|
|
203
203
|
// Previously rwep_factors held nulls (for unknown poc/ai/reboot) but
|
|
204
204
|
// rwep_score was computed from concrete defaults (poc=true, reboot=true).
|
|
205
205
|
// `scoring.validate()` then flagged every auto-imported draft for
|
|
@@ -365,7 +365,16 @@ function discoverNewKev(ctx, cap = DEFAULT_CAP) {
|
|
|
365
365
|
|
|
366
366
|
// --- RFC discovery -----------------------------------------------------
|
|
367
367
|
|
|
368
|
-
async function fetchDatatracker(url) {
|
|
368
|
+
async function fetchDatatracker(url, ctx) {
|
|
369
|
+
// Air-gap refusal — Datatracker is a live IETF service. When the operator
|
|
370
|
+
// (or the caller's ctx) declares air-gap, return a structured refusal so
|
|
371
|
+
// refresh-external can surface "discovery skipped" rather than logging a
|
|
372
|
+
// generic network error. Caller's signature already accepts a nullable
|
|
373
|
+
// return; the structured object is distinguishable from a successful
|
|
374
|
+
// payload by `ok:false`.
|
|
375
|
+
if ((ctx && ctx.airGap === true) || process.env.EXCEPTD_AIR_GAP === "1") {
|
|
376
|
+
return { ok: false, error: "air-gap-blocked", source: "datatracker" };
|
|
377
|
+
}
|
|
369
378
|
const ac = new AbortController();
|
|
370
379
|
const t = setTimeout(() => ac.abort(), TIMEOUT_MS);
|
|
371
380
|
try {
|
|
@@ -532,7 +541,22 @@ async function discoverNewRfcs(ctx, opts = {}) {
|
|
|
532
541
|
`https://datatracker.ietf.org/api/v1/doc/document/` +
|
|
533
542
|
`?type=rfc&group__acronym=${encodeURIComponent(wg)}` +
|
|
534
543
|
`&time__gt=${cutoff}&order_by=-time&limit=20&format=json`;
|
|
535
|
-
const payload = await fetchDatatracker(url);
|
|
544
|
+
const payload = await fetchDatatracker(url, ctx);
|
|
545
|
+
// Treat the air-gap structured refusal as a discovery skip — not an
|
|
546
|
+
// error. The existing `!payload || !Array.isArray(payload.objects)`
|
|
547
|
+
// path would have classed it as `errors++`, which misreports an
|
|
548
|
+
// operator-chosen offline posture as a fault. Short-circuit the whole
|
|
549
|
+
// WG loop on first air-gap refusal because every subsequent fetch will
|
|
550
|
+
// refuse identically.
|
|
551
|
+
if (payload && payload.ok === false && payload.error === "air-gap-blocked") {
|
|
552
|
+
return {
|
|
553
|
+
diffs: [],
|
|
554
|
+
errors: 0,
|
|
555
|
+
spilled: 0,
|
|
556
|
+
summary: "RFC discovery: skipped (air-gap mode)",
|
|
557
|
+
skipped: "air-gap",
|
|
558
|
+
};
|
|
559
|
+
}
|
|
536
560
|
if (!payload || !Array.isArray(payload.objects)) {
|
|
537
561
|
errors++;
|
|
538
562
|
continue;
|
package/lib/cross-ref-api.js
CHANGED
|
@@ -19,12 +19,12 @@ const ROOT = path.join(__dirname, '..');
|
|
|
19
19
|
const DATA_DIR = process.env.EXCEPTD_DATA_DIR || path.join(ROOT, 'data');
|
|
20
20
|
const INDEX_DIR = path.join(DATA_DIR, '_indexes');
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
22
|
+
// Cache entries store the parsed payload AND the mtimeMs of the source
|
|
23
|
+
// file at parse-time. Each load call re-stats the file; if mtime matches,
|
|
24
|
+
// the cached value is returned (one syscall, no parse). If mtime changed,
|
|
25
|
+
// re-parse + repopulate. If stat fails (file vanished mid-run, permission
|
|
26
|
+
// glitch), fall back to the cached value. Without mtime-keyed
|
|
27
|
+
// invalidation, long-running `orchestrator watch` processes never see
|
|
28
28
|
// `data/cve-catalog.json` mutations driven by `exceptd refresh --apply`.
|
|
29
29
|
const _cache = new Map();
|
|
30
30
|
|
|
@@ -106,13 +106,14 @@ function entries(catalog) {
|
|
|
106
106
|
* that references it across skills, framework gaps, theater fingerprints,
|
|
107
107
|
* recipes, and zero-day lessons.
|
|
108
108
|
*
|
|
109
|
-
*
|
|
109
|
+
* Auto-imported drafts (entries with `_auto_imported === true`) are
|
|
110
110
|
* EXCLUDED by default. Drafts carry conservative-default mechanical fields
|
|
111
111
|
* and null analytical fields pending curation; downstream analyze / bundle
|
|
112
|
-
* emitters that assume `byCve()` returns curated data would treat the
|
|
113
|
-
* placeholders as authoritative. The cve-curation flow (which
|
|
114
|
-
* editorial questionnaire) opts in via
|
|
115
|
-
*
|
|
112
|
+
* emitters that assume `byCve()` returns curated data would treat the
|
|
113
|
+
* draft's placeholders as authoritative. The cve-curation flow (which
|
|
114
|
+
* surfaces the editorial questionnaire) opts in via
|
|
115
|
+
* `byCve(id, { include_drafts: true })`; every other caller stays on the
|
|
116
|
+
* default exclude path.
|
|
116
117
|
*/
|
|
117
118
|
function byCve(cveId, opts) {
|
|
118
119
|
const includeDrafts = !!(opts && opts.include_drafts);
|
package/lib/cve-curation.js
CHANGED
|
@@ -76,7 +76,7 @@ function loadCveEntrySchema() {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
//
|
|
79
|
+
// Lazy-loaded module-level catalog cache. The ATLAS / ATT&CK / CWE /
|
|
80
80
|
// framework-control-gaps catalogs don't change inside a single CLI process,
|
|
81
81
|
// so re-reading them per `curate()` call is wasted I/O. Cache once per
|
|
82
82
|
// process; batch-curate paths (future) get the speedup for free.
|
|
@@ -87,7 +87,7 @@ function loadJsonRaw(absPath) {
|
|
|
87
87
|
catch { return null; }
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
//
|
|
90
|
+
// Resolve a catalog path that may be absolute (operator passed
|
|
91
91
|
// `--catalog C:\tmp\cat.json` or `/tmp/cat.json`) or repo-relative. Plain
|
|
92
92
|
// `path.join(ROOT, abs)` mishandles absolute paths on Windows.
|
|
93
93
|
function resolveCatalogPath(p) {
|
|
@@ -141,11 +141,10 @@ function pickCandidates(draftDigest, catalog, idField, descriptionField) {
|
|
|
141
141
|
return candidates.slice(0, 5);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// RUSTSEC-*, etc. that land via lib/source-osv.js)
|
|
147
|
-
//
|
|
148
|
-
// a one-line addition.
|
|
144
|
+
// Report the actual upstream source on a draft. A check that only knows
|
|
145
|
+
// about `_source_ghsa_id` would tag every OSV-imported draft (MAL-*,
|
|
146
|
+
// SNYK-*, RUSTSEC-*, etc. that land via lib/source-osv.js) as "unknown".
|
|
147
|
+
// The registry makes future sources a one-line addition.
|
|
149
148
|
const SOURCE_FIELDS = [
|
|
150
149
|
{ field: "_source_ghsa_id", label: "GHSA" },
|
|
151
150
|
{ field: "_source_osv_id", label: "OSV" },
|
|
@@ -161,9 +160,9 @@ function autoImportedFrom(draft) {
|
|
|
161
160
|
return "unknown";
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
// "unrated"
|
|
163
|
+
// Severity word. cvss_score: null returns "unrated" — collapsing it to
|
|
164
|
+
// "low" would be wrong + misleading on a draft that has not been scored
|
|
165
|
+
// yet. "unrated" lets operators grep for unscored drafts and the curate
|
|
167
166
|
// summary reflects the actual state.
|
|
168
167
|
function severityWord(score) {
|
|
169
168
|
if (typeof score !== "number" || Number.isNaN(score)) return "unrated";
|
|
@@ -173,7 +172,7 @@ function severityWord(score) {
|
|
|
173
172
|
return "low";
|
|
174
173
|
}
|
|
175
174
|
|
|
176
|
-
//
|
|
175
|
+
// The fields the strict CVE schema requires. Used to (a) extend the
|
|
177
176
|
// questionnaire so the operator is prompted for every blocking gap, and
|
|
178
177
|
// (b) compute `residual_warnings` after an apply.
|
|
179
178
|
const REQUIRED_SCHEMA_FIELDS = [
|
|
@@ -300,10 +299,10 @@ function buildQuestionnaire(cveId, draft) {
|
|
|
300
299
|
// specific ASK to surface what the reviewer needs to decide.
|
|
301
300
|
const questions = [];
|
|
302
301
|
|
|
303
|
-
//
|
|
304
|
-
// be treated as "answered"
|
|
305
|
-
// Use explicit emptiness checks so drafts
|
|
306
|
-
// arrays still get the prompt.
|
|
302
|
+
// Pre-filled empty containers (`iocs: {}`, `atlas_refs: []`) must NOT
|
|
303
|
+
// be treated as "answered". `if (!draft.iocs)` is truthy for {}, which
|
|
304
|
+
// would skip the prompt. Use explicit emptiness checks so drafts
|
|
305
|
+
// seeded with empty objects / arrays still get the prompt.
|
|
307
306
|
const arrEmpty = (a) => !Array.isArray(a) || a.length === 0;
|
|
308
307
|
const objEmpty = (o) => !o || typeof o !== "object" || Object.keys(o).length === 0;
|
|
309
308
|
|
|
@@ -380,9 +379,9 @@ function buildQuestionnaire(cveId, draft) {
|
|
|
380
379
|
});
|
|
381
380
|
}
|
|
382
381
|
|
|
383
|
-
//
|
|
384
|
-
// path cannot produce a schema-passing entry —
|
|
385
|
-
//
|
|
382
|
+
// Schema-required field prompts. Without these populated, the apply
|
|
383
|
+
// path cannot produce a schema-passing entry — the entry stays a draft
|
|
384
|
+
// even after curate --apply. Prompt for them explicitly so the
|
|
386
385
|
// operator sees what's actually blocking promotion.
|
|
387
386
|
if (draft.cvss_score === null || draft.cvss_score === undefined
|
|
388
387
|
|| draft.cvss_vector === null || draft.cvss_vector === undefined
|
|
@@ -460,7 +459,7 @@ function buildQuestionnaire(cveId, draft) {
|
|
|
460
459
|
}
|
|
461
460
|
|
|
462
461
|
/**
|
|
463
|
-
*
|
|
462
|
+
* Apply operator-supplied answers to a draft and write back atomically.
|
|
464
463
|
*
|
|
465
464
|
* Answers shape (each key optional; missing keys leave the draft unchanged
|
|
466
465
|
* for that field):
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Canonical exit-code constants for every CLI verb.
|
|
5
|
+
*
|
|
6
|
+
* Every `process.exitCode = N` / `process.exit(N)` site in `bin/exceptd.js`
|
|
7
|
+
* (and any library that wants to set an exit code via emit() ok:false bodies)
|
|
8
|
+
* should reference one of these constants rather than a bare number literal.
|
|
9
|
+
* The map is the source of truth for help text — `exceptd doctor --exit-codes`
|
|
10
|
+
* dumps it as JSON so operator-facing docs cannot drift from runtime.
|
|
11
|
+
*
|
|
12
|
+
* History: prior to v0.12.24 codes were bare magic numbers scattered across
|
|
13
|
+
* ~30 sites. Code 3 in particular meant both "session-id collision" (cmdRun)
|
|
14
|
+
* and "ran-but-no-evidence" (cmdCi) — two semantics, one code, no doc surface.
|
|
15
|
+
* v0.12.24 splits them and centralises so a new verb cannot regress by typo.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const EXIT_CODES = Object.freeze({
|
|
19
|
+
SUCCESS: 0,
|
|
20
|
+
GENERIC_FAILURE: 1,
|
|
21
|
+
DETECTED_ESCALATE: 2,
|
|
22
|
+
RAN_NO_EVIDENCE: 3,
|
|
23
|
+
BLOCKED: 4,
|
|
24
|
+
JURISDICTION_CLOCK_STARTED: 5,
|
|
25
|
+
TAMPERED: 6,
|
|
26
|
+
SESSION_ID_COLLISION: 7,
|
|
27
|
+
LOCK_CONTENTION: 8,
|
|
28
|
+
STORAGE_EXHAUSTED: 9,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Human-readable + machine-stable description per code. Source for the
|
|
33
|
+
* `exceptd doctor --exit-codes` dump and for help-text rendering.
|
|
34
|
+
*/
|
|
35
|
+
const EXIT_CODE_DESCRIPTIONS = Object.freeze({
|
|
36
|
+
0: { name: 'SUCCESS', summary: 'Verb completed successfully.' },
|
|
37
|
+
1: { name: 'GENERIC_FAILURE', summary: 'Unhandled error or validation failure.' },
|
|
38
|
+
2: { name: 'DETECTED_ESCALATE', summary: 'CI gate: classification === detected, operator action required.' },
|
|
39
|
+
3: { name: 'RAN_NO_EVIDENCE', summary: 'CI gate: verb ran but produced no actionable evidence.' },
|
|
40
|
+
4: { name: 'BLOCKED', summary: 'CI gate: ok:false body — precondition refusal or hard error.' },
|
|
41
|
+
5: { name: 'JURISDICTION_CLOCK_STARTED', summary: 'Jurisdictional notification window opened (e.g. NIS2 24h, DORA 4h, GDPR 72h).' },
|
|
42
|
+
6: { name: 'TAMPERED', summary: 'Attestation sidecar verification failed (signed-but-invalid, corrupt, unsigned-substitution, algorithm-unsupported).' },
|
|
43
|
+
7: { name: 'SESSION_ID_COLLISION', summary: 'Persisting attestation would overwrite an existing session; pass --force-overwrite to replace or supply a fresh --session-id.' },
|
|
44
|
+
8: { name: 'LOCK_CONTENTION', summary: 'Concurrent invocation holds the per-playbook attestation lock; retry after the busy run releases.' },
|
|
45
|
+
9: { name: 'STORAGE_EXHAUSTED', summary: 'Disk full, quota exceeded, or read-only filesystem prevented attestation write (ENOSPC, EDQUOT, EROFS).' },
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Return the human-readable name for a numeric exit code.
|
|
50
|
+
*/
|
|
51
|
+
function exitCodeName(code) {
|
|
52
|
+
const e = EXIT_CODE_DESCRIPTIONS[code];
|
|
53
|
+
return e ? e.name : 'UNKNOWN';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Return all exit codes as a stable-shape array suitable for JSON dump.
|
|
58
|
+
*/
|
|
59
|
+
function listExitCodes() {
|
|
60
|
+
return Object.entries(EXIT_CODE_DESCRIPTIONS).map(([code, info]) => ({
|
|
61
|
+
code: Number(code),
|
|
62
|
+
name: info.name,
|
|
63
|
+
summary: info.summary,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
EXIT_CODES,
|
|
69
|
+
EXIT_CODE_DESCRIPTIONS,
|
|
70
|
+
exitCodeName,
|
|
71
|
+
listExitCodes,
|
|
72
|
+
};
|