@blamejs/exceptd-skills 0.12.25 → 0.12.27

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.
@@ -1964,5 +1964,167 @@
1964
1964
  "attack_refs": [
1965
1965
  "T1195.001"
1966
1966
  ]
1967
+ },
1968
+ "FCC-CPNI-4.1": {
1969
+ "framework": "FCC-CPNI",
1970
+ "control_id": "47-CFR-64.2009(e)",
1971
+ "control_name": "CPNI Annual Certification + Operational Compliance",
1972
+ "designed_for": "Annual certification by US telecom carriers that they have established operating procedures to comply with CPNI rules; covers customer-proprietary network info disclosure to law enforcement and third parties.",
1973
+ "misses": [
1974
+ "Lawful-intercept (LI) gateway compromise detection — CPNI rules predate Salt Typhoon-class adversary access to CALEA-mandated systems",
1975
+ "Anomalous LI activation requests from compromised admin accounts",
1976
+ "Cross-PLMN signaling spike detection (SS7 / Diameter / GTP)",
1977
+ "OEM firmware drift attestation on equipment that touches CPNI flows"
1978
+ ],
1979
+ "real_requirement": "Annual CPNI certification PLUS quarterly LI-gateway activation audit (PRC / Salt-Typhoon-class threat model), gNB firmware hash attestation, signaling-anomaly baselines per PLMN-pair.",
1980
+ "status": "open",
1981
+ "opened_date": "2026-05-15",
1982
+ "evidence_cves": [],
1983
+ "atlas_refs": ["AML.T0040"],
1984
+ "attack_refs": ["T1078", "T1098", "T1199"]
1985
+ },
1986
+ "FCC-Cyber-Incident-Notification-2024": {
1987
+ "framework": "FCC",
1988
+ "control_id": "47-CFR-64.2011",
1989
+ "control_name": "FCC Cyber Incident Notification (4 business days)",
1990
+ "designed_for": "Notification rule requiring US telecom carriers to report PII or CPNI breaches within 4 business days of discovery (effective 2024-03-13).",
1991
+ "misses": [
1992
+ "No requirement to notify on lawful-intercept-system compromise that does not exfiltrate PII directly (e.g. Salt Typhoon access to LI feeds covering counter-intelligence targets)",
1993
+ "No requirement to notify on signaling-protocol intrusion (SS7 / Diameter abuse) absent PII loss",
1994
+ "4-business-day window is too slow for an ongoing nation-state campaign — peer regulators set tighter limits (NIS2 24h, DORA 4h)",
1995
+ "No structured-data feed for cross-carrier IOC sharing"
1996
+ ],
1997
+ "real_requirement": "4-business-day window paired with 24-hour preliminary signal-flag for LI-system compromise + structured CISA / NSA / FBI IOC handoff format.",
1998
+ "status": "open",
1999
+ "opened_date": "2026-05-15",
2000
+ "evidence_cves": [],
2001
+ "atlas_refs": [],
2002
+ "attack_refs": ["T1199", "T1078"]
2003
+ },
2004
+ "NIS2-Annex-I-Telecom": {
2005
+ "framework": "NIS2",
2006
+ "control_id": "Annex-I-Telecommunications",
2007
+ "control_name": "NIS2 Annex I — telecommunications essential entities",
2008
+ "designed_for": "NIS2 Directive (EU) 2022/2555 Annex I classifies telecom providers as essential entities, mandating risk management measures + 24h incident notification + supply-chain due diligence.",
2009
+ "misses": [
2010
+ "No specific obligations on lawful-intercept-system access controls (national-security scope deferred to MS-level law)",
2011
+ "Supply-chain due diligence (Art. 21(2)(d)) does not name OEM-vendor-equipment firmware integrity attestation specifically",
2012
+ "AI-RAN security obligations absent — O-RAN deployments span the entity boundary in ways the NIS2 risk-management framework does not yet model",
2013
+ "24h notification clock starts at significant-incident-detection but signaling-protocol intrusion + slow-roll campaigns evade the trigger"
2014
+ ],
2015
+ "real_requirement": "Annex I + explicit LI-gateway operator-attested firmware hash + AI-RAN model-tampering controls + cross-PLMN signaling baseline obligation.",
2016
+ "status": "open",
2017
+ "opened_date": "2026-05-15",
2018
+ "evidence_cves": [],
2019
+ "atlas_refs": ["AML.T0040"],
2020
+ "attack_refs": ["T1199", "T1078", "T1098"]
2021
+ },
2022
+ "DORA-Art-21-Telecom-ICT": {
2023
+ "framework": "DORA",
2024
+ "control_id": "Art-21-Telecom-ICT",
2025
+ "control_name": "DORA Art. 21 — ICT third-party risk (telecom-adjacent application)",
2026
+ "designed_for": "DORA Reg. (EU) 2022/2554 Art. 21 ICT third-party risk management; applies to financial entities consuming telecom-provided ICT services.",
2027
+ "misses": [
2028
+ "Telecom-to-financial trust boundary is asymmetric — DORA binds the financial entity but telecom providers (essential entities under NIS2) may not align reporting cadences",
2029
+ "Lawful-intercept access by the telecom upstream is not in DORA scope but creates a parallel data-exposure surface",
2030
+ "CTPP (critical third-party provider) oversight does not yet cover OEM equipment vendors transitively",
2031
+ "No bridge to 5G slice-isolation obligations for financial-sector dedicated network slices"
2032
+ ],
2033
+ "real_requirement": "DORA Art. 21 + alignment with NIS2 telecom-essential-entity reporting + slice-isolation attestation for finance-dedicated 5G slices.",
2034
+ "status": "open",
2035
+ "opened_date": "2026-05-15",
2036
+ "evidence_cves": [],
2037
+ "atlas_refs": [],
2038
+ "attack_refs": ["T1199"]
2039
+ },
2040
+ "UK-CAF-B5": {
2041
+ "framework": "UK-CAF",
2042
+ "control_id": "Principle-B5",
2043
+ "control_name": "Resilient networks and systems",
2044
+ "designed_for": "NCSC Cyber Assessment Framework Principle B5 — outcome-tested network resilience for essential service operators (incl. telecom under TSA 2021).",
2045
+ "misses": [
2046
+ "Signaling-protocol attack-surface (SS7 / Diameter / GTP) not in CAF B5 outcome tests",
2047
+ "gNB / DU / CU integrity attestation not modeled — CAF B5 expects network-availability resilience, not equipment-supply-chain attestation",
2048
+ "Lawful-intercept access path covered by separate IPA 2016 + TSA 2021 + Code of Practice — not CAF scope",
2049
+ "AI-RAN slice-isolation testing not in the CAF outcome catalog"
2050
+ ],
2051
+ "real_requirement": "CAF B5 + signaling-anomaly detection + gNB firmware attestation outcome test + slice-isolation outcome test.",
2052
+ "status": "open",
2053
+ "opened_date": "2026-05-15",
2054
+ "evidence_cves": [],
2055
+ "atlas_refs": [],
2056
+ "attack_refs": ["T1199", "T1078"]
2057
+ },
2058
+ "AU-ISM-1556": {
2059
+ "framework": "au-ism",
2060
+ "control_id": "ISM-1556",
2061
+ "control_name": "Multi-factor authentication for privileged users (telecom NMS application)",
2062
+ "designed_for": "Australian Government ISM control ISM-1556 — phishing-resistant MFA for privileged users + remote access.",
2063
+ "misses": [
2064
+ "Telecom NMS service accounts (which hold gNB / EMS / OSS access) often bypass human MFA",
2065
+ "Service-account credential management policy is ISM-1559 (covered separately); ISM-1556 alone is insufficient for telecom OEM-vendor support tunnels",
2066
+ "Lawful-intercept gateway operator credentials specifically uncovered — these are not always classified as privileged in telecom RBAC models",
2067
+ "OEM remote-support inbound tunnels (Cisco TAC, Ericsson ENS) often pass credentials via shared mailbox — defeats MFA intent"
2068
+ ],
2069
+ "real_requirement": "ISM-1556 + telecom-NMS service-account FIDO2 enforcement + LI-gateway-specific MFA + OEM remote-support-tunnel federated-MFA mandate.",
2070
+ "status": "open",
2071
+ "opened_date": "2026-05-15",
2072
+ "evidence_cves": [],
2073
+ "atlas_refs": [],
2074
+ "attack_refs": ["T1078", "T1098"]
2075
+ },
2076
+ "GSMA-NESAS-Deployment": {
2077
+ "framework": "GSMA-NESAS",
2078
+ "control_id": "NESAS-Deployment-Gap",
2079
+ "control_name": "NESAS at-deployment posture",
2080
+ "designed_for": "GSMA Network Equipment Security Assurance Scheme — product-time certification of telecom OEM equipment against 3GPP SCAS test cases (GSMA FS.13 / FS.14 / FS.15).",
2081
+ "misses": [
2082
+ "Certification is product-time (one snapshot, vendor-attested); deployment posture drifts as firmware / config evolves",
2083
+ "No post-deployment attested-runtime check — operator has no canonical way to confirm a running gNB matches the certified build",
2084
+ "Firmware update cadence is not tied to NESAS re-certification — vendor patches between certifications run uncertified",
2085
+ "NESAS scope excludes the EMS / OSS / NMS systems that operate the equipment, which is where Salt Typhoon-class campaigns gained access"
2086
+ ],
2087
+ "real_requirement": "NESAS product-time certification PLUS operator-attested-runtime gNB hash + EMS / OSS NESAS-equivalent scheme + firmware-update-cadence-tied recertification.",
2088
+ "status": "open",
2089
+ "opened_date": "2026-05-15",
2090
+ "evidence_cves": [],
2091
+ "atlas_refs": [],
2092
+ "attack_refs": ["T1199"]
2093
+ },
2094
+ "3GPP-TR-33.926": {
2095
+ "framework": "3GPP",
2096
+ "control_id": "TR-33.926",
2097
+ "control_name": "3GPP Security Assurance Specification (gNB / eNB)",
2098
+ "designed_for": "3GPP TR 33.926 Security Assurance Specification — security test cases applied against the gNB / eNB product class, paired with the broader 5G security architecture in TS 33.501.",
2099
+ "misses": [
2100
+ "TR 33.926 covers the equipment itself; the AI-RAN / O-RAN deployment is outside scope (O-RAN SFG / WG11 handles separately)",
2101
+ "Test cases assume deterministic equipment behavior — adversary-modified firmware that passes TR 33.926 tests at submission time is undetected after the fact",
2102
+ "N6 / N9 interface isolation testing is in TS 33.501 not TR 33.926 — operators consume both, gap is at the join",
2103
+ "No bridge to ISM-1556-class operator-account hardening for the NMS that operates the certified equipment"
2104
+ ],
2105
+ "real_requirement": "TR 33.926 + post-deployment hash-attestation + O-RAN security WG11 alignment + cross-spec join testing between TR 33.926 and TS 33.501.",
2106
+ "status": "open",
2107
+ "opened_date": "2026-05-15",
2108
+ "evidence_cves": [],
2109
+ "atlas_refs": [],
2110
+ "attack_refs": ["T1199"]
2111
+ },
2112
+ "ITU-T-X.805": {
2113
+ "framework": "ITU-T",
2114
+ "control_id": "X.805",
2115
+ "control_name": "ITU-T X.805 — 8-dimension security architecture for end-to-end communications",
2116
+ "designed_for": "ITU-T Recommendation X.805 (2003) — generic 8-dimension security architecture (access control, authentication, non-repudiation, data confidentiality, communication security, data integrity, availability, privacy) for telecom networks.",
2117
+ "misses": [
2118
+ "Specification predates 5G, O-RAN, AI-RAN, and CALEA / IPA-LI surface evolution; control language remains generic",
2119
+ "No mapping to modern threat models (Salt Typhoon, signaling-protocol abuse)",
2120
+ "Treated as reference architecture, not as a deployment-validation framework — operators rarely audit posture against X.805 dimensions directly",
2121
+ "No bridge to NESAS / 3GPP / NIS2 / FCC CPNI specific obligations"
2122
+ ],
2123
+ "real_requirement": "X.805 8-dimension framing PLUS modern-threat-model annexes (LI-system compromise, signaling-protocol abuse, slice-isolation) + deployment-validation checklist.",
2124
+ "status": "open",
2125
+ "opened_date": "2026-05-15",
2126
+ "evidence_cves": [],
2127
+ "atlas_refs": [],
2128
+ "attack_refs": ["T1199"]
1967
2129
  }
1968
2130
  }
@@ -463,7 +463,8 @@
463
463
  "relevance": "Transport Services (TAPS) architecture — abstracts the choice of underlying transport (TCP, QUIC, SCTP) behind a unified API that selects the protocol at runtime based on path conditions. Forward-watch relevance: webapp / AI-API client libraries that adopt TAPS may transparently shift between TCP-HTTP/2 and QUIC-HTTP/3 mid-session, complicating boundary inspection assumptions that pin to a single transport. Currently tracked as a deployment-watch item rather than an operational control point.",
464
464
  "lag_notes": "Architecture document; companion documents (TAPS implementation, TAPS programming interface) are still in progress at IETF TAPS WG. Enterprise tooling impact is forward-looking.",
465
465
  "skills_referencing": [
466
- "webapp-security"
466
+ "webapp-security",
467
+ "sector-telecom"
467
468
  ],
468
469
  "last_verified": "2026-05-15"
469
470
  },
@@ -79,21 +79,25 @@ const VERB_FLAG_ALLOWLIST = Object.freeze({
79
79
  'mode', 'air-gap', 'force-stale', 'operator', 'ack', 'csaf-status',
80
80
  'publisher-namespace', 'vex', 'all', 'scope', 'required', 'format',
81
81
  'strict-preconditions', 'block-on-jurisdiction-clock', 'tlp',
82
+ 'bundle-deterministic', 'bundle-epoch',
82
83
  ],
83
84
  'run-all': [
84
85
  'evidence', 'evidence-dir', 'session-id', 'force-overwrite', 'attestation-root',
85
86
  'mode', 'air-gap', 'force-stale', 'operator', 'ack', 'csaf-status',
86
87
  'publisher-namespace', 'vex', 'scope', 'strict-preconditions', 'tlp',
88
+ 'bundle-deterministic', 'bundle-epoch',
87
89
  ],
88
90
  'ai-run': [
89
91
  'evidence', 'no-stream', 'session-id', 'force-overwrite', 'attestation-root',
90
92
  'operator', 'ack', 'csaf-status', 'publisher-namespace', 'air-gap',
91
93
  'mode', 'force-stale', 'tlp',
94
+ 'bundle-deterministic', 'bundle-epoch',
92
95
  ],
93
96
  ingest: [
94
97
  'evidence', 'session-id', 'force-overwrite', 'attestation-root', 'operator',
95
98
  'ack', 'csaf-status', 'publisher-namespace', 'air-gap', 'force-stale',
96
99
  'strict-preconditions',
100
+ 'bundle-deterministic', 'bundle-epoch',
97
101
  ],
98
102
  brief: ['all', 'scope', 'directives', 'flat', 'phase'],
99
103
  discover: ['scan-only', 'scope'],
@@ -1515,6 +1515,13 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1515
1515
  // bypass run() (e.g. unit tests).
1516
1516
  const sessionId = runOpts.session_id || crypto.randomBytes(8).toString('hex');
1517
1517
 
1518
+ // v0.12.27: when opt-in deterministic bundle mode is set, resolve the
1519
+ // single frozen epoch used by every timestamp surface below. Cached for
1520
+ // the whole close() call so notification_actions, regression_schedule,
1521
+ // and the bundle emitter all agree on the same Date.
1522
+ const deterministic = runOpts.bundleDeterministic === true;
1523
+ const frozenEpoch = deterministic ? resolveFrozenEpoch(runOpts, playbook) : null;
1524
+
1518
1525
  // notification_actions — compute ISO deadlines from clock_starts events.
1519
1526
  // v0.11.12 (#123): enrich each entry with the matched obligation's
1520
1527
  // jurisdiction/regulation/window_hours/evidence_required fields. The
@@ -1605,7 +1612,10 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1605
1612
  framework_id: playbook.domain.frameworks_in_scope[0] || 'unspecified',
1606
1613
  control_id: analyzeResult.framework_gap_mapping?.[0]?.claimed_control || 'unspecified',
1607
1614
  ciso_name: agentSignals.ciso_name || '<CISO NAME>',
1608
- acceptance_date: new Date().toISOString().slice(0, 10),
1615
+ // v0.12.27: deterministic mode roots acceptance_date in the
1616
+ // frozen epoch so two runs against the same evidence emit the
1617
+ // same auditor-facing date.
1618
+ acceptance_date: (deterministic ? frozenEpoch : new Date().toISOString()).slice(0, 10),
1609
1619
  duration_expiry: agentSignals.duration_expiry || 'until vendor patch'
1610
1620
  })
1611
1621
  };
@@ -1628,7 +1638,11 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1628
1638
  // spurious millisecond drift on tracking.initial_release_date /
1629
1639
  // timestamp / current_release_date.
1630
1640
  const evidencePackage = c.evidence_package ? (() => {
1631
- const issuedAt = new Date().toISOString();
1641
+ // v0.12.27: deterministic mode pins issuedAt to the frozen epoch so
1642
+ // CSAF tracking.{initial_release_date,current_release_date,
1643
+ // generator.date,revision_history[0].date} and OpenVEX timestamp +
1644
+ // statements[].timestamp all collapse to a single, byte-stable value.
1645
+ const issuedAt = deterministic ? frozenEpoch : new Date().toISOString();
1632
1646
  const builtFormats = new Map();
1633
1647
  const buildOnce = (format) => {
1634
1648
  if (!builtFormats.has(format)) {
@@ -1680,11 +1694,27 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1680
1694
  } : { enabled: false };
1681
1695
 
1682
1696
  // regression_schedule
1683
- const regressionSchedule = c.regression_schedule ? {
1684
- next_run: validateResult.regression_next_run,
1685
- trigger: c.regression_schedule.trigger,
1686
- notify_on_skip: c.regression_schedule.notify_on_skip !== false
1687
- } : null;
1697
+ //
1698
+ // v0.12.27: deterministic mode re-derives next_run from the frozen epoch
1699
+ // rather than wall-clock-now-at-validate-time. Without this, two runs
1700
+ // against the same evidence diverge on next_run by the interval between
1701
+ // the two `validate()` invocations. Frozen base + the same interval set
1702
+ // = byte-identical schedule.
1703
+ const regressionSchedule = c.regression_schedule ? (() => {
1704
+ let nextRun = validateResult.regression_next_run;
1705
+ if (deterministic) {
1706
+ // Re-derive against the validate phase's trigger set (not the
1707
+ // close phase's regression_schedule subtree — close has no triggers
1708
+ // of its own, just the canonical interval declared upstream).
1709
+ const v = resolvedPhase(playbook, directiveId, 'validate');
1710
+ nextRun = frozenRegressionNextRun(v.regression_trigger || [], new Date(frozenEpoch));
1711
+ }
1712
+ return {
1713
+ next_run: nextRun,
1714
+ trigger: c.regression_schedule.trigger,
1715
+ notify_on_skip: c.regression_schedule.notify_on_skip !== false
1716
+ };
1717
+ })() : null;
1688
1718
 
1689
1719
  // feeds_into chaining — full analyze result is exposed so conditions can
1690
1720
  // reference `analyze.compliance_theater_check.verdict` etc.
@@ -1996,6 +2026,40 @@ function getEngineVersion() {
1996
2026
  return _CACHED_PKG_VERSION;
1997
2027
  }
1998
2028
 
2029
+ // v0.12.27: deterministic-bundle epoch resolution. Priority:
2030
+ // 1. runOpts.bundleEpoch (operator-supplied --bundle-epoch <ISO>)
2031
+ // 2. playbook._meta.last_threat_review (the freshness anchor that already
2032
+ // gates every shipped playbook — stable across re-runs of the same
2033
+ // catalog version)
2034
+ // 3. '1970-01-01T00:00:00Z' fallback (effectively impossible in practice
2035
+ // because every shipped playbook carries last_threat_review, but
2036
+ // guarantees the deterministic path never crashes on a malformed
2037
+ // playbook).
2038
+ // Returns a full ISO-8601 timestamp (date-only inputs are normalised).
2039
+ function resolveFrozenEpoch(runOpts, playbook) {
2040
+ const raw = runOpts && runOpts.bundleEpoch
2041
+ ? runOpts.bundleEpoch
2042
+ : (playbook && playbook._meta && playbook._meta.last_threat_review)
2043
+ || '1970-01-01T00:00:00Z';
2044
+ try { return new Date(raw).toISOString(); }
2045
+ catch { return '1970-01-01T00:00:00Z'; }
2046
+ }
2047
+
2048
+ // Recompute regression_schedule.next_run against a frozen `now` so two
2049
+ // deterministic-mode runs of the same playbook produce byte-identical
2050
+ // schedules. Mirrors computeRegressionNextRun but with an injected base
2051
+ // date. Returns the soonest ISO timestamp or null when no interval-based
2052
+ // trigger fired.
2053
+ function frozenRegressionNextRun(triggers, frozenNow) {
2054
+ let soonest = null;
2055
+ for (const t of (triggers || [])) {
2056
+ const parsed = parseInterval(t.interval, frozenNow);
2057
+ if (!parsed || !parsed.date) continue;
2058
+ if (!soonest || parsed.date < soonest) soonest = parsed.date;
2059
+ }
2060
+ return soonest ? soonest.toISOString() : null;
2061
+ }
2062
+
1999
2063
  // Operator-supplied identity strings (--operator) and publisher namespace
2000
2064
  // URLs (--publisher-namespace) flow into operator-facing CSAF surfaces.
2001
2065
  // Strip ASCII control characters as defence in depth — bin/exceptd.js
@@ -2410,7 +2474,19 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
2410
2474
  if (branches.length > 0) tree.branches = branches;
2411
2475
  return tree;
2412
2476
  })(),
2413
- vulnerabilities: [...cveVulns, ...indicatorVulns],
2477
+ vulnerabilities: (function () {
2478
+ // v0.12.27: deterministic mode sorts vulnerabilities[] by their
2479
+ // primary identifier (cve_id for CVE entries, ids[0].text otherwise)
2480
+ // ascending. Default mode preserves insertion order so existing
2481
+ // operators see byte-identical output to pre-v0.12.27.
2482
+ const all = [...cveVulns, ...indicatorVulns];
2483
+ if (runOpts && runOpts.bundleDeterministic === true) {
2484
+ const keyOf = (v) => (typeof v.cve === 'string' && v.cve)
2485
+ || (Array.isArray(v.ids) && v.ids[0] && typeof v.ids[0].text === 'string' ? v.ids[0].text : '');
2486
+ return all.slice().sort((a, b) => keyOf(a).localeCompare(keyOf(b)));
2487
+ }
2488
+ return all;
2489
+ })(),
2414
2490
  exceptd_extension: {
2415
2491
  classification: analyze._detect_classification,
2416
2492
  rwep: analyze.rwep,
@@ -2642,7 +2718,17 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
2642
2718
  author: 'exceptd',
2643
2719
  timestamp: issued,
2644
2720
  version: 1,
2645
- statements: [...cveStatements, ...indicatorStatements],
2721
+ statements: (function () {
2722
+ // v0.12.27: deterministic mode sorts statements[] by
2723
+ // vulnerability['@id'] ascending. Insertion order otherwise.
2724
+ const all = [...cveStatements, ...indicatorStatements];
2725
+ if (runOpts && runOpts.bundleDeterministic === true) {
2726
+ const keyOf = (s) => (s && s.vulnerability && typeof s.vulnerability['@id'] === 'string')
2727
+ ? s.vulnerability['@id'] : '';
2728
+ return all.slice().sort((a, b) => keyOf(a).localeCompare(keyOf(b)));
2729
+ }
2730
+ return all;
2731
+ })(),
2646
2732
  };
2647
2733
  }
2648
2734
 
@@ -2948,7 +3034,28 @@ function run(playbookId, directiveId, agentSubmission = {}, runOpts = {}) {
2948
3034
  // Without the single-source-of-truth, close() would mint its own id
2949
3035
  // and operators correlating attestation files to embedded bundle URNs
2950
3036
  // would see mismatches.
2951
- const sessionId = runOpts.session_id || crypto.randomBytes(8).toString('hex');
3037
+ //
3038
+ // v0.12.27: when runOpts.bundleDeterministic is set AND the operator did
3039
+ // not pass --session-id, derive the session_id from the submission shape
3040
+ // so two runs against identical evidence produce the same id (and
3041
+ // therefore the same CSAF tracking.id / OpenVEX @id / attestation file
3042
+ // name). Mirrors the evidence_hash path further down but is computed
3043
+ // here so close() can thread it through. Operator-supplied --session-id
3044
+ // still wins on collision.
3045
+ let sessionId;
3046
+ if (runOpts.session_id) {
3047
+ sessionId = runOpts.session_id;
3048
+ } else if (runOpts.bundleDeterministic) {
3049
+ const submissionDigest = crypto.createHash('sha256')
3050
+ .update(canonicalStringify(extractSubmissionForHash(agentSubmission)))
3051
+ .digest('hex');
3052
+ sessionId = crypto.createHash('sha256')
3053
+ .update(`${playbookId}\0${submissionDigest}\0${getEngineVersion()}`)
3054
+ .digest('hex')
3055
+ .slice(0, 16);
3056
+ } else {
3057
+ sessionId = crypto.randomBytes(8).toString('hex');
3058
+ }
2952
3059
  const cachedRunOpts = { ...runOpts, _playbookCache: playbook, session_id: sessionId };
2953
3060
  // Run-time error accumulator for evalCondition regex failures and other
2954
3061
  // non-fatal anomalies surfaced into analyze.runtime_errors[].
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "_comment": "Auto-generated by scripts/refresh-manifest-snapshot.js — do not hand-edit. Public skill surface used by check-manifest-snapshot.js to detect breaking removals.",
3
- "_generated_at": "2026-05-15T21:41:39.732Z",
3
+ "_generated_at": "2026-05-15T22:38:13.114Z",
4
4
  "atlas_version": "5.1.0",
5
- "skill_count": 38,
5
+ "skill_count": 39,
6
6
  "skills": [
7
7
  {
8
8
  "name": "age-gates-child-safety",
@@ -1543,6 +1543,79 @@
1543
1543
  "d3fend_refs": [],
1544
1544
  "dlp_refs": []
1545
1545
  },
1546
+ {
1547
+ "name": "sector-telecom",
1548
+ "version": "1.0.0",
1549
+ "triggers": [
1550
+ "3gpp tr 33.926",
1551
+ "3gpp ts 33.501",
1552
+ "4-business-day notification",
1553
+ "5g core",
1554
+ "au soci",
1555
+ "calea",
1556
+ "diameter",
1557
+ "fcc cpni",
1558
+ "gnb integrity",
1559
+ "gsma nesas",
1560
+ "gtp",
1561
+ "itu-t x.805",
1562
+ "lawful intercept",
1563
+ "n6 n9 isolation",
1564
+ "nis2 annex i",
1565
+ "o-ran",
1566
+ "salt typhoon",
1567
+ "ss7",
1568
+ "telecom security",
1569
+ "tssr",
1570
+ "uk tsa 2021",
1571
+ "volt typhoon"
1572
+ ],
1573
+ "data_deps": [
1574
+ "atlas-ttps.json",
1575
+ "cve-catalog.json",
1576
+ "cwe-catalog.json",
1577
+ "d3fend-catalog.json",
1578
+ "framework-control-gaps.json",
1579
+ "global-frameworks.json"
1580
+ ],
1581
+ "atlas_refs": [
1582
+ "AML.T0040"
1583
+ ],
1584
+ "attack_refs": [
1585
+ "T1071",
1586
+ "T1078",
1587
+ "T1098",
1588
+ "T1190",
1589
+ "T1199",
1590
+ "T1556"
1591
+ ],
1592
+ "framework_gaps": [
1593
+ "3GPP-TR-33.926",
1594
+ "AU-ISM-1556",
1595
+ "DORA-Art-21-Telecom-ICT",
1596
+ "FCC-CPNI-4.1",
1597
+ "FCC-Cyber-Incident-Notification-2024",
1598
+ "GSMA-NESAS-Deployment",
1599
+ "ITU-T-X.805",
1600
+ "NIS2-Annex-I-Telecom",
1601
+ "UK-CAF-B5"
1602
+ ],
1603
+ "rfc_refs": [
1604
+ "RFC-9622"
1605
+ ],
1606
+ "cwe_refs": [
1607
+ "CWE-287",
1608
+ "CWE-306",
1609
+ "CWE-918"
1610
+ ],
1611
+ "d3fend_refs": [
1612
+ "D3-IOPR",
1613
+ "D3-NI",
1614
+ "D3-NTA",
1615
+ "D3-NTPM"
1616
+ ],
1617
+ "dlp_refs": []
1618
+ },
1546
1619
  {
1547
1620
  "name": "security-maturity-tiers",
1548
1621
  "version": "1.0.0",
@@ -1 +1 @@
1
- e00285a7e3629aac068b46959038a8e9b1ead1ba7c722796c8c749f2b64ed764 manifest-snapshot.json
1
+ 259bbbc7ec375bfb21c2e8fe4c397cca265b33b357e19282d34acff932752237 manifest-snapshot.json