@blamejs/exceptd-skills 0.12.27 → 0.12.29
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 +4 -1
- package/CHANGELOG.md +54 -0
- package/bin/exceptd.js +30 -20
- package/data/_indexes/_meta.json +26 -23
- package/data/_indexes/activity-feed.json +32 -11
- package/data/_indexes/catalog-summaries.json +3 -3
- package/data/_indexes/chains.json +965 -35
- package/data/_indexes/currency.json +68 -41
- package/data/_indexes/frequency.json +428 -124
- package/data/_indexes/handoff-dag.json +70 -19
- package/data/_indexes/jurisdiction-map.json +37 -12
- package/data/_indexes/section-offsets.json +282 -0
- package/data/_indexes/stale-content.json +3 -3
- package/data/_indexes/summary-cards.json +198 -0
- package/data/_indexes/token-budget.json +168 -3
- package/data/_indexes/trigger-table.json +190 -0
- package/data/_indexes/xref.json +145 -2
- package/data/atlas-ttps.json +61 -111
- package/data/attack-techniques.json +104 -19
- package/data/cve-catalog.json +101 -45
- package/data/cwe-catalog.json +149 -94
- package/data/d3fend-catalog.json +199 -53
- package/data/framework-control-gaps.json +1679 -89
- package/data/playbooks/cloud-iam-incident.json +1351 -0
- package/data/playbooks/crypto-codebase.json +1 -1
- package/data/playbooks/idp-incident.json +1259 -0
- package/data/playbooks/ransomware.json +1407 -0
- package/data/rfc-references.json +58 -59
- package/lib/exit-codes.js +2 -0
- package/lib/playbook-runner.js +25 -1
- package/manifest-snapshot.json +220 -3
- package/manifest-snapshot.sha256 +1 -1
- package/manifest.json +287 -45
- package/package.json +3 -2
- package/sbom.cdx.json +1854 -11
- package/scripts/backfill-theater-test.js +806 -0
- package/scripts/refresh-reverse-refs.js +171 -0
- package/scripts/refresh-sbom.js +155 -8
- package/skills/cloud-iam-incident/skill.md +419 -0
- package/skills/idp-incident-response/skill.md +352 -0
- package/skills/ransomware-response/skill.md +374 -0
package/AGENTS.md
CHANGED
|
@@ -139,7 +139,7 @@ Cross-cutting playbook `framework` is the natural correlation layer — many pla
|
|
|
139
139
|
|
|
140
140
|
| Verb | What it does |
|
|
141
141
|
|---|---|
|
|
142
|
-
| `exceptd brief --all` | Grouped-by-scope summary of all
|
|
142
|
+
| `exceptd brief --all` | Grouped-by-scope summary of all 16 playbooks. `--scope <type>` filters. `--directives` expands directive IDs/titles per playbook. `--flat` for non-grouped. Legacy alias: `exceptd plan` (deprecated, scheduled for removal in v0.13). |
|
|
143
143
|
| `exceptd brief <pb>` | Phase 2 threat-context briefing — threat context, RWEP thresholds, skill chain, token budget, jurisdiction obligations. |
|
|
144
144
|
| `exceptd run <pb> --evidence <file>` | Phases 5-7 (analyze + validate + close) from agent evidence. Auto-detect cwd when no playbook positional. `--vex <file>` drops CycloneDX/OpenVEX `not_affected` CVEs. `--diff-from-latest` for drift mode. `--force-stale` overrides currency hard-block. |
|
|
145
145
|
| `exceptd ai-run <pb>` | Streaming variant of `run` for AI agents; emits phase-by-phase NDJSON. |
|
|
@@ -353,6 +353,9 @@ Maintainers convert approved requests into skill files. The contributor is credi
|
|
|
353
353
|
| container security, kubernetes, cis k8s, pod security standards, kyverno, gatekeeper, falco, tetragon, admission policy | container-runtime-security |
|
|
354
354
|
| mlops security, model registry, training data integrity, mlflow, kubeflow, vertex ai, sagemaker, hugging face, model signing, drift detection | mlops-security |
|
|
355
355
|
| incident response, ir playbook, csirt, picerl, nist 800-61, iso 27035, breach notification, bec incident, ai incident | incident-response-playbook |
|
|
356
|
+
| ransomware response, decryptor, no more ransom, ofac sdn, cyber insurance, immutable backup, double extortion, phi exfil before encrypt, lockbit, alphv, akira | ransomware-response |
|
|
357
|
+
| idp incident, okta tenant compromise, entra id, auth0, saml token forgery, oauth consent abuse, federated trust modification, midnight blizzard, scattered spider | idp-incident-response |
|
|
358
|
+
| cloud iam incident, aws account takeover, gcp account takeover, azure account takeover, cross-account assume-role, imds, access key leak, snowflake breach, scim, workload identity | cloud-iam-incident |
|
|
356
359
|
| email security, anti-phishing, dmarc, dkim, spf, bimi, arc, mta-sts, bec, vishing, deepfake phishing | email-security-anti-phishing |
|
|
357
360
|
| age gate, age verification, coppa, cipa, california aadc, uk children's code, kosa, gdpr article 8, dsa article 28, parental consent, csam, child safety, children's online safety | age-gates-child-safety |
|
|
358
361
|
| forward watch, watchlist, upcoming standards, horizon scan | `node orchestrator/index.js watchlist` (add `--by-skill` to invert) |
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.29 — 2026-05-15
|
|
4
|
+
|
|
5
|
+
Catalog hygiene + pipeline integrity pass. Closes Hard Rule #1, #6, #7, and #8 gaps that had accumulated across the 2025-2026 catalog growth; tightens the SBOM + OpenVEX + exit-code surfaces.
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
**Compliance-theater test on every framework gap.** Every entry in the framework-control-gaps catalog (109 entries spanning NIST 800-53, ISO/IEC 27001/27017/27035/42001, SOC 2, UK CAF, AU ISM/Essential 8, EU DORA, EU NIS2, EU AI Act, HIPAA, PCI DSS, FedRAMP, CMMC, HITRUST, IEC 62443, OWASP, telecom standards, ransomware-class gaps, and OFAC sanctions screening) now carries a `theater_test` field with a falsifiable test that distinguishes paper compliance from actual security. Closes Hard Rule #6. Sample shape: `{claim, test, evidence_required[], verdict_when_failed: "compliance-theater"}`. The test must reference a concrete artifact (audit log, config dump, tabletop exercise stopwatch) whose result is binary.
|
|
10
|
+
|
|
11
|
+
**SBOM per-file SHA-256 + bundle digest.** `sbom.cdx.json` now includes `metadata.component.hashes[]` (bundle digest, SHA-256) and one `components[type=file]` entry per shipped file with its own SHA-256. Downstream supply-chain consumers can verify any individual file against the bundle. Excludes the regenerable `data/_indexes/` cache from per-file inventory (covered by the `Pre-computed indexes freshness` gate instead). Also corrects `metadata.tools` from the placeholder `name: "hand-written"` to the real generator script and bound package version.
|
|
12
|
+
|
|
13
|
+
**OpenVEX `author` threads operator attribution.** Previously hard-pinned to `"exceptd"`, which falsely attributed every disposition statement to the tooling vendor. Now mirrors the CSAF publisher.namespace fallback ladder: `runOpts.publisherNamespace` → `runOpts.operator` → `urn:exceptd:operator:unknown` with a `bundle_publisher_unclaimed` runtime warning. Operators running scans correctly own their dispositions.
|
|
14
|
+
|
|
15
|
+
**Exit code 10: UNKNOWN_COMMAND.** The dispatcher's unknown-command / missing-script / spawn-error paths previously exited 2, colliding with `EXIT_CODES.DETECTED_ESCALATE` semantics. Split into `EXIT_CODES.UNKNOWN_COMMAND = 10`. CI gates wiring `case 2)` for escalation triage no longer false-alarm on operator typos. Same regression class v0.12.24 closed for the SESSION_ID_COLLISION / RAN_NO_EVIDENCE code-3 collision.
|
|
16
|
+
|
|
17
|
+
**Reverse-reference auto-regeneration.** New `npm run refresh-reverse-refs` rebuilds the `skills_referencing` / `exceptd_skills` arrays on `data/atlas-ttps.json`, `data/cwe-catalog.json`, `data/d3fend-catalog.json`, and `data/rfc-references.json` from the manifest forward direction. Idempotent. A new `tests/reverse-ref-drift.test.js` blocks merges that leave the reverse direction out of sync with the manifest — eliminates the one-sided-reference drift class that audits have flagged repeatedly.
|
|
18
|
+
|
|
19
|
+
### Bugs
|
|
20
|
+
|
|
21
|
+
- `crypto-codebase` `feeds_into` condition used the unsupported `contains` operator; the chain to the `secrets` playbook never fired. Replaced with `analyze.classification == 'detected'`. Same class of bug v0.12.28 corrected on the IR-cluster playbooks.
|
|
22
|
+
- Manifest `atlas_version` / `attack_version` had drifted to v5.1.0 / v17 while the data catalogs already pinned v5.4.0 / v19.0. Manifest now matches the catalogs and AGENTS.md ground truth.
|
|
23
|
+
- 14 sites in `bin/exceptd.js` used bare numeric `process.exitCode = 1` / `finish(1)` / `finish(0)` instead of `EXIT_CODES.*` constants. All migrated to the constant.
|
|
24
|
+
- `cmdCi` per-id loop called `runner.loadPlaybook(id)` without first running `validateIdComponent('playbook')` — a defense-in-depth gap relative to `cmdRunMulti`. Now validates before load.
|
|
25
|
+
|
|
26
|
+
### Internal
|
|
27
|
+
|
|
28
|
+
- AI-discovery rate on `data/cve-catalog.json` moves 10% → 20% with three new flag flips backed by citations: CVE-2026-43284 + CVE-2026-43500 (Dirty Frag pair, Hyunwoo Kim with AI-assisted methodology per Sysdig); CVE-2026-46300 (Fragnesia, William Bowling using Zellic.io's AI agentic auditor). All other CVEs gain a `discovery_attribution_note` field citing the human researcher or vendor team. New `_meta.ai_discovery_methodology` block documents the 20%/30%/40% advancement ladder against the AGENTS.md Hard Rule #7 target. Gap to 40% explicitly tracked.
|
|
29
|
+
- AGENTS.md Quick Skill Reference: playbook count "all 13 playbooks" → "all 16 playbooks".
|
|
30
|
+
- `package.json.description`: "38 skills" → "42 skills".
|
|
31
|
+
- 22 reverse-reference entries across 4 catalogs cleaned up by the new regen script (atlas: 30 entries changed, cwe: 46, d3fend: 28, rfc: 22).
|
|
32
|
+
- Test suite 1064 → 1082 (six new test files: framework-gaps-theater-test-coverage, cve-ai-discovery-attribution, sbom-per-file-hash, reverse-ref-drift, plus updates to bin-dispatcher, cli-exit-codes, lib-exit-codes, cve-additions-v0-12-21 for the new contract).
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## 0.12.28 — 2026-05-15
|
|
36
|
+
|
|
37
|
+
Incident-response cluster — three new playbooks and skills covering identity-provider tenant compromise, cloud-IAM account takeover, and ransomware response. The existing `incident-response-playbook` skill stays as the generic PICERL backbone; the new surface adds attack-class-specific depth for the three IR scenarios that dominate 2025-2026 breach reporting.
|
|
38
|
+
|
|
39
|
+
### Features
|
|
40
|
+
|
|
41
|
+
**`idp-incident` playbook + `idp-incident-response` skill.** Tenant-compromise response for Okta / Entra ID / Auth0 / Ping / OneLogin. Covers federated-trust modification, OAuth consent abuse, SAML token forgery, cross-tenant relationship abuse, dormant service-account reactivation, and help-desk social engineering. Maps T1078.004, T1098.001, T1556.007, T1606.002, T1199. Eight jurisdiction clocks (GDPR Art.33/34, NIS2 Art.23, DORA Art.19, NYDFS 500.17, CCPA/CPRA, AU NDB, UK GDPR). Detects on unauthorized consent grants from non-corp tenants, anomalous federated-trust additions, MFA factor swaps without password reset, recent high-privilege role assignments, and cross-tenant assumption anomalies — each indicator carries explicit false-positive checks.
|
|
42
|
+
|
|
43
|
+
**`cloud-iam-incident` playbook + `cloud-iam-incident` skill.** Account-takeover response for AWS / GCP / Azure. Covers cross-account assume-role abuse, IMDS exposure, managed-identity token replay, access-key leakage to public repositories, federated-trust attacks against IAM Identity Center, and crypto-mining detection via GPU-instance creation. Maps T1078.004, T1098.001, T1098.003, T1136.003, T1538, T1552.005, T1562.008, T1580. Ten jurisdiction clocks including SG PDPA, JP APPI, and US-CA. Detects on root-login ASN anomalies, mass IAM-user creation outside IaC, unused-region resource creation, cross-account assume-role anomalies, IMDSv1 legacy access, KMS key-policy self-grants, and S3-bucket public-grant events.
|
|
44
|
+
|
|
45
|
+
**`ransomware` playbook + `ransomware-response` skill.** Ransomware-specific incident response — extends the generic `incident-response-playbook` with the four decision properties that don't appear in standard IR frameworks: OFAC SDN sanctions check (BLOCKING for payment posture; payment to a sanctioned threat actor is a federal-law violation in the US), decryptor availability (No More Ransom + vendor-specific decryptors), cyber-insurance carrier notification posture (most policies require 24-hour notification), and immutable-backup viability versus replication-only "backups." Sixteen jurisdiction obligations spanning OFAC (0-hour BLOCKING), insurance carrier (24h), NIS2 (24h), DORA (4h), GDPR (72h), SEC 8-K (4 business days), HIPAA, CCPA, NYDFS ransom-event notification, and CIRCIA. Detects on mass file-extension change events, shadow-copy deletion outside maintenance windows, encrypted-file-extension growth rate anomalies, BloodHound-class AD reconnaissance, and large outbound transfers 24-72 hours before encryption (exfil-before-encrypt as distinct breach class).
|
|
46
|
+
|
|
47
|
+
### Internal
|
|
48
|
+
|
|
49
|
+
- Skill count 39 → 42 (Ed25519 manifest re-signed).
|
|
50
|
+
- Playbook count 13 → 16 (validator `tests/validate-playbooks.test.js` updated).
|
|
51
|
+
- RFC catalog: added RFC-7591 (OAuth 2.0 Dynamic Client Registration), RFC-8693 (OAuth 2.0 Token Exchange), RFC-9068 (JWT Profile for OAuth 2.0 Access Tokens).
|
|
52
|
+
- ATT&CK techniques added to resolution catalog: T1098.001, T1098.003, T1136.003, T1538, T1562.008, T1580, T1606.002.
|
|
53
|
+
- Framework-control-gaps catalog: 22 new entries covering federated-identity gaps (NIST 800-53 IA-5, ISO 27001 A.5.16-17, SOC 2 CC6, UK CAF B2, AU ISM-1559), cloud-IAM gaps (FedRAMP IL5, NIST AC-2 cross-account, ISO 27017, AWS Security Hub coverage, AU ISM-1546), and ransomware-specific gaps (OFAC SDN payment block, cyber-insurance 24h notification, EU Reg 2014/833 cyber sanctions, immutable-backup recovery, decryptor availability pre-decision, PHI-exfil-before-encrypt breach class).
|
|
54
|
+
- AGENTS.md Quick Skill Reference table extended with the three new skills.
|
|
55
|
+
|
|
56
|
+
|
|
3
57
|
## 0.12.27 — 2026-05-15
|
|
4
58
|
|
|
5
59
|
**Patch: opt-in `--bundle-deterministic` mode for reproducible CSAF + OpenVEX + close-envelope bytes. Closes cycle 6 III P2-E + cycle 7 CCC bundle-non-determinism finding.**
|
package/bin/exceptd.js
CHANGED
|
@@ -516,10 +516,11 @@ function main() {
|
|
|
516
516
|
// Emit a structured JSON error matching the seven-phase verbs so operators
|
|
517
517
|
// piping through `jq` get one consistent shape across the CLI surface.
|
|
518
518
|
// emitError() sets exitCode + returns rather than calling process.exit()
|
|
519
|
-
// so the stderr JSON drains before teardown; promote the exit code to
|
|
520
|
-
//
|
|
519
|
+
// so the stderr JSON drains before teardown; promote the exit code to
|
|
520
|
+
// UNKNOWN_COMMAND (10) afterwards. Cycle 9 split this away from
|
|
521
|
+
// DETECTED_ESCALATE (2) — the two semantics had collided since v0.12.24.
|
|
521
522
|
emitError(`unknown command "${cmd}"`, { hint: "Run `exceptd help` for the list of verbs.", verb: cmd });
|
|
522
|
-
process.exitCode =
|
|
523
|
+
process.exitCode = EXIT_CODES.UNKNOWN_COMMAND;
|
|
523
524
|
return;
|
|
524
525
|
}
|
|
525
526
|
|
|
@@ -530,7 +531,7 @@ function main() {
|
|
|
530
531
|
`command "${cmd}" not available — expected ${path.relative(PKG_ROOT, script)} in the installed package.`,
|
|
531
532
|
{ verb: cmd }
|
|
532
533
|
);
|
|
533
|
-
process.exitCode =
|
|
534
|
+
process.exitCode = EXIT_CODES.UNKNOWN_COMMAND;
|
|
534
535
|
return;
|
|
535
536
|
}
|
|
536
537
|
|
|
@@ -541,7 +542,7 @@ function main() {
|
|
|
541
542
|
if (res.error) {
|
|
542
543
|
// emitError + exitCode rather than stderr + exit() so the JSON drains.
|
|
543
544
|
emitError(`failed to run ${cmd}: ${res.error.message}`, { verb: cmd });
|
|
544
|
-
process.exitCode =
|
|
545
|
+
process.exitCode = EXIT_CODES.UNKNOWN_COMMAND;
|
|
545
546
|
return;
|
|
546
547
|
}
|
|
547
548
|
// Propagate the child's exit status via exitCode so any buffered output
|
|
@@ -615,7 +616,7 @@ function emit(obj, pretty, humanRenderer) {
|
|
|
615
616
|
// and CI gates. The previous fix was per-verb; this is a universal catch
|
|
616
617
|
// so new verbs / new ok:false paths can't regress the contract.
|
|
617
618
|
if (obj && obj.ok === false && !process.exitCode) {
|
|
618
|
-
process.exitCode =
|
|
619
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
619
620
|
}
|
|
620
621
|
const wantJson = !!global.__exceptdWantJson || !!process.env.EXCEPTD_RAW_JSON;
|
|
621
622
|
if (humanRenderer && !wantJson && !pretty) {
|
|
@@ -638,7 +639,7 @@ function emitError(msg, extra, pretty) {
|
|
|
638
639
|
const body = Object.assign({ ok: false, error: msg }, extra || {});
|
|
639
640
|
const s = pretty ? JSON.stringify(body, null, 2) : JSON.stringify(body);
|
|
640
641
|
process.stderr.write(s + "\n");
|
|
641
|
-
process.exitCode =
|
|
642
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
642
643
|
}
|
|
643
644
|
|
|
644
645
|
/**
|
|
@@ -2107,7 +2108,7 @@ function cmdLint(runner, args, runOpts, pretty) {
|
|
|
2107
2108
|
}
|
|
2108
2109
|
return lines.join("\n");
|
|
2109
2110
|
});
|
|
2110
|
-
if (!ok) process.exitCode =
|
|
2111
|
+
if (!ok) process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
2111
2112
|
}
|
|
2112
2113
|
|
|
2113
2114
|
function cmdBrief(runner, args, runOpts, pretty) {
|
|
@@ -3398,7 +3399,7 @@ function cmdIngest(runner, args, runOpts, pretty) {
|
|
|
3398
3399
|
|
|
3399
3400
|
if (result && result.ok === false) {
|
|
3400
3401
|
process.stderr.write((pretty ? JSON.stringify(result, null, 2) : JSON.stringify(result)) + "\n");
|
|
3401
|
-
process.exitCode =
|
|
3402
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
3402
3403
|
return;
|
|
3403
3404
|
}
|
|
3404
3405
|
emit(result, pretty);
|
|
@@ -5276,7 +5277,7 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5276
5277
|
|
|
5277
5278
|
if (wantJson) {
|
|
5278
5279
|
emit(out, indent);
|
|
5279
|
-
if (!allGreen) process.exitCode =
|
|
5280
|
+
if (!allGreen) process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
5280
5281
|
return;
|
|
5281
5282
|
}
|
|
5282
5283
|
|
|
@@ -5367,10 +5368,10 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5367
5368
|
process.stdout.write(`\n[doctor --fix] ${out.summary.fix_applied} — re-run \`exceptd doctor\` to confirm.\n`);
|
|
5368
5369
|
} else if (out.summary.fix_attempted) {
|
|
5369
5370
|
process.stdout.write(`\n[doctor --fix] ${out.summary.fix_attempted} (exit=${out.summary.fix_exit_code}); run \`node lib/sign.js generate-keypair\` manually.\n`);
|
|
5370
|
-
process.exitCode =
|
|
5371
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
5371
5372
|
return;
|
|
5372
5373
|
}
|
|
5373
|
-
if (errorList.length > 0) process.exitCode =
|
|
5374
|
+
if (errorList.length > 0) process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
5374
5375
|
// Warnings alone do NOT force exit 1 — CI gates use exit 0 to mean "ran
|
|
5375
5376
|
// successfully" even with informational warnings. Operators reading the
|
|
5376
5377
|
// visible "[!! warn]" line still see the issue.
|
|
@@ -5502,7 +5503,7 @@ function cmdAiRun(runner, args, runOpts, pretty) {
|
|
|
5502
5503
|
// the framed error event so the stdout-only JSONL contract holds — host
|
|
5503
5504
|
// AIs reading this stream must see structured frames, never bare text.
|
|
5504
5505
|
process.stdout.write(JSON.stringify({ event: "error", reason: e.message, phase: "info", playbook_id: playbookId, directive_id: directiveId }) + "\n");
|
|
5505
|
-
process.exitCode =
|
|
5506
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
5506
5507
|
return;
|
|
5507
5508
|
}
|
|
5508
5509
|
|
|
@@ -5589,7 +5590,7 @@ function cmdAiRun(runner, args, runOpts, pretty) {
|
|
|
5589
5590
|
// v0.12.12: same exit-after-write anti-pattern as the pre-stream
|
|
5590
5591
|
// load path. Use exitCode + return so stderr drains.
|
|
5591
5592
|
process.stderr.write((pretty ? JSON.stringify(result || {}, null, 2) : JSON.stringify(result || {})) + "\n");
|
|
5592
|
-
process.exitCode =
|
|
5593
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
5593
5594
|
return;
|
|
5594
5595
|
}
|
|
5595
5596
|
// v0.12.14: ai-run --no-stream previously emitted a
|
|
@@ -5696,7 +5697,7 @@ function cmdAiRun(runner, args, runOpts, pretty) {
|
|
|
5696
5697
|
catch (e) {
|
|
5697
5698
|
handled = true;
|
|
5698
5699
|
writeLine({ event: "error", reason: `invalid JSON on stdin: ${e.message}`, line_preview: line.slice(0, 120) });
|
|
5699
|
-
return finish(
|
|
5700
|
+
return finish(EXIT_CODES.GENERIC_FAILURE);
|
|
5700
5701
|
}
|
|
5701
5702
|
if (!parsed || parsed.event !== "evidence" || !parsed.payload) {
|
|
5702
5703
|
// Ignore non-evidence chatter so the host AI can interleave its own
|
|
@@ -5710,11 +5711,11 @@ function cmdAiRun(runner, args, runOpts, pretty) {
|
|
|
5710
5711
|
result = runner.run(playbookId, directiveId, submission, runOpts);
|
|
5711
5712
|
} catch (e) {
|
|
5712
5713
|
writeLine({ event: "error", reason: `runner threw: ${e.message}` });
|
|
5713
|
-
return finish(
|
|
5714
|
+
return finish(EXIT_CODES.GENERIC_FAILURE);
|
|
5714
5715
|
}
|
|
5715
5716
|
if (!result || result.ok === false) {
|
|
5716
5717
|
writeLine({ event: "error", reason: result?.reason || "runner returned ok:false", result });
|
|
5717
|
-
return finish(
|
|
5718
|
+
return finish(EXIT_CODES.GENERIC_FAILURE);
|
|
5718
5719
|
}
|
|
5719
5720
|
writeLine({ phase: "detect", ...result.phases?.detect });
|
|
5720
5721
|
writeLine({ phase: "analyze", ...result.phases?.analyze });
|
|
@@ -5759,7 +5760,7 @@ function cmdAiRun(runner, args, runOpts, pretty) {
|
|
|
5759
5760
|
}
|
|
5760
5761
|
}
|
|
5761
5762
|
writeLine({ event: "done", ok: true, session_id: result.session_id, evidence_hash: result.evidence_hash });
|
|
5762
|
-
return finish(
|
|
5763
|
+
return finish(EXIT_CODES.SUCCESS);
|
|
5763
5764
|
};
|
|
5764
5765
|
|
|
5765
5766
|
// Handle empty/closed stdin: emit a hint then exit cleanly so AI agents
|
|
@@ -5767,7 +5768,7 @@ function cmdAiRun(runner, args, runOpts, pretty) {
|
|
|
5767
5768
|
// a hung process.
|
|
5768
5769
|
if (process.stdin.isTTY) {
|
|
5769
5770
|
writeLine({ event: "error", reason: "ai-run streaming mode requires evidence on stdin; pipe {\"event\":\"evidence\",\"payload\":{...}} or use --no-stream." });
|
|
5770
|
-
process.exitCode =
|
|
5771
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
5771
5772
|
return;
|
|
5772
5773
|
}
|
|
5773
5774
|
|
|
@@ -5803,7 +5804,7 @@ function cmdAiRun(runner, args, runOpts, pretty) {
|
|
|
5803
5804
|
} catch { /* fall through to error */ }
|
|
5804
5805
|
}
|
|
5805
5806
|
writeLine({ event: "error", reason: "stdin closed without an evidence event. Pipe `{\"event\":\"evidence\",\"payload\":{...}}` for streaming mode, or pass --no-stream + --evidence <file> for single-shot." });
|
|
5806
|
-
process.exitCode =
|
|
5807
|
+
process.exitCode = EXIT_CODES.GENERIC_FAILURE;
|
|
5807
5808
|
return;
|
|
5808
5809
|
}
|
|
5809
5810
|
});
|
|
@@ -6061,6 +6062,15 @@ function cmdCi(runner, args, runOpts, pretty) {
|
|
|
6061
6062
|
let clockStartedReasons = [];
|
|
6062
6063
|
|
|
6063
6064
|
for (const id of ids) {
|
|
6065
|
+
// Cycle 9 B4: defense-in-depth — validate id even though the catalog-iter
|
|
6066
|
+
// upstream is trusted. A corrupt catalog returning a malformed id would
|
|
6067
|
+
// otherwise reach loadPlaybook unchecked. Matches the cmdRunMulti pattern.
|
|
6068
|
+
const idCheck = validateIdComponent(id, "playbook");
|
|
6069
|
+
if (!idCheck.ok) {
|
|
6070
|
+
results.push({ playbook_id: id, ok: false, error: idCheck.reason });
|
|
6071
|
+
fail = true;
|
|
6072
|
+
continue;
|
|
6073
|
+
}
|
|
6064
6074
|
let pb;
|
|
6065
6075
|
try { pb = runner.loadPlaybook(id); }
|
|
6066
6076
|
catch (e) { results.push({ playbook_id: id, ok: false, error: e.message }); fail = true; continue; }
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-05-
|
|
3
|
+
"generated_at": "2026-05-16T01:17:57.741Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
|
-
"source_count":
|
|
5
|
+
"source_count": 54,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
8
|
-
"data/atlas-ttps.json": "
|
|
9
|
-
"data/attack-techniques.json": "
|
|
10
|
-
"data/cve-catalog.json": "
|
|
11
|
-
"data/cwe-catalog.json": "
|
|
12
|
-
"data/d3fend-catalog.json": "
|
|
7
|
+
"manifest.json": "5b9c21d9c2d885b439990b7b499429bf8a59e69cc737abfb386aa0255f2e8228",
|
|
8
|
+
"data/atlas-ttps.json": "259e76e4252c7a56c17bbe96982a5e37ac89131c2d37a547fe38d64dcacfd763",
|
|
9
|
+
"data/attack-techniques.json": "51f60819aef36e960fd768e44dcc725e137781534fbbb028e5ef6baa21defa1d",
|
|
10
|
+
"data/cve-catalog.json": "72164f10238b4ba26a6c2fcacd0dfa3e745cd83b7c7c525ca26d8069511a4f24",
|
|
11
|
+
"data/cwe-catalog.json": "b6d1a950e9dec8b313f65a546dcff724bf27d3717deca74decc04a6ba15d4538",
|
|
12
|
+
"data/d3fend-catalog.json": "ac7c1b0ba5cc84754264846b8173011ca4328773dd981c7b42599e112e54b3c4",
|
|
13
13
|
"data/dlp-controls.json": "8ea8d907aea0a2cfd772b048a62122a322ba3284a5c36a272ad5e9d392564cb5",
|
|
14
14
|
"data/exploit-availability.json": "a9eeda95d24b56c28a0d0178fc601b531653e2ba7dc857160b35ad23ad6c7471",
|
|
15
|
-
"data/framework-control-gaps.json": "
|
|
15
|
+
"data/framework-control-gaps.json": "0c4aa0d3c48da3b3a88d0b9faa078003af76a809b63d00f1da1c504738872a06",
|
|
16
16
|
"data/global-frameworks.json": "0168825497e03f079274c9da2e5529310a2ba5bd7c7da7c93acd0b66ed845b8a",
|
|
17
|
-
"data/rfc-references.json": "
|
|
17
|
+
"data/rfc-references.json": "e90ec6755f6a670fdb589bbdc61b3010c90531da46065ada377272f34d282fcb",
|
|
18
18
|
"data/zeroday-lessons.json": "d960e5f8ca7a83c10194cd60207e13046a7eee1b8793e2f3de79475db283f800",
|
|
19
19
|
"skills/kernel-lpe-triage/skill.md": "8e94bfd38d6db47342fbbe95a0c8df8f7c38743982c13e9de6a1c59cd3783d33",
|
|
20
20
|
"skills/ai-attack-surface/skill.md": "13e543fc92b9b27cdb647dce96a9eeb44919e0fa92ec41e8265a9981a23e7b79",
|
|
@@ -53,36 +53,39 @@
|
|
|
53
53
|
"skills/container-runtime-security/skill.md": "f06260f0c468d6a4f0409294899017edab45c98d71db1fedd7a630fe6a7bf53a",
|
|
54
54
|
"skills/mlops-security/skill.md": "e6a296fc67724aa3b026c0039f44867b44cf0926eade4fe616bfd0a4c77310bf",
|
|
55
55
|
"skills/incident-response-playbook/skill.md": "8ef7ce1246dc1329b6df3cc9de8d79d35e2c02c703dcef20f35b312b1c24fd52",
|
|
56
|
+
"skills/ransomware-response/skill.md": "ffe07ba8c196aabceb69b07dafa7a9c3ca2ec8e5ce079107f4eec82512a01be1",
|
|
56
57
|
"skills/email-security-anti-phishing/skill.md": "b5a7693b3ddbd6cd83303d092bc5e324db431245d25c4945d9f65fcffa1995e7",
|
|
57
|
-
"skills/age-gates-child-safety/skill.md": "c741d7dca9da0abb09bdebb8a02e803ce4ae9fb9a6904fb8df3ec19cae83917d"
|
|
58
|
+
"skills/age-gates-child-safety/skill.md": "c741d7dca9da0abb09bdebb8a02e803ce4ae9fb9a6904fb8df3ec19cae83917d",
|
|
59
|
+
"skills/cloud-iam-incident/skill.md": "35a9dd108679103c0eca54ea0c5b8f3db0a199975f87e20660b2c3a11440f40b",
|
|
60
|
+
"skills/idp-incident-response/skill.md": "39b5b492914e9092fe1c0b2be5af83d4ed869939996b6a201f2d0cd8142ab8f3"
|
|
58
61
|
},
|
|
59
|
-
"skill_count":
|
|
62
|
+
"skill_count": 42,
|
|
60
63
|
"catalog_count": 11,
|
|
61
64
|
"index_stats": {
|
|
62
65
|
"xref_entries": {
|
|
63
|
-
"cwe_refs":
|
|
64
|
-
"d3fend_refs":
|
|
65
|
-
"framework_gaps":
|
|
66
|
+
"cwe_refs": 36,
|
|
67
|
+
"d3fend_refs": 21,
|
|
68
|
+
"framework_gaps": 80,
|
|
66
69
|
"atlas_refs": 10,
|
|
67
|
-
"attack_refs":
|
|
68
|
-
"rfc_refs":
|
|
70
|
+
"attack_refs": 39,
|
|
71
|
+
"rfc_refs": 23,
|
|
69
72
|
"dlp_refs": 0
|
|
70
73
|
},
|
|
71
|
-
"trigger_table_entries":
|
|
74
|
+
"trigger_table_entries": 538,
|
|
72
75
|
"chains_cve_entries": 27,
|
|
73
76
|
"chains_cwe_entries": 55,
|
|
74
77
|
"jurisdictions_indexed": 29,
|
|
75
|
-
"handoff_dag_nodes":
|
|
76
|
-
"summary_cards":
|
|
77
|
-
"section_offsets_skills":
|
|
78
|
-
"token_budget_total_approx":
|
|
78
|
+
"handoff_dag_nodes": 42,
|
|
79
|
+
"summary_cards": 42,
|
|
80
|
+
"section_offsets_skills": 42,
|
|
81
|
+
"token_budget_total_approx": 397336,
|
|
79
82
|
"recipes": 8,
|
|
80
83
|
"jurisdiction_clocks": 29,
|
|
81
84
|
"did_ladders": 8,
|
|
82
85
|
"theater_fingerprints": 7,
|
|
83
86
|
"currency_action_required": 0,
|
|
84
87
|
"frequency_fields": 7,
|
|
85
|
-
"activity_feed_events":
|
|
88
|
+
"activity_feed_events": 54,
|
|
86
89
|
"catalog_summaries": 11,
|
|
87
90
|
"stale_content_findings": 3
|
|
88
91
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"_meta": {
|
|
3
3
|
"schema_version": "1.0.0",
|
|
4
4
|
"note": "Per-artifact 'last changed' feed sorted descending by date. Skill events from manifest.last_threat_review; catalog events from data/<catalog>.json _meta.last_updated.",
|
|
5
|
-
"event_count":
|
|
5
|
+
"event_count": 54
|
|
6
6
|
},
|
|
7
7
|
"events": [
|
|
8
8
|
{
|
|
@@ -12,6 +12,27 @@
|
|
|
12
12
|
"path": "skills/sector-telecom/skill.md",
|
|
13
13
|
"note": "Telecom and 5G security for mid-2026 — Salt Typhoon, Volt Typhoon, CALEA / IPA-LI gateway compromise, signaling-protocol abuse (SS7 / Diameter / GTP), 5G N6 / N9 isolation, gNB / DU / CU integrity, OEM-equipment supply-chain compromise, AI-RAN / O-RAN security"
|
|
14
14
|
},
|
|
15
|
+
{
|
|
16
|
+
"date": "2026-05-15",
|
|
17
|
+
"type": "skill_review",
|
|
18
|
+
"artifact": "ransomware-response",
|
|
19
|
+
"path": "skills/ransomware-response/skill.md",
|
|
20
|
+
"note": "Ransomware-specific incident response — OFAC SDN sanctions screening as payment-posture blocker, EU Reg 2014/833 + UK OFSI + AU DFAT + JP MOF cross-jurisdiction sanctions lookups, decryptor availability via No More Ransom + vendor-specific catalogs, cyber-insurance carrier 24h notification, negotiator-engagement legal posture, immutable-backup viability test, PHI exfil-before-encrypt as distinct breach class, parallel jurisdiction clocks"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"date": "2026-05-15",
|
|
24
|
+
"type": "skill_review",
|
|
25
|
+
"artifact": "cloud-iam-incident",
|
|
26
|
+
"path": "skills/cloud-iam-incident/skill.md",
|
|
27
|
+
"note": "Cloud-IAM incident response for AWS / GCP / Azure — account takeover, IAM role assumption abuse, access-key compromise, cross-account assume-role chains, federated-trust attacks, IMDS metadata exfiltration, and Snowflake-AA24-class IdP-to-cloud credential reuse"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"date": "2026-05-15",
|
|
31
|
+
"type": "skill_review",
|
|
32
|
+
"artifact": "idp-incident-response",
|
|
33
|
+
"path": "skills/idp-incident-response/skill.md",
|
|
34
|
+
"note": "Identity-provider incident response for mid-2026 — Okta, Entra ID, Auth0, Ping, OneLogin tenant compromise, federated-trust abuse, OAuth app consent abuse, Midnight Blizzard and Scattered Spider TTPs against the IdP control plane"
|
|
35
|
+
},
|
|
15
36
|
{
|
|
16
37
|
"date": "2026-05-15",
|
|
17
38
|
"type": "catalog_update",
|
|
@@ -26,7 +47,7 @@
|
|
|
26
47
|
"artifact": "data/attack-techniques.json",
|
|
27
48
|
"path": "data/attack-techniques.json",
|
|
28
49
|
"schema_version": "1.0.0",
|
|
29
|
-
"entry_count":
|
|
50
|
+
"entry_count": 98
|
|
30
51
|
},
|
|
31
52
|
{
|
|
32
53
|
"date": "2026-05-15",
|
|
@@ -42,7 +63,7 @@
|
|
|
42
63
|
"artifact": "data/framework-control-gaps.json",
|
|
43
64
|
"path": "data/framework-control-gaps.json",
|
|
44
65
|
"schema_version": "1.0.0",
|
|
45
|
-
"entry_count":
|
|
66
|
+
"entry_count": 109
|
|
46
67
|
},
|
|
47
68
|
{
|
|
48
69
|
"date": "2026-05-15",
|
|
@@ -58,7 +79,7 @@
|
|
|
58
79
|
"artifact": "data/rfc-references.json",
|
|
59
80
|
"path": "data/rfc-references.json",
|
|
60
81
|
"schema_version": "1.0.0",
|
|
61
|
-
"entry_count":
|
|
82
|
+
"entry_count": 41
|
|
62
83
|
},
|
|
63
84
|
{
|
|
64
85
|
"date": "2026-05-15",
|
|
@@ -68,6 +89,13 @@
|
|
|
68
89
|
"schema_version": "1.1.0",
|
|
69
90
|
"entry_count": 15
|
|
70
91
|
},
|
|
92
|
+
{
|
|
93
|
+
"date": "2026-05-15",
|
|
94
|
+
"type": "manifest_review",
|
|
95
|
+
"artifact": "manifest.json",
|
|
96
|
+
"path": "manifest.json",
|
|
97
|
+
"note": "manifest threat_review_date — 42 skills, 11 catalogs"
|
|
98
|
+
},
|
|
71
99
|
{
|
|
72
100
|
"date": "2026-05-13",
|
|
73
101
|
"type": "catalog_update",
|
|
@@ -365,13 +393,6 @@
|
|
|
365
393
|
"artifact": "security-maturity-tiers",
|
|
366
394
|
"path": "skills/security-maturity-tiers/skill.md",
|
|
367
395
|
"note": "Three-tier implementation roadmap — MVP (ship this week), Practical (scalable today), Overkill (defense-in-depth)"
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
"date": "2026-05-01",
|
|
371
|
-
"type": "manifest_review",
|
|
372
|
-
"artifact": "manifest.json",
|
|
373
|
-
"path": "manifest.json",
|
|
374
|
-
"note": "manifest threat_review_date — 39 skills, 11 catalogs"
|
|
375
396
|
}
|
|
376
397
|
]
|
|
377
398
|
}
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"rebuild_after_days": 365,
|
|
41
41
|
"note": "Catalog must be rebuilt against the upstream ATT&CK release whenever MITRE publishes a new version. AGENTS.md external-data version-pinning rule requires the bump to be intentional, not silent. ATT&CK ships semi-annually (April + October); audit on each release for tactic moves, technique splits, and new Detection Strategies."
|
|
42
42
|
},
|
|
43
|
-
"entry_count":
|
|
43
|
+
"entry_count": 98,
|
|
44
44
|
"sample_keys": [
|
|
45
45
|
"T0001",
|
|
46
46
|
"T0017",
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
"rebuild_after_days": 365,
|
|
173
173
|
"note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
|
|
174
174
|
},
|
|
175
|
-
"entry_count":
|
|
175
|
+
"entry_count": 109,
|
|
176
176
|
"sample_keys": [
|
|
177
177
|
"ALL-AI-PIPELINE-INTEGRITY",
|
|
178
178
|
"ALL-MCP-TOOL-TRUST",
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
"rebuild_after_days": 365,
|
|
217
217
|
"note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
|
|
218
218
|
},
|
|
219
|
-
"entry_count":
|
|
219
|
+
"entry_count": 41,
|
|
220
220
|
"sample_keys": [
|
|
221
221
|
"RFC-4301",
|
|
222
222
|
"RFC-4303",
|