@blamejs/exceptd-skills 0.16.16 → 0.16.18
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 -0
- package/data/_indexes/_meta.json +16 -14
- package/data/_indexes/activity-feed.json +17 -3
- package/data/_indexes/catalog-summaries.json +1 -1
- package/data/_indexes/chains.json +26271 -1538
- package/data/_indexes/currency.json +19 -1
- package/data/_indexes/frequency.json +154 -92
- package/data/_indexes/handoff-dag.json +9 -1
- package/data/_indexes/jurisdiction-map.json +9 -3
- package/data/_indexes/section-offsets.json +170 -0
- package/data/_indexes/stale-content.json +1 -1
- package/data/_indexes/summary-cards.json +80 -0
- package/data/_indexes/token-budget.json +103 -3
- package/data/_indexes/trigger-table.json +91 -0
- package/data/_indexes/xref.json +41 -3
- package/data/cwe-catalog.json +75 -3
- package/data/playbooks/audit-log-integrity.json +3 -0
- package/data/playbooks/crypto-codebase.json +31 -8
- package/data/playbooks/decompression-dos.json +626 -0
- package/data/playbooks/framework.json +2 -0
- package/data/playbooks/log-injection-telemetry.json +619 -0
- package/data/playbooks/secrets.json +1 -0
- package/manifest-snapshot.json +107 -2
- package/manifest-snapshot.sha256 +1 -1
- package/manifest.json +163 -50
- package/package.json +2 -2
- package/sbom.cdx.json +94 -34
- package/skills/decompression-dos/skill.md +83 -0
- package/skills/log-injection-telemetry/skill.md +80 -0
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"id": "decompression-dos",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"last_threat_review": "2026-06-02",
|
|
6
|
+
"threat_currency_score": 92,
|
|
7
|
+
"changelog": [
|
|
8
|
+
{
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"date": "2026-06-02",
|
|
11
|
+
"summary": "Initial seven-phase decompression-bomb / parser-DoS / ReDoS playbook. Covers the input-amplification denial-of-service class a single crafted file or string can trigger, which generic input-validation controls do not bound: unbounded archive decompression (zip bomb) and nested-archive bombs, Zip Slip path traversal on extraction, XML entity expansion (billion laughs) / XXE, catastrophic-backtracking regular expressions (ReDoS), recursive parsing with no depth limit, and length-field-driven unbounded allocation in binary parsers (ASN.1/DER, CBOR, MIME, protobuf). Adds CWE-409 (data amplification) and CWE-1333 (inefficient regex complexity) to the weakness catalog. Maps to ATT&CK T1499 / T1499.001 / T1059 and to NIST 800-53 SI-10 / SC-5, ISO 27001 A.8.26, NIS2 Art.21, UK CAF B4, and AU ISM."
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"owner": "@blamejs/platform-security",
|
|
15
|
+
"air_gap_mode": false,
|
|
16
|
+
"scope": "code",
|
|
17
|
+
"preconditions": [
|
|
18
|
+
{
|
|
19
|
+
"id": "source-or-config-read",
|
|
20
|
+
"description": "Agent must read the operator's parsing / decompression / regex source and/or runtime configuration to inspect size and ratio caps, path containment, entity processing, regex complexity, and depth/allocation bounds. A host with neither marks the playbook visibility_gap=no_parser_inventory.",
|
|
21
|
+
"check": "agent_has_filesystem_read == true OR agent_has_parser_config == true",
|
|
22
|
+
"on_fail": "halt"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"mutex": [],
|
|
26
|
+
"feeds_into": [
|
|
27
|
+
{
|
|
28
|
+
"playbook_id": "crypto-codebase",
|
|
29
|
+
"condition": "finding.includes_vendored_parser_weakness == true"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"playbook_id": "framework",
|
|
33
|
+
"condition": "analyze.compliance_theater_check.verdict == 'theater'"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"domain": {
|
|
38
|
+
"name": "Decompression-bomb / parser-DoS / ReDoS",
|
|
39
|
+
"attack_class": "webapp-exploit",
|
|
40
|
+
"atlas_refs": [],
|
|
41
|
+
"attack_refs": [
|
|
42
|
+
"T1499",
|
|
43
|
+
"T1499.001",
|
|
44
|
+
"T1059"
|
|
45
|
+
],
|
|
46
|
+
"cve_refs": [],
|
|
47
|
+
"cwe_refs": [
|
|
48
|
+
"CWE-409",
|
|
49
|
+
"CWE-1333",
|
|
50
|
+
"CWE-400",
|
|
51
|
+
"CWE-776",
|
|
52
|
+
"CWE-22",
|
|
53
|
+
"CWE-834",
|
|
54
|
+
"CWE-770"
|
|
55
|
+
],
|
|
56
|
+
"frameworks_in_scope": [
|
|
57
|
+
"nist-800-53",
|
|
58
|
+
"iso-27001-2022",
|
|
59
|
+
"nis2",
|
|
60
|
+
"uk-caf",
|
|
61
|
+
"au-ism"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"phases": {
|
|
65
|
+
"govern": {
|
|
66
|
+
"jurisdiction_obligations": [
|
|
67
|
+
{
|
|
68
|
+
"jurisdiction": "EU",
|
|
69
|
+
"regulation": "NIS2 Art.23",
|
|
70
|
+
"obligation": "notify_regulator",
|
|
71
|
+
"window_hours": 24,
|
|
72
|
+
"clock_starts": "detect_confirmed",
|
|
73
|
+
"evidence_required": [
|
|
74
|
+
"parser_inventory",
|
|
75
|
+
"amplification_dos_evidence"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"jurisdiction": "EU",
|
|
80
|
+
"regulation": "EU CRA Annex I",
|
|
81
|
+
"obligation": "notify_supervisory_body",
|
|
82
|
+
"window_hours": 24,
|
|
83
|
+
"clock_starts": "detect_confirmed",
|
|
84
|
+
"evidence_required": [
|
|
85
|
+
"affected_product",
|
|
86
|
+
"resource_exhaustion_vector"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"theater_fingerprints": [
|
|
91
|
+
{
|
|
92
|
+
"pattern_id": "we-validate-input",
|
|
93
|
+
"claim": "We validate all input, so we are safe from malformed data.",
|
|
94
|
+
"fast_detection_test": "Format validation does not bound AMPLIFICATION. Ask whether decompression caps output size/ratio, the XML parser disables entities, and regexes are length-capped or linear — a valid-format zip bomb or billion-laughs passes input validation."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"pattern_id": "waf-blocks-it",
|
|
98
|
+
"claim": "Our WAF blocks malicious uploads.",
|
|
99
|
+
"fast_detection_test": "A zip bomb / ReDoS string is structurally valid and small; a WAF rarely catches it. Ask for the size/ratio cap, depth limit, and regex-complexity guard at the parser."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"pattern_id": "cloud-scales",
|
|
103
|
+
"claim": "The service autoscales, so resource exhaustion is handled.",
|
|
104
|
+
"fast_detection_test": "Autoscaling pays for the amplification; it does not stop a single crafted file from pinning a worker. Ask for the per-input resource ceiling."
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"framework_context": {
|
|
108
|
+
"gap_summary": "Org controls treat \"we validate input\" and \"the cloud scales\" as DoS protection. Neither requires bounding the amplification a single crafted input causes — decompression ratio, entity expansion, regex complexity, parse depth, length-field allocation — which is where zip bombs, billion laughs, and ReDoS live.",
|
|
109
|
+
"lag_score": 70,
|
|
110
|
+
"per_framework_gaps": [
|
|
111
|
+
{
|
|
112
|
+
"framework": "nist-800-53",
|
|
113
|
+
"control_id": "SI-10",
|
|
114
|
+
"designed_for": "information input validation",
|
|
115
|
+
"insufficient_because": "satisfied by validating format; does not require bounding decompression ratio, entity expansion, or regex complexity against amplification."
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"framework": "nist-800-53",
|
|
119
|
+
"control_id": "SC-5",
|
|
120
|
+
"designed_for": "denial-of-service protection",
|
|
121
|
+
"insufficient_because": "framed at the network tier; not operationalised for single-request asymmetric application-layer DoS."
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"skill_preload": [
|
|
126
|
+
"decompression-dos",
|
|
127
|
+
"webapp-security",
|
|
128
|
+
"fuzz-testing-strategy",
|
|
129
|
+
"framework-gap-analysis",
|
|
130
|
+
"compliance-theater",
|
|
131
|
+
"policy-exception-gen"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"direct": {
|
|
135
|
+
"threat_context": "Amplification DoS turns a tiny, structurally-valid input into ruinous server work: a 42 KB zip bomb expands to petabytes, a few lines of XML entities expand to gigabytes (billion laughs), a crafted string pins a CPU on a backtracking regex (ReDoS), and a declared 2 GB length field allocates a 2 GB buffer from a 10-byte message. Input-format validation passes all of these because they are valid. The defence is resource bounds at the parser — size/ratio caps, entity disabling, linear regex, depth and allocation limits — not validation or autoscaling.",
|
|
136
|
+
"rwep_threshold": {
|
|
137
|
+
"escalate": 50,
|
|
138
|
+
"monitor": 30,
|
|
139
|
+
"close": 15
|
|
140
|
+
},
|
|
141
|
+
"framework_lag_declaration": "A clean \"we validate input / have a WAF / autoscale\" audit is NON-EVIDENCE for amplification-DoS resistance; it confirms format validation and elastic infra, not decompression caps, entity disabling, regex-complexity bounds, or parse-depth limits.",
|
|
142
|
+
"skill_chain": [
|
|
143
|
+
{
|
|
144
|
+
"skill": "decompression-dos",
|
|
145
|
+
"purpose": "enumerate the decompression / parser / regex resource-bound checks"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"skill": "webapp-security",
|
|
149
|
+
"purpose": "cross-reference the OWASP ReDoS / XXE / resource-consumption classes"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"skill": "fuzz-testing-strategy",
|
|
153
|
+
"purpose": "frame coverage-guided fuzzing as the regression control for parser DoS"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"skill": "framework-gap-analysis",
|
|
157
|
+
"purpose": "map findings to the SI-10 / SC-5 gaps the controls do not cover"
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"token_budget": {
|
|
161
|
+
"estimated_total": 12000,
|
|
162
|
+
"breakdown": {
|
|
163
|
+
"govern": 1200,
|
|
164
|
+
"direct": 800,
|
|
165
|
+
"look": 4000,
|
|
166
|
+
"detect": 3800,
|
|
167
|
+
"analyze": 1500,
|
|
168
|
+
"validate": 500,
|
|
169
|
+
"close": 200
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"look": {
|
|
174
|
+
"artifacts": [
|
|
175
|
+
{
|
|
176
|
+
"id": "decompression-config",
|
|
177
|
+
"type": "config_file",
|
|
178
|
+
"source": "grep for safe-decompress / safe-archive / archive-gz / archive-tar / archive-read / inflate / gunzip / max size / ratio across the archive-handling path.",
|
|
179
|
+
"description": "Whether archive decompression caps total output size / ratio and limits nested-archive recursion.",
|
|
180
|
+
"required": true,
|
|
181
|
+
"air_gap_alternative": "Inspect the decompression source for a size/ratio cap and nesting limit."
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"id": "archive-extraction-paths",
|
|
185
|
+
"type": "config_file",
|
|
186
|
+
"source": "grep for extract / entry name / path.join / .. / normaliz / symlink across the archive-extraction path.",
|
|
187
|
+
"description": "Whether extracted entry paths are normalised and confined to the target (Zip Slip defence).",
|
|
188
|
+
"required": true,
|
|
189
|
+
"air_gap_alternative": "Inspect the extraction source for path normalisation + containment before write."
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"id": "xml-and-parser-config",
|
|
193
|
+
"type": "config_file",
|
|
194
|
+
"source": "grep for guard-xml / xml-c14n / DTD / entity / external / DOCTYPE across the XML parser config.",
|
|
195
|
+
"description": "Whether the XML parser disables DTDs / external + general entities (XXE + billion-laughs defence).",
|
|
196
|
+
"required": true,
|
|
197
|
+
"air_gap_alternative": "Inspect the XML parser construction for entity/DTD disabling."
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"id": "regex-and-recursion-config",
|
|
201
|
+
"type": "config_file",
|
|
202
|
+
"source": "grep for guard-regex / safe-regex / RegExp / max-depth / depth / recursion / nesting across the regex and structured-parser paths.",
|
|
203
|
+
"description": "Whether regexes on untrusted input are linear/length-capped and whether nested-structure parsers (JSON/CBOR/protobuf/ASN.1/MIME) enforce depth and length-field bounds.",
|
|
204
|
+
"required": true,
|
|
205
|
+
"air_gap_alternative": "Inspect the regex usage and the structured-parser source for length caps, linear engines, and depth/allocation bounds."
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"collection_scope": {
|
|
209
|
+
"time_window": "current parsing / decompression source + configuration state (point-in-time posture audit)",
|
|
210
|
+
"asset_scope": "every code path that decompresses an archive, parses XML/JSON/CBOR/protobuf/ASN.1/MIME, or applies a regex to attacker-suppliable input"
|
|
211
|
+
},
|
|
212
|
+
"environment_assumptions": [
|
|
213
|
+
{
|
|
214
|
+
"assumption": "The product parses or decompresses attacker-suppliable input.",
|
|
215
|
+
"if_false": "A product that never ingests untrusted files/strings has a reduced surface; focus on any regex over user input."
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"assumption": "Parser/decompression source or config is readable.",
|
|
219
|
+
"if_false": "Mark visibility_gap=no_parser_inventory and report only what the parsing source reveals."
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
"fallback_if_unavailable": [
|
|
223
|
+
{
|
|
224
|
+
"artifact_id": "decompression-config",
|
|
225
|
+
"fallback_action": "If the product never decompresses archives, skip the bomb / nested-bomb / Zip Slip indicators.",
|
|
226
|
+
"confidence_impact": "none"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"artifact_id": "xml-and-parser-config",
|
|
230
|
+
"fallback_action": "If no XML is parsed, skip the entity-expansion indicator.",
|
|
231
|
+
"confidence_impact": "none"
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
"detect": {
|
|
236
|
+
"indicators": [
|
|
237
|
+
{
|
|
238
|
+
"id": "archive-decompression-unbounded",
|
|
239
|
+
"type": "config_value",
|
|
240
|
+
"value": "An archive (zip/tar/gzip) is decompressed without a cap on total decompressed size or a compression-ratio guard.",
|
|
241
|
+
"description": "A decompression bomb — a small archive that expands to gigabytes — exhausts memory/disk and denies service; the classic 42 KB → 4.5 PB zip bomb.",
|
|
242
|
+
"confidence": "high",
|
|
243
|
+
"deterministic": false,
|
|
244
|
+
"attack_ref": "T1499.001",
|
|
245
|
+
"false_positive_checks_required": [
|
|
246
|
+
"Confirm extraction enforces a total-output-size ceiling AND/OR a per-entry compression-ratio guard (and fails closed when exceeded) — extraction with no size/ratio cap is the finding.",
|
|
247
|
+
"A decompression of a fully-trusted, operator-produced artifact of known size is lower-risk; the finding is decompressing attacker-suppliable input without a cap."
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"id": "zip-slip-path-traversal",
|
|
252
|
+
"type": "config_value",
|
|
253
|
+
"value": "Archive extraction writes entries to a path derived from the entry name without normalising and confining it to the target directory, so an entry named `../../x` escapes (Zip Slip).",
|
|
254
|
+
"description": "A Zip Slip entry overwrites files outside the extraction target — config, a binary on the execution path, an SSH key — turning archive extraction into arbitrary file write and often code execution.",
|
|
255
|
+
"confidence": "high",
|
|
256
|
+
"deterministic": false,
|
|
257
|
+
"attack_ref": "T1059",
|
|
258
|
+
"false_positive_checks_required": [
|
|
259
|
+
"Confirm each extracted path is normalised and verified to remain within the target directory (reject `..`, absolute paths, and symlink escapes) before writing — extraction that joins the raw entry name to the target is the finding.",
|
|
260
|
+
"An extractor that resolves the final path and refuses any that escapes the target (or extracts into a jail) is safe even if entries contain `..`."
|
|
261
|
+
]
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"id": "xml-entity-expansion-enabled",
|
|
265
|
+
"type": "config_value",
|
|
266
|
+
"value": "XML is parsed with DTD processing / external + general entity expansion enabled, exposing the billion-laughs (entity-expansion) and XXE classes.",
|
|
267
|
+
"description": "Recursive entity expansion (billion laughs) amplifies a tiny document into gigabytes of memory; external entities additionally enable file read / SSRF (XXE).",
|
|
268
|
+
"confidence": "high",
|
|
269
|
+
"deterministic": false,
|
|
270
|
+
"attack_ref": "T1499.001",
|
|
271
|
+
"false_positive_checks_required": [
|
|
272
|
+
"Confirm the XML parser disables DTDs / external + parameter entities (or caps entity expansion) — a parser with entity expansion and external entities enabled on untrusted input is the finding.",
|
|
273
|
+
"A parser fed only trusted, schema-fixed internal XML with no DTD may be lower-risk; the finding is entity processing enabled on attacker-suppliable XML."
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"id": "redos-catastrophic-backtracking",
|
|
278
|
+
"type": "config_value",
|
|
279
|
+
"value": "A regular expression with super-linear (exponential / polynomial) worst-case complexity is applied to an actor-controlled value without a length cap or a linear-time engine.",
|
|
280
|
+
"description": "A crafted input triggers catastrophic backtracking, pinning a CPU core for seconds-to-minutes per request — a cheap single-request denial of service (ReDoS).",
|
|
281
|
+
"confidence": "medium",
|
|
282
|
+
"deterministic": false,
|
|
283
|
+
"attack_ref": "T1499",
|
|
284
|
+
"false_positive_checks_required": [
|
|
285
|
+
"Confirm regexes on untrusted input are either provably linear (no nested/overlapping quantifiers like (a+)+), length-capped, or run on a linear-time engine (RE2) — a backtracking-prone pattern on uncapped input is the finding.",
|
|
286
|
+
"A regex applied only to a short, fixed-length, trusted token is lower-risk; the finding is a backtracking pattern on unbounded attacker input."
|
|
287
|
+
]
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"id": "recursive-parse-no-depth-limit",
|
|
291
|
+
"type": "config_value",
|
|
292
|
+
"value": "A nested-structure parser (JSON / CBOR / protobuf / nested archives / nested XML) imposes no maximum nesting depth, so deeply nested input drives unbounded recursion.",
|
|
293
|
+
"description": "Deeply nested input exhausts the stack or CPU (and can crash via recursion limit) — a single small payload denies service through the parser.",
|
|
294
|
+
"confidence": "medium",
|
|
295
|
+
"deterministic": false,
|
|
296
|
+
"attack_ref": "T1499.001",
|
|
297
|
+
"false_positive_checks_required": [
|
|
298
|
+
"Confirm the parser enforces a maximum nesting depth and fails closed beyond it — a recursive descent / nesting handler with no depth ceiling on untrusted input is the finding.",
|
|
299
|
+
"A parser with an explicit depth limit (or an iterative non-recursive design with a bound) is safe."
|
|
300
|
+
]
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"id": "length-field-unbounded-allocation",
|
|
304
|
+
"type": "config_value",
|
|
305
|
+
"value": "A binary parser (ASN.1/DER, CBOR, MIME, protobuf) reads a length/count field and pre-allocates a buffer or collection of that size without bounding it against the actual remaining input.",
|
|
306
|
+
"description": "A crafted length field (e.g. a 2 GB declared length on a 10-byte message) makes the parser allocate huge buffers before reading, exhausting memory on a single message.",
|
|
307
|
+
"confidence": "medium",
|
|
308
|
+
"deterministic": false,
|
|
309
|
+
"attack_ref": "T1499.001",
|
|
310
|
+
"false_positive_checks_required": [
|
|
311
|
+
"Confirm declared lengths/counts are validated against the remaining input size (and a hard ceiling) before allocation — allocating to a declared length without bounding it is the finding.",
|
|
312
|
+
"A parser that streams and allocates incrementally as bytes arrive (not up-front to a declared size) is safe."
|
|
313
|
+
]
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"id": "nested-archive-bomb-uncapped",
|
|
317
|
+
"type": "config_value",
|
|
318
|
+
"value": "Recursive archive handling (an archive containing archives) has no limit on nesting depth or cumulative decompressed output across layers.",
|
|
319
|
+
"description": "A nested bomb (zip-within-zip-within-...) multiplies the amplification at each layer, defeating a single-layer size cap if the cap is not cumulative across recursion.",
|
|
320
|
+
"confidence": "low",
|
|
321
|
+
"deterministic": false,
|
|
322
|
+
"attack_ref": "T1499.001",
|
|
323
|
+
"false_positive_checks_required": [
|
|
324
|
+
"Confirm nested-archive handling caps both recursion depth AND cumulative total output across all layers (not just per-archive) — recursion into inner archives without a cumulative cap is the finding.",
|
|
325
|
+
"A handler that does not recurse into inner archives at all (treats them as opaque files) is not exposed to nested-bomb amplification."
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
],
|
|
329
|
+
"false_positive_profile": [
|
|
330
|
+
{
|
|
331
|
+
"indicator_id": "archive-decompression-unbounded",
|
|
332
|
+
"benign_pattern": "Parsing/decompression bounded by a lower layer (a streaming parser, a size-capped extractor, a linear-time regex engine) or applied only to trusted fixed-size input.",
|
|
333
|
+
"distinguishing_test": "Confirm extraction enforces a total-output-size ceiling AND/OR a per-entry compression-ratio guard (and fails closed when exceeded) — extraction with no size/ratio cap is the finding."
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"indicator_id": "zip-slip-path-traversal",
|
|
337
|
+
"benign_pattern": "Parsing/decompression bounded by a lower layer (a streaming parser, a size-capped extractor, a linear-time regex engine) or applied only to trusted fixed-size input.",
|
|
338
|
+
"distinguishing_test": "Confirm each extracted path is normalised and verified to remain within the target directory (reject `..`, absolute paths, and symlink escapes) before writing — extraction that joins the raw entry name to the target is the finding."
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"indicator_id": "xml-entity-expansion-enabled",
|
|
342
|
+
"benign_pattern": "Parsing/decompression bounded by a lower layer (a streaming parser, a size-capped extractor, a linear-time regex engine) or applied only to trusted fixed-size input.",
|
|
343
|
+
"distinguishing_test": "Confirm the XML parser disables DTDs / external + parameter entities (or caps entity expansion) — a parser with entity expansion and external entities enabled on untrusted input is the finding."
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"indicator_id": "redos-catastrophic-backtracking",
|
|
347
|
+
"benign_pattern": "Parsing/decompression bounded by a lower layer (a streaming parser, a size-capped extractor, a linear-time regex engine) or applied only to trusted fixed-size input.",
|
|
348
|
+
"distinguishing_test": "Confirm regexes on untrusted input are either provably linear (no nested/overlapping quantifiers like (a+)+), length-capped, or run on a linear-time engine (RE2) — a backtracking-prone pattern on uncapped input is the finding."
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"indicator_id": "recursive-parse-no-depth-limit",
|
|
352
|
+
"benign_pattern": "Parsing/decompression bounded by a lower layer (a streaming parser, a size-capped extractor, a linear-time regex engine) or applied only to trusted fixed-size input.",
|
|
353
|
+
"distinguishing_test": "Confirm the parser enforces a maximum nesting depth and fails closed beyond it — a recursive descent / nesting handler with no depth ceiling on untrusted input is the finding."
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"indicator_id": "length-field-unbounded-allocation",
|
|
357
|
+
"benign_pattern": "Parsing/decompression bounded by a lower layer (a streaming parser, a size-capped extractor, a linear-time regex engine) or applied only to trusted fixed-size input.",
|
|
358
|
+
"distinguishing_test": "Confirm declared lengths/counts are validated against the remaining input size (and a hard ceiling) before allocation — allocating to a declared length without bounding it is the finding."
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"indicator_id": "nested-archive-bomb-uncapped",
|
|
362
|
+
"benign_pattern": "Parsing/decompression bounded by a lower layer (a streaming parser, a size-capped extractor, a linear-time regex engine) or applied only to trusted fixed-size input.",
|
|
363
|
+
"distinguishing_test": "Confirm nested-archive handling caps both recursion depth AND cumulative total output across all layers (not just per-archive) — recursion into inner archives without a cumulative cap is the finding."
|
|
364
|
+
}
|
|
365
|
+
],
|
|
366
|
+
"minimum_signal": {
|
|
367
|
+
"detected": "At least one parser/decompression path on attacker-suppliable input lacks a resource bound — no decompression size/ratio cap, XML entities enabled, a backtracking regex on uncapped input, no parse-depth limit, or unbounded length-field allocation.",
|
|
368
|
+
"inconclusive": "A bound is enforced by a lower layer the audit could not read (a streaming runtime, an RE2 engine) — record as visibility_gap, not a clean result.",
|
|
369
|
+
"not_detected": "Decompression caps output size/ratio and nesting, extraction confines paths, the XML parser disables entities, regexes on untrusted input are linear or length-capped, parsers enforce depth limits, and binary parsers bound length-field allocation."
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
"analyze": {
|
|
373
|
+
"rwep_inputs": [
|
|
374
|
+
{
|
|
375
|
+
"signal_id": "archive-decompression-unbounded",
|
|
376
|
+
"rwep_factor": "public_poc",
|
|
377
|
+
"weight": 20
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"signal_id": "xml-entity-expansion-enabled",
|
|
381
|
+
"rwep_factor": "blast_radius",
|
|
382
|
+
"weight": 15
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"signal_id": "redos-catastrophic-backtracking",
|
|
386
|
+
"rwep_factor": "active_exploitation",
|
|
387
|
+
"weight": 10
|
|
388
|
+
}
|
|
389
|
+
],
|
|
390
|
+
"blast_radius_model": {
|
|
391
|
+
"scope_question": "Can a single small crafted input deny service to the whole instance, and is the ingest path internet-facing?",
|
|
392
|
+
"scoring_rubric": [
|
|
393
|
+
{
|
|
394
|
+
"condition": "An internet-facing endpoint accepts attacker uploads/strings and a single crafted input (zip bomb / billion laughs / ReDoS) exhausts the instance",
|
|
395
|
+
"blast_radius_score": 5,
|
|
396
|
+
"description": "Single-request denial of service on a public endpoint"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"condition": "Zip Slip extraction on an internet-facing path enabling arbitrary file write / code execution",
|
|
400
|
+
"blast_radius_score": 5,
|
|
401
|
+
"description": "Path traversal to write/exec via archive extraction"
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"condition": "Parser on an internal/authenticated path only",
|
|
405
|
+
"blast_radius_score": 2,
|
|
406
|
+
"description": "Exploitation requires prior access"
|
|
407
|
+
}
|
|
408
|
+
]
|
|
409
|
+
},
|
|
410
|
+
"compliance_theater_check": {
|
|
411
|
+
"claim": "We validate all input and have a WAF / autoscaling, so amplification attacks are handled.",
|
|
412
|
+
"audit_evidence": "An input-validation library, WAF rules, an autoscaling group.",
|
|
413
|
+
"reality_test": "Feed a zip bomb, a billion-laughs XML, and a ReDoS string. If any expands unbounded, pins a CPU, or allocates from a declared length, validation/WAF/autoscaling did not bound the amplification.",
|
|
414
|
+
"theater_verdict_if_gap": "theater"
|
|
415
|
+
},
|
|
416
|
+
"framework_gap_mapping": [
|
|
417
|
+
{
|
|
418
|
+
"finding_id": "archive-decompression-unbounded",
|
|
419
|
+
"framework": "nist-800-53",
|
|
420
|
+
"claimed_control": "SI-10 (input validation)",
|
|
421
|
+
"actual_gap": "SI-10 validates format; a valid-format zip bomb passes — it does not require a decompression-ratio cap."
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"finding_id": "redos-catastrophic-backtracking",
|
|
425
|
+
"framework": "nist-800-53",
|
|
426
|
+
"claimed_control": "SC-5 (DoS protection)",
|
|
427
|
+
"actual_gap": "SC-5 is network-tier; it does not require linear-time regex on untrusted input against ReDoS."
|
|
428
|
+
}
|
|
429
|
+
],
|
|
430
|
+
"escalation_criteria": [
|
|
431
|
+
{
|
|
432
|
+
"condition": "An internet-facing ingest accepts attacker input and a single crafted payload exhausts the instance",
|
|
433
|
+
"action": "raise_severity"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
"condition": "Archive extraction is vulnerable to Zip Slip on a writable path",
|
|
437
|
+
"action": "trigger_playbook"
|
|
438
|
+
}
|
|
439
|
+
]
|
|
440
|
+
},
|
|
441
|
+
"validate": {
|
|
442
|
+
"remediation_paths": [
|
|
443
|
+
{
|
|
444
|
+
"id": "cap-decompression",
|
|
445
|
+
"description": "Enforce a total decompressed-output size ceiling AND a per-entry compression-ratio guard, plus a cumulative cap and recursion limit for nested archives; fail closed when exceeded.",
|
|
446
|
+
"preconditions": [
|
|
447
|
+
"operator controls the decompression path"
|
|
448
|
+
],
|
|
449
|
+
"priority": 1,
|
|
450
|
+
"for_signals": [
|
|
451
|
+
"archive-decompression-unbounded",
|
|
452
|
+
"nested-archive-bomb-uncapped"
|
|
453
|
+
]
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
"id": "confine-extraction-paths",
|
|
457
|
+
"description": "Normalise each archive entry path and verify it stays within the target directory (reject `..`, absolute paths, symlink escapes) before writing.",
|
|
458
|
+
"preconditions": [],
|
|
459
|
+
"priority": 1,
|
|
460
|
+
"for_signals": [
|
|
461
|
+
"zip-slip-path-traversal"
|
|
462
|
+
]
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"id": "disable-xml-entities",
|
|
466
|
+
"description": "Disable DTD processing and external + parameter entity expansion in the XML parser (or hard-cap expansion) on all untrusted XML.",
|
|
467
|
+
"preconditions": [],
|
|
468
|
+
"priority": 1,
|
|
469
|
+
"for_signals": [
|
|
470
|
+
"xml-entity-expansion-enabled"
|
|
471
|
+
]
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"id": "bound-regex-complexity",
|
|
475
|
+
"description": "Run regexes on untrusted input through a linear-time engine (RE2) or length-cap the input and remove nested/overlapping quantifiers that backtrack.",
|
|
476
|
+
"preconditions": [],
|
|
477
|
+
"priority": 2,
|
|
478
|
+
"for_signals": [
|
|
479
|
+
"redos-catastrophic-backtracking"
|
|
480
|
+
]
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
"id": "limit-depth-and-allocation",
|
|
484
|
+
"description": "Enforce a maximum nesting depth in structured parsers and validate declared length/count fields against remaining input before allocating.",
|
|
485
|
+
"preconditions": [],
|
|
486
|
+
"priority": 2,
|
|
487
|
+
"for_signals": [
|
|
488
|
+
"recursive-parse-no-depth-limit",
|
|
489
|
+
"length-field-unbounded-allocation"
|
|
490
|
+
]
|
|
491
|
+
}
|
|
492
|
+
],
|
|
493
|
+
"validation_tests": [
|
|
494
|
+
{
|
|
495
|
+
"id": "zip-bomb-rejected",
|
|
496
|
+
"test": "Submit a high-ratio decompression bomb (small archive expanding to many GB).",
|
|
497
|
+
"expected_result": "Extraction aborts at the size/ratio ceiling; memory/disk are not exhausted.",
|
|
498
|
+
"test_type": "negative"
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"id": "zip-slip-rejected",
|
|
502
|
+
"test": "Submit an archive with an entry named `../../evil`.",
|
|
503
|
+
"expected_result": "Extraction refuses the escaping path; nothing is written outside the target.",
|
|
504
|
+
"test_type": "negative"
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
"id": "billion-laughs-rejected",
|
|
508
|
+
"test": "Submit a billion-laughs entity-expansion XML document.",
|
|
509
|
+
"expected_result": "The parser rejects entity expansion / DTD; memory is not exhausted.",
|
|
510
|
+
"test_type": "negative"
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
"id": "redos-input-bounded",
|
|
514
|
+
"test": "Submit an input crafted to trigger catastrophic backtracking on a known regex.",
|
|
515
|
+
"expected_result": "Matching completes in linear time (RE2 / length cap); no CPU pinning.",
|
|
516
|
+
"test_type": "negative"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"id": "legitimate-input-parses",
|
|
520
|
+
"test": "Submit a normal archive, XML document, and input string.",
|
|
521
|
+
"expected_result": "All parse/extract normally — no regression on the legitimate path.",
|
|
522
|
+
"test_type": "functional"
|
|
523
|
+
}
|
|
524
|
+
],
|
|
525
|
+
"residual_risk_statement": {
|
|
526
|
+
"risk": "A novel amplification pattern in a parser without a coverage-guided fuzzer can still surface after the known classes are bounded.",
|
|
527
|
+
"why_remains": "Resource caps stop the known amplification classes; an unforeseen pathological input is caught by continuous fuzzing, not by the caps alone.",
|
|
528
|
+
"acceptance_level": "ciso"
|
|
529
|
+
},
|
|
530
|
+
"evidence_requirements": [
|
|
531
|
+
{
|
|
532
|
+
"evidence_type": "config_diff",
|
|
533
|
+
"description": "The decompression size/ratio caps, extraction path-confinement, XML entity settings, regex engine/length caps, and parser depth/allocation bounds as audited.",
|
|
534
|
+
"retention_period": "1 year"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
"evidence_type": "exploit_replay_negative",
|
|
538
|
+
"description": "Outcomes of the zip-bomb / Zip-Slip / billion-laughs / ReDoS negative tests plus the legitimate-input functional test.",
|
|
539
|
+
"retention_period": "1 year"
|
|
540
|
+
}
|
|
541
|
+
],
|
|
542
|
+
"regression_trigger": [
|
|
543
|
+
{
|
|
544
|
+
"condition": "A new parser, decompression path, or regex over untrusted input is added",
|
|
545
|
+
"interval": "on change"
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
"condition": "Continuous coverage-guided fuzzing of the parsers",
|
|
549
|
+
"interval": "continuous"
|
|
550
|
+
}
|
|
551
|
+
]
|
|
552
|
+
},
|
|
553
|
+
"close": {
|
|
554
|
+
"evidence_package": {
|
|
555
|
+
"bundle_format": "csaf-2.0",
|
|
556
|
+
"contents": [
|
|
557
|
+
"parser/decompression config snapshot",
|
|
558
|
+
"per-indicator findings + false-positive disposition",
|
|
559
|
+
"negative + functional test results",
|
|
560
|
+
"framework gap mapping"
|
|
561
|
+
],
|
|
562
|
+
"destination": ".exceptd/attestations/<session_id>/attestation.json"
|
|
563
|
+
},
|
|
564
|
+
"learning_loop": {
|
|
565
|
+
"enabled": true,
|
|
566
|
+
"lesson_template": {
|
|
567
|
+
"attack_vector": "Single crafted input (zip bomb / nested bomb / Zip Slip / billion-laughs XML / ReDoS / length-field over-allocation) that amplifies into resource exhaustion or arbitrary file write because the parser imposed no resource bound.",
|
|
568
|
+
"control_gap": "Decompression / XML / regex / structured-parser path lacks size-ratio caps, entity disabling, linear-regex, depth limits, or length-field bounds.",
|
|
569
|
+
"framework_gap": "NIST SI-10 + SC-5 validate format and protect the network tier but not single-request application-layer amplification.",
|
|
570
|
+
"new_control_requirement": "Every parser/decompressor on untrusted input MUST enforce a resource bound (size/ratio cap, entity disable, linear regex, depth + allocation limit)."
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
"notification_actions": [
|
|
574
|
+
{
|
|
575
|
+
"obligation_ref": "EU/NIS2 Art.23 24h",
|
|
576
|
+
"deadline": "24h from detect_confirmed",
|
|
577
|
+
"recipient": "national CSIRT / competent authority",
|
|
578
|
+
"evidence_attached": [
|
|
579
|
+
"attestation"
|
|
580
|
+
]
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
"obligation_ref": "EU/EU CRA Annex I 24h",
|
|
584
|
+
"deadline": "24h from detect_confirmed",
|
|
585
|
+
"recipient": "CRA market-surveillance authority",
|
|
586
|
+
"evidence_attached": [
|
|
587
|
+
"attestation"
|
|
588
|
+
]
|
|
589
|
+
}
|
|
590
|
+
],
|
|
591
|
+
"exception_generation": {
|
|
592
|
+
"trigger_condition": "A legacy parser cannot adopt a resource bound immediately.",
|
|
593
|
+
"exception_template": {
|
|
594
|
+
"scope": "the specific parser / endpoint",
|
|
595
|
+
"duration": "90 days",
|
|
596
|
+
"compensating_controls": [
|
|
597
|
+
"front the ingest with a size-limited reverse proxy",
|
|
598
|
+
"run the parse in a memory/CPU-limited sandbox",
|
|
599
|
+
"rate-limit the ingest endpoint"
|
|
600
|
+
],
|
|
601
|
+
"risk_acceptance_owner": "ciso"
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
"regression_schedule": {
|
|
605
|
+
"next_run": "continuous fuzzing + quarterly re-attestation",
|
|
606
|
+
"trigger": "change to a parser / decompression / regex path, or quarterly re-attestation"
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
"directives": [
|
|
611
|
+
{
|
|
612
|
+
"id": "all-parser-and-decompression-paths",
|
|
613
|
+
"title": "Inventory + resource-bound-test every parser and decompression path on untrusted input",
|
|
614
|
+
"applies_to": {
|
|
615
|
+
"always": true
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
"id": "amplification-dos-sweep",
|
|
620
|
+
"title": "Targeted zip-bomb / billion-laughs / ReDoS sweep on internet-facing ingest",
|
|
621
|
+
"applies_to": {
|
|
622
|
+
"attack_technique": "T1499.001"
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
]
|
|
626
|
+
}
|
|
@@ -54,11 +54,13 @@
|
|
|
54
54
|
"cloud-iam-incident",
|
|
55
55
|
"crypto",
|
|
56
56
|
"crypto-codebase",
|
|
57
|
+
"decompression-dos",
|
|
57
58
|
"identity-sso-compromise",
|
|
58
59
|
"idp-incident",
|
|
59
60
|
"kernel",
|
|
60
61
|
"library-author",
|
|
61
62
|
"llm-tool-use-exfil",
|
|
63
|
+
"log-injection-telemetry",
|
|
62
64
|
"mail-server-hardening",
|
|
63
65
|
"mcp",
|
|
64
66
|
"multitenancy-isolation",
|