@blamejs/exceptd-skills 0.16.14 → 0.16.16
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 +3 -1
- package/CHANGELOG.md +8 -0
- package/README.md +5 -5
- package/bin/exceptd.js +3 -1
- package/data/_indexes/_meta.json +17 -15
- package/data/_indexes/activity-feed.json +16 -2
- package/data/_indexes/chains.json +7429 -451
- package/data/_indexes/currency.json +19 -1
- package/data/_indexes/frequency.json +135 -64
- package/data/_indexes/handoff-dag.json +9 -1
- package/data/_indexes/jurisdiction-map.json +11 -4
- package/data/_indexes/section-offsets.json +170 -0
- package/data/_indexes/stale-content.json +1 -1
- package/data/_indexes/summary-cards.json +77 -0
- package/data/_indexes/token-budget.json +103 -3
- package/data/_indexes/trigger-table.json +98 -1
- package/data/_indexes/xref.json +45 -4
- package/data/cwe-catalog.json +21 -5
- package/data/playbooks/cloud-iam-incident.json +26 -5
- package/data/playbooks/framework.json +2 -0
- package/data/playbooks/multitenancy-isolation.json +660 -0
- package/data/playbooks/sbom.json +21 -6
- package/data/playbooks/self-update-integrity.json +636 -0
- package/manifest-snapshot.json +106 -2
- package/manifest-snapshot.sha256 +1 -1
- package/manifest.json +160 -48
- package/package.json +2 -2
- package/sbom.cdx.json +92 -32
- package/skills/multitenancy-isolation/skill.md +83 -0
- package/skills/self-update-integrity/skill.md +79 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"id": "self-update-integrity",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"last_threat_review": "2026-06-02",
|
|
6
|
+
"threat_currency_score": 93,
|
|
7
|
+
"changelog": [
|
|
8
|
+
{
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"date": "2026-06-02",
|
|
11
|
+
"summary": "Initial seven-phase consumer-side self-update integrity playbook. Covers the receiving end of the software supply chain that publisher-side posture (library-author, supply-chain-integrity) does not: a self-update that applies without verifying a signature, verifies against an in-band (attacker-suppliable) key, accepts a signed-but-older version (downgrade / no anti-rollback), fetches over an unauthenticated channel, serves browser modules without Subresource Integrity, ignores C2PA content credentials or SCITT/TSA transparency receipts on received artifacts, or applies the update without gating on the verifier result. Maps to ATT&CK T1195.002 / T1574 and to NIST 800-53 SR-11 / SI-7, ISO 27001 A.8.19, NIS2 Art.21, and EU CRA Annex I secure-update obligations."
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"owner": "@blamejs/platform-security",
|
|
15
|
+
"air_gap_mode": false,
|
|
16
|
+
"scope": "service",
|
|
17
|
+
"preconditions": [
|
|
18
|
+
{
|
|
19
|
+
"id": "update-path-source-or-config-read",
|
|
20
|
+
"description": "Agent must read the operator's self-update / artifact-consumption source and/or runtime configuration to inspect signature verification, key pinning, anti-rollback, channel pinning, SRI, and provenance/transparency checks. A host with neither marks the playbook visibility_gap=no_update_path_inventory.",
|
|
21
|
+
"check": "agent_has_filesystem_read == true OR agent_has_update_config == true",
|
|
22
|
+
"on_fail": "halt"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"mutex": [],
|
|
26
|
+
"feeds_into": [
|
|
27
|
+
{
|
|
28
|
+
"playbook_id": "sbom",
|
|
29
|
+
"condition": "finding.includes_unverified_dependency_update == true"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"playbook_id": "framework",
|
|
33
|
+
"condition": "analyze.compliance_theater_check.verdict == 'theater'"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"domain": {
|
|
38
|
+
"name": "Consumer-side self-update / artifact integrity",
|
|
39
|
+
"attack_class": "supply-chain",
|
|
40
|
+
"atlas_refs": [],
|
|
41
|
+
"attack_refs": [
|
|
42
|
+
"T1195.002",
|
|
43
|
+
"T1574"
|
|
44
|
+
],
|
|
45
|
+
"cve_refs": [],
|
|
46
|
+
"cwe_refs": [
|
|
47
|
+
"CWE-494",
|
|
48
|
+
"CWE-829",
|
|
49
|
+
"CWE-353",
|
|
50
|
+
"CWE-347"
|
|
51
|
+
],
|
|
52
|
+
"frameworks_in_scope": [
|
|
53
|
+
"nist-800-53",
|
|
54
|
+
"iso-27001-2022",
|
|
55
|
+
"nis2",
|
|
56
|
+
"eu-cra",
|
|
57
|
+
"uk-caf",
|
|
58
|
+
"au-ism"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"phases": {
|
|
62
|
+
"govern": {
|
|
63
|
+
"jurisdiction_obligations": [
|
|
64
|
+
{
|
|
65
|
+
"jurisdiction": "EU",
|
|
66
|
+
"regulation": "EU CRA Annex I",
|
|
67
|
+
"obligation": "notify_supervisory_body",
|
|
68
|
+
"window_hours": 24,
|
|
69
|
+
"clock_starts": "detect_confirmed",
|
|
70
|
+
"evidence_required": [
|
|
71
|
+
"update_path_inventory",
|
|
72
|
+
"unverified_update_application_evidence"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"jurisdiction": "EU",
|
|
77
|
+
"regulation": "NIS2 Art.23",
|
|
78
|
+
"obligation": "notify_regulator",
|
|
79
|
+
"window_hours": 24,
|
|
80
|
+
"clock_starts": "detect_confirmed",
|
|
81
|
+
"evidence_required": [
|
|
82
|
+
"update_channel_compromise_scope",
|
|
83
|
+
"affected_installed_base"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"theater_fingerprints": [
|
|
88
|
+
{
|
|
89
|
+
"pattern_id": "updates-are-signed",
|
|
90
|
+
"claim": "Our updates are signed, so the update channel is secure.",
|
|
91
|
+
"fast_detection_test": "Signing is the PUBLISHER side. Ask whether the CLIENT verifies the signature, against a key pinned OUT OF BAND, BEFORE applying — and refuses downgrades. A signed update the client does not verify is theater."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"pattern_id": "https-is-enough",
|
|
95
|
+
"claim": "Updates come over HTTPS, so they cannot be tampered.",
|
|
96
|
+
"fast_detection_test": "HTTPS authenticates against a CA bundle and is defeated by a mis-issued cert. Ask whether the artifact itself is signature-verified with a pinned key independent of the channel."
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"pattern_id": "we-have-a-verifier",
|
|
100
|
+
"claim": "We have an update verifier.",
|
|
101
|
+
"fast_detection_test": "Ask whether the APPLY step is GATED on the verifier result and fails closed. A verifier whose output does not block activation is decorative."
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"framework_context": {
|
|
105
|
+
"gap_summary": "Org supply-chain controls focus on the publisher: signing, SBOM, SLSA. They do not require the CONSUMER side — that the receiving client verifies signatures against a pinned key before applying, refuses downgrades, pins the channel, enforces SRI on browser modules, and verifies provenance/transparency receipts.",
|
|
106
|
+
"lag_score": 72,
|
|
107
|
+
"per_framework_gaps": [
|
|
108
|
+
{
|
|
109
|
+
"framework": "nist-800-53",
|
|
110
|
+
"control_id": "SI-7",
|
|
111
|
+
"designed_for": "software/firmware integrity",
|
|
112
|
+
"insufficient_because": "attested by \"updates are signed\" without verifying the client checks the signature against a pinned key before applying and refuses downgrades."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"framework": "eu-cra",
|
|
116
|
+
"control_id": "Annex I",
|
|
117
|
+
"designed_for": "secure updates for products with digital elements",
|
|
118
|
+
"insufficient_because": "conformance is attested by shipping signed updates without verifying the receiving client enforces signature + anti-rollback + key-pin."
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
"skill_preload": [
|
|
123
|
+
"self-update-integrity",
|
|
124
|
+
"supply-chain-integrity",
|
|
125
|
+
"pqc-first",
|
|
126
|
+
"framework-gap-analysis",
|
|
127
|
+
"compliance-theater",
|
|
128
|
+
"policy-exception-gen"
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
"direct": {
|
|
132
|
+
"threat_context": "The self-update loop is the highest-privilege code path most products ship: it fetches code and runs it as the application. Publisher-side signing (library-author, supply-chain-integrity) is necessary but useless if the CONSUMER applies updates without verifying the signature, trusts a key the channel supplies, accepts a signed-but-vulnerable older version, or applies before the verifier gates it. Browser-served modules add the Subresource-Integrity surface. These are consumer-side validation gaps, not publisher posture.",
|
|
133
|
+
"rwep_threshold": {
|
|
134
|
+
"escalate": 60,
|
|
135
|
+
"monitor": 40,
|
|
136
|
+
"close": 25
|
|
137
|
+
},
|
|
138
|
+
"framework_lag_declaration": "A clean \"updates are signed / SLSA-attested / SBOM-published\" audit is NON-EVIDENCE for consumer-side update integrity; it confirms publisher posture, not signature-before-apply, key-pinning, anti-rollback, or verifier-gating on the receiving client.",
|
|
139
|
+
"skill_chain": [
|
|
140
|
+
{
|
|
141
|
+
"skill": "self-update-integrity",
|
|
142
|
+
"purpose": "enumerate the consumer-side verify / pin / anti-rollback / SRI / provenance checks"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"skill": "supply-chain-integrity",
|
|
146
|
+
"purpose": "cross-reference the publisher-side signing/SLSA posture this consumes"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"skill": "pqc-first",
|
|
150
|
+
"purpose": "assess the update-signature algorithm for downgrade / non-PQC exposure"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"skill": "framework-gap-analysis",
|
|
154
|
+
"purpose": "map findings to the SR-11 / SI-7 / CRA gaps the controls do not cover"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"token_budget": {
|
|
158
|
+
"estimated_total": 12000,
|
|
159
|
+
"breakdown": {
|
|
160
|
+
"govern": 1200,
|
|
161
|
+
"direct": 800,
|
|
162
|
+
"look": 4000,
|
|
163
|
+
"detect": 3800,
|
|
164
|
+
"analyze": 1500,
|
|
165
|
+
"validate": 500,
|
|
166
|
+
"close": 200
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"look": {
|
|
171
|
+
"artifacts": [
|
|
172
|
+
{
|
|
173
|
+
"id": "self-update-config",
|
|
174
|
+
"type": "config_file",
|
|
175
|
+
"source": "grep for self-update / self-update-standalone-verifier / downloadUpdate / applyUpdate / signature / verify across the update subsystem and config.",
|
|
176
|
+
"description": "How the self-update fetches, verifies, and applies releases, and whether the apply is gated on the verifier.",
|
|
177
|
+
"required": true,
|
|
178
|
+
"air_gap_alternative": "Inspect the self-update source for the fetch/verify/apply sequence and the signature + verifier-gate logic."
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"id": "update-key-and-channel-config",
|
|
182
|
+
"type": "config_file",
|
|
183
|
+
"source": "grep for public key / root / pin / TOFU / https / cert / version / rollback / minimum-version across the update trust + channel config.",
|
|
184
|
+
"description": "Whether the verifying key is pinned out-of-band, the channel is pinned, and anti-rollback / monotonic-version is enforced.",
|
|
185
|
+
"required": true,
|
|
186
|
+
"air_gap_alternative": "Inspect the updater for key-pin source, channel pinning, and the version-comparison logic."
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"id": "importmap-sri-config",
|
|
190
|
+
"type": "config_file",
|
|
191
|
+
"source": "grep for importmap-integrity / import map / integrity= / subresource / module script across the browser-served asset pipeline.",
|
|
192
|
+
"description": "Whether browser-served modules / import maps carry Subresource Integrity hashes.",
|
|
193
|
+
"required": false,
|
|
194
|
+
"air_gap_alternative": "Inspect the asset pipeline / template for integrity= on module scripts and the importmap."
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"id": "provenance-and-transparency-config",
|
|
198
|
+
"type": "config_file",
|
|
199
|
+
"source": "grep for content-credentials / c2pa / scitt / tsa / transparency / receipt / timestamp across the artifact-consumption path.",
|
|
200
|
+
"description": "Whether C2PA content credentials and SCITT/TSA transparency receipts on received artifacts are verified.",
|
|
201
|
+
"required": false,
|
|
202
|
+
"air_gap_alternative": "Inspect the artifact-consumption path for manifest + receipt verification where provenance is relied upon."
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
"collection_scope": {
|
|
206
|
+
"time_window": "current update-path configuration + source state (point-in-time posture audit)",
|
|
207
|
+
"asset_scope": "every self-updating client/agent and every consumer of externally-sourced artifacts (modules, models, signed bundles)"
|
|
208
|
+
},
|
|
209
|
+
"environment_assumptions": [
|
|
210
|
+
{
|
|
211
|
+
"assumption": "The product has a self-update path or consumes externally-sourced executable artifacts.",
|
|
212
|
+
"if_false": "A product updated solely via an OS package manager that verifies signatures has a reduced surface; focus on browser-module SRI and provenance if applicable."
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"assumption": "Update-path source or config is readable.",
|
|
216
|
+
"if_false": "Mark visibility_gap=no_update_path_inventory and report only what the fetch/verify/apply source reveals."
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"fallback_if_unavailable": [
|
|
220
|
+
{
|
|
221
|
+
"artifact_id": "importmap-sri-config",
|
|
222
|
+
"fallback_action": "If the product serves no browser modules, skip the SRI indicator.",
|
|
223
|
+
"confidence_impact": "none"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"artifact_id": "provenance-and-transparency-config",
|
|
227
|
+
"fallback_action": "If no C2PA / SCITT / TSA is used, skip those indicators.",
|
|
228
|
+
"confidence_impact": "none"
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
"detect": {
|
|
233
|
+
"indicators": [
|
|
234
|
+
{
|
|
235
|
+
"id": "self-update-no-signature-verification",
|
|
236
|
+
"type": "config_value",
|
|
237
|
+
"value": "The self-update path fetches and applies a new release without verifying a cryptographic signature over the downloaded artifact before swapping it in.",
|
|
238
|
+
"description": "An attacker who can serve or tamper with the update (compromised mirror, MITM, poisoned CDN) delivers arbitrary code that the client installs as a trusted update — the canonical software-supply-chain foothold.",
|
|
239
|
+
"confidence": "high",
|
|
240
|
+
"deterministic": false,
|
|
241
|
+
"attack_ref": "T1195.002",
|
|
242
|
+
"false_positive_checks_required": [
|
|
243
|
+
"Confirm the update path verifies a signature over the artifact BEFORE applying it (not merely a checksum the same server provides) — a path that applies on download, or trusts a server-provided hash with no signature, is the finding.",
|
|
244
|
+
"A self-update that delegates to an OS package manager which itself verifies signatures (apt/rpm/notary) is safe; the finding is a bespoke self-update with no signature step."
|
|
245
|
+
]
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"id": "self-update-no-anti-rollback",
|
|
249
|
+
"type": "config_value",
|
|
250
|
+
"value": "The self-update accepts a correctly-signed but OLDER version (no monotonic version / anti-rollback check), so a downgrade attack is possible.",
|
|
251
|
+
"description": "A downgrade serves a genuinely-signed but known-vulnerable prior release; without anti-rollback the client willingly reinstalls a version with a patched CVE, re-opening it.",
|
|
252
|
+
"confidence": "high",
|
|
253
|
+
"deterministic": false,
|
|
254
|
+
"attack_ref": "T1195.002",
|
|
255
|
+
"false_positive_checks_required": [
|
|
256
|
+
"Confirm the updater refuses a target version lower than the installed version (monotonic counter / minimum-version floor) — accepting any signed version regardless of order is the finding.",
|
|
257
|
+
"A deliberate, operator-initiated rollback path (explicit downgrade command) is not this finding; the finding is the AUTOMATIC update accepting an older signed version."
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"id": "self-update-key-not-pinned",
|
|
262
|
+
"type": "config_value",
|
|
263
|
+
"value": "The update signature is verified against a key/root that the update channel itself can supply or rotate without an out-of-band pin (TOFU with no pinned root, or a key fetched from the same endpoint as the update).",
|
|
264
|
+
"description": "If the signing key is not pinned out-of-band, an attacker who controls the channel supplies their own key + signature and the verification passes — signature verification with an attacker-chosen key is no verification.",
|
|
265
|
+
"confidence": "high",
|
|
266
|
+
"deterministic": false,
|
|
267
|
+
"attack_ref": "T1195.002",
|
|
268
|
+
"false_positive_checks_required": [
|
|
269
|
+
"Confirm the verifying root key is pinned (shipped in the binary / OS trust store / a separate pinned channel) and not fetched from the same place as the update — a key delivered alongside the update is the finding.",
|
|
270
|
+
"A documented, audited key-rotation that re-pins out-of-band is safe; the finding is in-band key trust."
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"id": "self-update-channel-not-authenticated",
|
|
275
|
+
"type": "config_value",
|
|
276
|
+
"value": "The update is fetched over plaintext HTTP, or over HTTPS without certificate/SPKI pinning where the channel is the sole integrity control.",
|
|
277
|
+
"description": "A plaintext or unpinned channel lets an on-path attacker substitute the update; even with HTTPS, a mis-issued cert defeats CA-only trust for the update endpoint.",
|
|
278
|
+
"confidence": "medium",
|
|
279
|
+
"deterministic": false,
|
|
280
|
+
"attack_ref": "T1195.002",
|
|
281
|
+
"false_positive_checks_required": [
|
|
282
|
+
"Confirm the update is fetched over TLS with the endpoint pinned (or that artifact-signature verification makes the channel non-load-bearing) — plaintext fetch, or HTTPS-only as the sole control, is the finding.",
|
|
283
|
+
"If the artifact is independently signature-verified with a pinned key, the channel is not the sole control and a weaker channel is acceptable."
|
|
284
|
+
]
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"id": "importmap-sri-not-enforced",
|
|
288
|
+
"type": "config_value",
|
|
289
|
+
"value": "Browser-served module scripts / import maps are delivered without Subresource Integrity (no integrity= hashes), so a tampered or substituted module loads without detection.",
|
|
290
|
+
"description": "Without SRI, a compromised CDN or MITM swaps a JavaScript module the page imports; the browser executes it with the page's full privileges — a client-side supply-chain compromise.",
|
|
291
|
+
"confidence": "medium",
|
|
292
|
+
"deterministic": false,
|
|
293
|
+
"attack_ref": "T1195.002",
|
|
294
|
+
"false_positive_checks_required": [
|
|
295
|
+
"Confirm import-map entries / module script tags carry integrity= hashes matching the served bytes (and the importmap itself is integrity-protected) — modules served without SRI are the finding.",
|
|
296
|
+
"First-party modules served same-origin over a pinned channel have a reduced need for SRI; the finding is third-party / CDN-served modules without integrity hashes."
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"id": "content-credentials-not-verified",
|
|
301
|
+
"type": "config_value",
|
|
302
|
+
"value": "C2PA / content-credential (provenance manifest) bound to a received artifact (model, media, signed bundle) is present but not verified, or is absent where the consumer relies on provenance.",
|
|
303
|
+
"description": "Unverified content credentials let a tampered or mis-attributed artifact pass as provenance-checked; the manifest exists but proves nothing if the consumer never validates the claim chain to a trust anchor.",
|
|
304
|
+
"confidence": "low",
|
|
305
|
+
"deterministic": false,
|
|
306
|
+
"attack_ref": "T1195.002",
|
|
307
|
+
"false_positive_checks_required": [
|
|
308
|
+
"Confirm the consumer verifies the C2PA manifest signature + claim chain to a trusted signer where it relies on provenance — accepting the manifest on its face (or ignoring it) is the finding.",
|
|
309
|
+
"A consumer that does not rely on content provenance for any trust decision is not in scope for this indicator."
|
|
310
|
+
]
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"id": "transparency-log-not-checked",
|
|
314
|
+
"type": "config_value",
|
|
315
|
+
"value": "Received artifacts carry SCITT transparency-log receipts or TSA timestamps that the consumer does not verify (existence + inclusion proof + timestamp validity).",
|
|
316
|
+
"description": "An unverified transparency receipt provides no non-repudiation or timestamp guarantee; an attacker omits or forges it and the consumer accepts an unlogged artifact as transparently-recorded.",
|
|
317
|
+
"confidence": "low",
|
|
318
|
+
"deterministic": false,
|
|
319
|
+
"attack_ref": "T1195.002",
|
|
320
|
+
"false_positive_checks_required": [
|
|
321
|
+
"Confirm the consumer verifies the SCITT receipt inclusion proof / TSA timestamp signature against the expected log/authority — accepting an artifact without checking the receipt is the finding.",
|
|
322
|
+
"A supply chain that does not use transparency logs / timestamping has no receipt to verify and is not in scope."
|
|
323
|
+
]
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"id": "update-applied-without-verifier-gate",
|
|
327
|
+
"type": "config_value",
|
|
328
|
+
"value": "The standalone update verifier exists but the apply step does not GATE on it — the new binary/code is swapped into the execution path before (or regardless of) the verifier's result.",
|
|
329
|
+
"description": "A verifier whose result does not block the apply is decorative; the attacker-supplied update executes because the swap happened independent of (or before) verification.",
|
|
330
|
+
"confidence": "medium",
|
|
331
|
+
"deterministic": false,
|
|
332
|
+
"attack_ref": "T1574",
|
|
333
|
+
"false_positive_checks_required": [
|
|
334
|
+
"Confirm the apply/swap is conditional on the verifier returning success and fails closed on a verifier error — an apply that proceeds on verifier failure/timeout, or before verification, is the finding.",
|
|
335
|
+
"A two-phase stage-then-verify-then-activate flow where activation is gated on the verifier is safe; the finding is verify-and-apply racing or verifier advisory-only."
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
"false_positive_profile": [
|
|
340
|
+
{
|
|
341
|
+
"indicator_id": "self-update-no-signature-verification",
|
|
342
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
343
|
+
"distinguishing_test": "Confirm the update path verifies a signature over the artifact BEFORE applying it (not merely a checksum the same server provides) — a path that applies on download, or trusts a server-provided hash with no signature, is the finding."
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"indicator_id": "self-update-no-anti-rollback",
|
|
347
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
348
|
+
"distinguishing_test": "Confirm the updater refuses a target version lower than the installed version (monotonic counter / minimum-version floor) — accepting any signed version regardless of order is the finding."
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"indicator_id": "self-update-key-not-pinned",
|
|
352
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
353
|
+
"distinguishing_test": "Confirm the verifying root key is pinned (shipped in the binary / OS trust store / a separate pinned channel) and not fetched from the same place as the update — a key delivered alongside the update is the finding."
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"indicator_id": "self-update-channel-not-authenticated",
|
|
357
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
358
|
+
"distinguishing_test": "Confirm the update is fetched over TLS with the endpoint pinned (or that artifact-signature verification makes the channel non-load-bearing) — plaintext fetch, or HTTPS-only as the sole control, is the finding."
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"indicator_id": "importmap-sri-not-enforced",
|
|
362
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
363
|
+
"distinguishing_test": "Confirm import-map entries / module script tags carry integrity= hashes matching the served bytes (and the importmap itself is integrity-protected) — modules served without SRI are the finding."
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"indicator_id": "content-credentials-not-verified",
|
|
367
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
368
|
+
"distinguishing_test": "Confirm the consumer verifies the C2PA manifest signature + claim chain to a trusted signer where it relies on provenance — accepting the manifest on its face (or ignoring it) is the finding."
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
"indicator_id": "transparency-log-not-checked",
|
|
372
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
373
|
+
"distinguishing_test": "Confirm the consumer verifies the SCITT receipt inclusion proof / TSA timestamp signature against the expected log/authority — accepting an artifact without checking the receipt is the finding."
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"indicator_id": "update-applied-without-verifier-gate",
|
|
377
|
+
"benign_pattern": "Integrity enforced by a delegated mechanism (OS package manager, pinned-key signature, gated verifier) or a surface where the control is genuinely not relied upon.",
|
|
378
|
+
"distinguishing_test": "Confirm the apply/swap is conditional on the verifier returning success and fails closed on a verifier error — an apply that proceeds on verifier failure/timeout, or before verification, is the finding."
|
|
379
|
+
}
|
|
380
|
+
],
|
|
381
|
+
"minimum_signal": {
|
|
382
|
+
"detected": "At least one consumer-side control is absent on a production update path — no signature verification, in-band key trust, no anti-rollback, unauthenticated channel as sole control, missing SRI, or an apply not gated on the verifier.",
|
|
383
|
+
"inconclusive": "A control is delegated to infrastructure the audit could not read (OS package manager, an external verifier service) — record as visibility_gap, not a clean result.",
|
|
384
|
+
"not_detected": "The update path verifies a signature against an out-of-band-pinned key before applying, refuses downgrades, pins/secures the channel, gates the apply on the verifier, enforces SRI on browser modules, and verifies provenance/transparency receipts where relied upon."
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
"analyze": {
|
|
388
|
+
"rwep_inputs": [
|
|
389
|
+
{
|
|
390
|
+
"signal_id": "self-update-no-signature-verification",
|
|
391
|
+
"rwep_factor": "blast_radius",
|
|
392
|
+
"weight": 30
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"signal_id": "self-update-no-anti-rollback",
|
|
396
|
+
"rwep_factor": "public_poc",
|
|
397
|
+
"weight": 20
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"signal_id": "update-applied-without-verifier-gate",
|
|
401
|
+
"rwep_factor": "active_exploitation",
|
|
402
|
+
"weight": 10
|
|
403
|
+
}
|
|
404
|
+
],
|
|
405
|
+
"blast_radius_model": {
|
|
406
|
+
"scope_question": "Does the gap let an attacker who can influence the update channel run arbitrary code on the entire installed base?",
|
|
407
|
+
"scoring_rubric": [
|
|
408
|
+
{
|
|
409
|
+
"condition": "A self-update with no signature-before-apply on a widely-distributed client — channel compromise yields RCE across the install base",
|
|
410
|
+
"blast_radius_score": 5,
|
|
411
|
+
"description": "Mass arbitrary-code execution via the update mechanism"
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"condition": "Downgrade or in-band-key gap requiring a more specific channel position",
|
|
415
|
+
"blast_radius_score": 4,
|
|
416
|
+
"description": "Targeted reintroduction of vulnerable code or key substitution"
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"condition": "Browser-module SRI gap on a single app surface",
|
|
420
|
+
"blast_radius_score": 3,
|
|
421
|
+
"description": "Client-side code substitution scoped to the served page"
|
|
422
|
+
}
|
|
423
|
+
]
|
|
424
|
+
},
|
|
425
|
+
"compliance_theater_check": {
|
|
426
|
+
"claim": "Our updates are signed and delivered over HTTPS, so the update channel is secure.",
|
|
427
|
+
"audit_evidence": "A code-signing certificate, an HTTPS update endpoint, a published SBOM/SLSA attestation.",
|
|
428
|
+
"reality_test": "Verify the CLIENT checks the signature against an out-of-band-pinned key before applying, refuses older versions, and gates the apply on the verifier. If a swapped artifact, an attacker-supplied key, or an older signed version would be applied, the signing did not protect the consumer.",
|
|
429
|
+
"theater_verdict_if_gap": "theater"
|
|
430
|
+
},
|
|
431
|
+
"framework_gap_mapping": [
|
|
432
|
+
{
|
|
433
|
+
"finding_id": "self-update-no-signature-verification",
|
|
434
|
+
"framework": "nist-800-53",
|
|
435
|
+
"claimed_control": "SI-7 (software integrity)",
|
|
436
|
+
"actual_gap": "SI-7 is attested by \"updates are signed\" without verifying the client checks the signature before applying."
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
"finding_id": "self-update-no-anti-rollback",
|
|
440
|
+
"framework": "eu-cra",
|
|
441
|
+
"claimed_control": "Annex I (secure updates)",
|
|
442
|
+
"actual_gap": "CRA conformance via signed updates does not verify the client refuses a signed-but-older vulnerable version."
|
|
443
|
+
}
|
|
444
|
+
],
|
|
445
|
+
"escalation_criteria": [
|
|
446
|
+
{
|
|
447
|
+
"condition": "A widely-distributed self-updating client applies updates with no signature-before-apply or in-band key trust",
|
|
448
|
+
"action": "raise_severity"
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"condition": "The apply step proceeds on verifier failure/timeout",
|
|
452
|
+
"action": "trigger_playbook"
|
|
453
|
+
}
|
|
454
|
+
]
|
|
455
|
+
},
|
|
456
|
+
"validate": {
|
|
457
|
+
"remediation_paths": [
|
|
458
|
+
{
|
|
459
|
+
"id": "verify-signature-before-apply",
|
|
460
|
+
"description": "Verify an artifact signature against an out-of-band-pinned root key BEFORE swapping the update in, and fail closed on any verification error — never trust a server-provided hash alone.",
|
|
461
|
+
"preconditions": [
|
|
462
|
+
"operator controls the update client"
|
|
463
|
+
],
|
|
464
|
+
"priority": 1,
|
|
465
|
+
"for_signals": [
|
|
466
|
+
"self-update-no-signature-verification",
|
|
467
|
+
"self-update-key-not-pinned",
|
|
468
|
+
"update-applied-without-verifier-gate"
|
|
469
|
+
]
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
"id": "enforce-anti-rollback",
|
|
473
|
+
"description": "Refuse any target version lower than the installed version (monotonic counter / minimum-version floor); require an explicit operator action for any intentional downgrade.",
|
|
474
|
+
"preconditions": [],
|
|
475
|
+
"priority": 1,
|
|
476
|
+
"for_signals": [
|
|
477
|
+
"self-update-no-anti-rollback"
|
|
478
|
+
]
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
"id": "pin-the-update-channel",
|
|
482
|
+
"description": "Fetch over TLS with the endpoint pinned (cert/SPKI), treating the channel as defence-in-depth behind artifact-signature verification.",
|
|
483
|
+
"preconditions": [],
|
|
484
|
+
"priority": 2,
|
|
485
|
+
"for_signals": [
|
|
486
|
+
"self-update-channel-not-authenticated"
|
|
487
|
+
]
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"id": "enforce-sri-on-modules",
|
|
491
|
+
"description": "Add Subresource Integrity hashes to import-map entries and module script tags (and integrity-protect the import map itself) for third-party / CDN-served modules.",
|
|
492
|
+
"preconditions": [],
|
|
493
|
+
"priority": 2,
|
|
494
|
+
"for_signals": [
|
|
495
|
+
"importmap-sri-not-enforced"
|
|
496
|
+
]
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
"id": "verify-provenance-and-transparency",
|
|
500
|
+
"description": "Verify C2PA content-credential claim chains and SCITT/TSA transparency receipts on received artifacts where provenance is relied upon.",
|
|
501
|
+
"preconditions": [],
|
|
502
|
+
"priority": 3,
|
|
503
|
+
"for_signals": [
|
|
504
|
+
"content-credentials-not-verified",
|
|
505
|
+
"transparency-log-not-checked"
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
],
|
|
509
|
+
"validation_tests": [
|
|
510
|
+
{
|
|
511
|
+
"id": "tampered-update-rejected",
|
|
512
|
+
"test": "Serve a tampered (or attacker-signed) update artifact to the client.",
|
|
513
|
+
"expected_result": "Client rejects: signature does not verify against the pinned key; nothing is applied.",
|
|
514
|
+
"test_type": "negative"
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
"id": "downgrade-rejected",
|
|
518
|
+
"test": "Serve a correctly-signed but older version to the auto-updater.",
|
|
519
|
+
"expected_result": "Client refuses the downgrade (version below installed).",
|
|
520
|
+
"test_type": "negative"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"id": "verifier-failure-blocks-apply",
|
|
524
|
+
"test": "Force the update verifier to fail/timeout during an apply.",
|
|
525
|
+
"expected_result": "Apply is blocked (fails closed); the running version is unchanged.",
|
|
526
|
+
"test_type": "negative"
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
"id": "legitimate-update-applies",
|
|
530
|
+
"test": "Serve a valid, correctly-signed, newer update.",
|
|
531
|
+
"expected_result": "Update verifies and applies — no regression on the legitimate path.",
|
|
532
|
+
"test_type": "functional"
|
|
533
|
+
}
|
|
534
|
+
],
|
|
535
|
+
"residual_risk_statement": {
|
|
536
|
+
"risk": "Compromise of the pinned signing key (or the publisher's build pipeline) still yields a validly-signed malicious update the consumer will accept.",
|
|
537
|
+
"why_remains": "Consumer-side verification authenticates the publisher key, not the publisher's own integrity; build-pipeline / key compromise is the publisher-side supply-chain-integrity concern.",
|
|
538
|
+
"acceptance_level": "ciso"
|
|
539
|
+
},
|
|
540
|
+
"evidence_requirements": [
|
|
541
|
+
{
|
|
542
|
+
"evidence_type": "config_diff",
|
|
543
|
+
"description": "The signature-verification, key-pin, anti-rollback, channel-pin, SRI, and provenance/transparency configuration as audited.",
|
|
544
|
+
"retention_period": "1 year"
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
"evidence_type": "exploit_replay_negative",
|
|
548
|
+
"description": "Outcomes of the tampered-update / downgrade / verifier-failure negative tests plus the legitimate-update functional test.",
|
|
549
|
+
"retention_period": "1 year"
|
|
550
|
+
}
|
|
551
|
+
],
|
|
552
|
+
"regression_trigger": [
|
|
553
|
+
{
|
|
554
|
+
"condition": "A change to the update client, signing key, channel, or artifact-consumption path",
|
|
555
|
+
"interval": "on change"
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
"condition": "Periodic re-attestation of consumer-side update integrity",
|
|
559
|
+
"interval": "quarterly"
|
|
560
|
+
}
|
|
561
|
+
]
|
|
562
|
+
},
|
|
563
|
+
"close": {
|
|
564
|
+
"evidence_package": {
|
|
565
|
+
"bundle_format": "csaf-2.0",
|
|
566
|
+
"contents": [
|
|
567
|
+
"update-path config snapshot",
|
|
568
|
+
"per-indicator findings + false-positive disposition",
|
|
569
|
+
"negative + functional test results",
|
|
570
|
+
"framework gap mapping"
|
|
571
|
+
],
|
|
572
|
+
"destination": ".exceptd/attestations/<session_id>/attestation.json"
|
|
573
|
+
},
|
|
574
|
+
"learning_loop": {
|
|
575
|
+
"enabled": true,
|
|
576
|
+
"lesson_template": {
|
|
577
|
+
"attack_vector": "Malicious update accepted by a consumer that did not verify the signature against a pinned key before applying, refused downgrades, or gated the apply on the verifier — channel compromise to arbitrary code execution.",
|
|
578
|
+
"control_gap": "Consumer-side update path lacks signature-before-apply / out-of-band key pin / anti-rollback / verifier-gating.",
|
|
579
|
+
"framework_gap": "NIST SR-11 + SI-7 + EU CRA Annex I cover publisher signing but not consumer-side verification enforcement.",
|
|
580
|
+
"new_control_requirement": "The update client MUST verify a signature against an out-of-band-pinned key before applying, refuse downgrades, and gate the apply on the verifier."
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
"notification_actions": [
|
|
584
|
+
{
|
|
585
|
+
"obligation_ref": "EU/EU CRA Annex I 24h",
|
|
586
|
+
"deadline": "24h from detect_confirmed",
|
|
587
|
+
"recipient": "CRA market-surveillance authority",
|
|
588
|
+
"evidence_attached": [
|
|
589
|
+
"attestation"
|
|
590
|
+
]
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"obligation_ref": "EU/NIS2 Art.23 24h",
|
|
594
|
+
"deadline": "24h from detect_confirmed",
|
|
595
|
+
"recipient": "national CSIRT / competent authority",
|
|
596
|
+
"evidence_attached": [
|
|
597
|
+
"attestation"
|
|
598
|
+
]
|
|
599
|
+
}
|
|
600
|
+
],
|
|
601
|
+
"exception_generation": {
|
|
602
|
+
"trigger_condition": "A legacy client cannot adopt signature-before-apply immediately (e.g. a third-party updater).",
|
|
603
|
+
"exception_template": {
|
|
604
|
+
"scope": "the specific update client",
|
|
605
|
+
"duration": "90 days",
|
|
606
|
+
"compensating_controls": [
|
|
607
|
+
"pin the update channel (cert/SPKI)",
|
|
608
|
+
"restrict update sources to a controlled mirror",
|
|
609
|
+
"monitor + alert on update-artifact hash changes"
|
|
610
|
+
],
|
|
611
|
+
"risk_acceptance_owner": "ciso"
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
"regression_schedule": {
|
|
615
|
+
"next_run": "quarterly or on any update-client / signing-key / channel change",
|
|
616
|
+
"trigger": "change to the update path, or quarterly re-attestation"
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
"directives": [
|
|
621
|
+
{
|
|
622
|
+
"id": "all-self-update-paths",
|
|
623
|
+
"title": "Inventory + integrity-test every self-update and artifact-consumption path",
|
|
624
|
+
"applies_to": {
|
|
625
|
+
"always": true
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
"id": "downgrade-and-key-pin-sweep",
|
|
630
|
+
"title": "Targeted anti-rollback + key-pinning sweep on auto-updating clients",
|
|
631
|
+
"applies_to": {
|
|
632
|
+
"attack_technique": "T1195.002"
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
]
|
|
636
|
+
}
|