@blamejs/exceptd-skills 0.16.17 → 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 +2 -1
- package/CHANGELOG.md +4 -0
- package/README.md +5 -5
- package/bin/exceptd.js +2 -1
- package/data/_indexes/_meta.json +15 -14
- package/data/_indexes/activity-feed.json +10 -3
- package/data/_indexes/catalog-summaries.json +1 -1
- package/data/_indexes/chains.json +10092 -616
- package/data/_indexes/currency.json +10 -1
- package/data/_indexes/frequency.json +95 -68
- package/data/_indexes/handoff-dag.json +5 -1
- package/data/_indexes/jurisdiction-map.json +6 -3
- package/data/_indexes/section-offsets.json +85 -0
- package/data/_indexes/stale-content.json +1 -1
- package/data/_indexes/summary-cards.json +39 -0
- package/data/_indexes/token-budget.json +53 -3
- package/data/_indexes/trigger-table.json +45 -0
- package/data/_indexes/xref.json +19 -3
- package/data/cwe-catalog.json +26 -1
- package/data/playbooks/audit-log-integrity.json +3 -0
- package/data/playbooks/framework.json +1 -0
- package/data/playbooks/log-injection-telemetry.json +619 -0
- package/data/playbooks/secrets.json +1 -0
- package/manifest-snapshot.json +53 -2
- package/manifest-snapshot.sha256 +1 -1
- package/manifest.json +106 -51
- package/package.json +2 -2
- package/sbom.cdx.json +62 -32
- package/skills/log-injection-telemetry/skill.md +80 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"id": "log-injection-telemetry",
|
|
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 telemetry-pipeline integrity playbook — log injection + telemetry-sink confidentiality. Covers the integrity and leakage failures of the observability path that logging-presence controls do not: CR/LF log injection forging entries on every sink except syslog, secrets/PII logged without redaction, unauthenticated /metrics + debug endpoints leaking internal state, telemetry exporters shipping to un-inventoried or input-derived destinations (exfil/SSRF), embedded exporter credentials, and plaintext/relaxed-TLS export. Distinct from dlp-gap-analysis (LLM/RAG exfil) — this is the telemetry pipeline. Adds CWE-117 (improper output neutralization for logs) to the catalog. Maps to ATT&CK T1565.001 / T1530 / T1213 and to NIST 800-53 AU-9 / SI-11, ISO 27001 A.8.15, NIS2 Art.21, UK CAF B4, and AU ISM."
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"owner": "@blamejs/platform-security",
|
|
15
|
+
"air_gap_mode": false,
|
|
16
|
+
"scope": "service",
|
|
17
|
+
"preconditions": [
|
|
18
|
+
{
|
|
19
|
+
"id": "logging-source-or-config-read",
|
|
20
|
+
"description": "Agent must read the operator's logging / telemetry-exporter / metrics source and/or configuration to inspect per-sink neutralization, redaction, exporter destinations/credentials/TLS, and metrics-endpoint exposure. A host with neither marks the playbook visibility_gap=no_telemetry_inventory.",
|
|
21
|
+
"check": "agent_has_filesystem_read == true OR agent_has_telemetry_config == true",
|
|
22
|
+
"on_fail": "halt"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"mutex": [],
|
|
26
|
+
"feeds_into": [
|
|
27
|
+
{
|
|
28
|
+
"playbook_id": "secrets",
|
|
29
|
+
"condition": "finding.includes_embedded_sink_credential == true"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"playbook_id": "audit-log-integrity",
|
|
33
|
+
"condition": "finding.includes_log_forging == true"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"playbook_id": "framework",
|
|
37
|
+
"condition": "analyze.compliance_theater_check.verdict == 'theater'"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"domain": {
|
|
42
|
+
"name": "Telemetry-pipeline integrity (log injection + sink confidentiality)",
|
|
43
|
+
"attack_class": "cloud-misconfig",
|
|
44
|
+
"atlas_refs": [],
|
|
45
|
+
"attack_refs": [
|
|
46
|
+
"T1565.001",
|
|
47
|
+
"T1530",
|
|
48
|
+
"T1213"
|
|
49
|
+
],
|
|
50
|
+
"cve_refs": [],
|
|
51
|
+
"cwe_refs": [
|
|
52
|
+
"CWE-117",
|
|
53
|
+
"CWE-532",
|
|
54
|
+
"CWE-918",
|
|
55
|
+
"CWE-200"
|
|
56
|
+
],
|
|
57
|
+
"frameworks_in_scope": [
|
|
58
|
+
"nist-800-53",
|
|
59
|
+
"iso-27001-2022",
|
|
60
|
+
"nis2",
|
|
61
|
+
"uk-caf",
|
|
62
|
+
"au-ism"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"phases": {
|
|
66
|
+
"govern": {
|
|
67
|
+
"jurisdiction_obligations": [
|
|
68
|
+
{
|
|
69
|
+
"jurisdiction": "EU",
|
|
70
|
+
"regulation": "GDPR Art.33",
|
|
71
|
+
"obligation": "notify_regulator",
|
|
72
|
+
"window_hours": 72,
|
|
73
|
+
"clock_starts": "detect_confirmed",
|
|
74
|
+
"evidence_required": [
|
|
75
|
+
"pii_in_logs_scope",
|
|
76
|
+
"sink_exposure_estimate"
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"jurisdiction": "EU",
|
|
81
|
+
"regulation": "NIS2 Art.23",
|
|
82
|
+
"obligation": "notify_regulator",
|
|
83
|
+
"window_hours": 24,
|
|
84
|
+
"clock_starts": "detect_confirmed",
|
|
85
|
+
"evidence_required": [
|
|
86
|
+
"telemetry_inventory",
|
|
87
|
+
"log_forging_or_exfil_evidence"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"theater_fingerprints": [
|
|
92
|
+
{
|
|
93
|
+
"pattern_id": "we-have-centralized-logging",
|
|
94
|
+
"claim": "We centralize all logs to a SIEM, so logging is handled.",
|
|
95
|
+
"fast_detection_test": "Centralization is not integrity or confidentiality. Ask whether CR/LF is neutralized per sink (log forging), whether secrets are redacted before shipping, and whether the /metrics endpoint is authenticated."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"pattern_id": "logs-are-access-controlled",
|
|
99
|
+
"claim": "The log store has access controls, so logs are protected.",
|
|
100
|
+
"fast_detection_test": "Store ACLs do not stop INJECTION at write time. Ask whether interpolated values are CR/LF-neutralized — an attacker forges entries before they reach the protected store."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"pattern_id": "metrics-are-internal",
|
|
104
|
+
"claim": "Our metrics are internal-only.",
|
|
105
|
+
"fast_detection_test": "Confirm the network binding. An \"internal\" /metrics that is actually reachable (default 0.0.0.0 bind, exposed ingress) leaks internal state to anyone who can reach it."
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"framework_context": {
|
|
109
|
+
"gap_summary": "Org logging controls require that events are recorded, centralized, and access-controlled. None address the integrity (CR/LF injection / log forging) and confidentiality (secrets in logs, exporter egress/SSRF, unauthenticated metrics) of the telemetry pipeline itself.",
|
|
110
|
+
"lag_score": 70,
|
|
111
|
+
"per_framework_gaps": [
|
|
112
|
+
{
|
|
113
|
+
"framework": "nist-800-53",
|
|
114
|
+
"control_id": "AU-9",
|
|
115
|
+
"designed_for": "protection of audit information",
|
|
116
|
+
"insufficient_because": "attested by store access controls; does not address CR/LF log injection that forges entries before storage."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"framework": "nist-800-53",
|
|
120
|
+
"control_id": "SI-11",
|
|
121
|
+
"designed_for": "error handling / output neutralization",
|
|
122
|
+
"insufficient_because": "not operationalised as per-sink CR/LF neutralization or secret redaction on the telemetry path."
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"skill_preload": [
|
|
127
|
+
"log-injection-telemetry",
|
|
128
|
+
"webapp-security",
|
|
129
|
+
"dlp-gap-analysis",
|
|
130
|
+
"framework-gap-analysis",
|
|
131
|
+
"compliance-theater",
|
|
132
|
+
"policy-exception-gen"
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
"direct": {
|
|
136
|
+
"threat_context": "The telemetry pipeline is both an integrity target and a confidentiality leak. Integrity: un-sanitized CR/LF lets an attacker forge or split log entries, corrupting the record incident response depends on (only the syslog sink here sanitizes). Confidentiality: secrets/PII logged without redaction persist in every downstream sink; an unauthenticated /metrics leaks internal state; exporters shipping to un-inventoried or input-derived endpoints become exfil/SSRF channels; embedded sink credentials and plaintext export widen it. These are pipeline-posture gaps that \"we centralize logs\" does not address.",
|
|
137
|
+
"rwep_threshold": {
|
|
138
|
+
"escalate": 50,
|
|
139
|
+
"monitor": 30,
|
|
140
|
+
"close": 15
|
|
141
|
+
},
|
|
142
|
+
"framework_lag_declaration": "A clean \"we centralize logs to a SIEM with access controls\" audit is NON-EVIDENCE for telemetry-pipeline integrity; it confirms log presence and store ACLs, not CR/LF neutralization, secret redaction, metrics-endpoint auth, or exporter egress/SSRF/TLS posture.",
|
|
143
|
+
"skill_chain": [
|
|
144
|
+
{
|
|
145
|
+
"skill": "log-injection-telemetry",
|
|
146
|
+
"purpose": "enumerate the per-sink neutralization / redaction / exporter / metrics checks"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"skill": "webapp-security",
|
|
150
|
+
"purpose": "cross-reference the OWASP log-injection and SSRF classes"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"skill": "dlp-gap-analysis",
|
|
154
|
+
"purpose": "frame the secrets/PII-in-telemetry leakage dimension (without duplicating LLM/RAG exfil)"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"skill": "framework-gap-analysis",
|
|
158
|
+
"purpose": "map findings to the AU-9 / SI-11 gaps the controls do not cover"
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
"token_budget": {
|
|
162
|
+
"estimated_total": 12000,
|
|
163
|
+
"breakdown": {
|
|
164
|
+
"govern": 1200,
|
|
165
|
+
"direct": 800,
|
|
166
|
+
"look": 4000,
|
|
167
|
+
"detect": 3800,
|
|
168
|
+
"analyze": 1500,
|
|
169
|
+
"validate": 500,
|
|
170
|
+
"close": 200
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"look": {
|
|
175
|
+
"artifacts": [
|
|
176
|
+
{
|
|
177
|
+
"id": "log-write-path",
|
|
178
|
+
"type": "config_file",
|
|
179
|
+
"source": "grep for log.js / log-stream / logger / format / CRLF / sanitiz / redact across the logging write path and per-sink formatters.",
|
|
180
|
+
"description": "Whether interpolated values are CR/LF-neutralized per sink and whether a redaction pass runs before values reach any sink.",
|
|
181
|
+
"required": true,
|
|
182
|
+
"air_gap_alternative": "Inspect the per-sink formatter source for control-character neutralization and the redaction stage."
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"id": "telemetry-exporter-config",
|
|
186
|
+
"type": "config_file",
|
|
187
|
+
"source": "grep for log-stream-otlp / otlp-grpc / cloudwatch / webhook / otel-export / endpoint / url / insecure / tls across the exporter config.",
|
|
188
|
+
"description": "Exporter destinations (inventoried/allowlisted?), embedded credentials, TLS posture, and webhook SSRF guarding.",
|
|
189
|
+
"required": true,
|
|
190
|
+
"air_gap_alternative": "Inspect the exporter config for destination allowlisting, credential source, TLS verification, and webhook SSRF guards."
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"id": "metrics-and-debug-endpoints",
|
|
194
|
+
"type": "config_file",
|
|
195
|
+
"source": "grep for metrics.js / server-timing / /metrics / prometheus / debug / pprof / actuator across the observability surface + the route auth config.",
|
|
196
|
+
"description": "Whether metrics/debug/telemetry endpoints require auth or are bound to a private interface.",
|
|
197
|
+
"required": false,
|
|
198
|
+
"air_gap_alternative": "Inspect the metrics/debug route registration + its auth/binding."
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "secret-and-pii-redaction",
|
|
202
|
+
"type": "config_file",
|
|
203
|
+
"source": "grep for redact / mask / pii / secret pattern / token across the redaction module and its wiring into the log path.",
|
|
204
|
+
"description": "Whether a redaction pass strips secrets/PII before values reach the sinks.",
|
|
205
|
+
"required": false,
|
|
206
|
+
"air_gap_alternative": "Inspect the redaction module and confirm it is wired into every sink path."
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
"collection_scope": {
|
|
210
|
+
"time_window": "current logging / telemetry configuration + source state (point-in-time posture audit)",
|
|
211
|
+
"asset_scope": "every log/trace/metric sink + exporter and every metrics/debug endpoint"
|
|
212
|
+
},
|
|
213
|
+
"environment_assumptions": [
|
|
214
|
+
{
|
|
215
|
+
"assumption": "The product writes logs/traces/metrics to one or more sinks.",
|
|
216
|
+
"if_false": "A product with no telemetry pipeline has no surface here."
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"assumption": "Logging/telemetry source or config is readable.",
|
|
220
|
+
"if_false": "Mark visibility_gap=no_telemetry_inventory and report only what the logging source reveals."
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
"fallback_if_unavailable": [
|
|
224
|
+
{
|
|
225
|
+
"artifact_id": "metrics-and-debug-endpoints",
|
|
226
|
+
"fallback_action": "If no metrics/debug endpoint is exposed, skip that indicator.",
|
|
227
|
+
"confidence_impact": "none"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"artifact_id": "secret-and-pii-redaction",
|
|
231
|
+
"fallback_action": "If logging is strictly structured with a field allowlist, the redaction indicator is reduced-scope.",
|
|
232
|
+
"confidence_impact": "low"
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
"detect": {
|
|
237
|
+
"indicators": [
|
|
238
|
+
{
|
|
239
|
+
"id": "crlf-log-injection-unsanitized",
|
|
240
|
+
"type": "config_value",
|
|
241
|
+
"value": "Log entries are written to a sink (CloudWatch / OTLP / webhook / local file) with operator- or attacker-influenced values that are not neutralized for CR/LF and control characters — only the syslog sink sanitizes.",
|
|
242
|
+
"description": "Un-sanitized CR/LF lets an attacker forge or split log entries (injecting fake lines, breaking the parser, or hiding their own actions), corrupting the audit/observability record that incident response relies on.",
|
|
243
|
+
"confidence": "high",
|
|
244
|
+
"deterministic": false,
|
|
245
|
+
"attack_ref": "T1565.001",
|
|
246
|
+
"false_positive_checks_required": [
|
|
247
|
+
"Confirm every sink (not just syslog) neutralizes CR/LF + control characters in interpolated values (or uses a structured/encoded format that cannot be line-split) — a sink writing raw user-influenced text is the finding.",
|
|
248
|
+
"A purely structured sink (JSON-encoded fields the consumer parses structurally, never line-split) is safe; the finding is a line-oriented sink interpolating un-neutralized values."
|
|
249
|
+
]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"id": "metrics-endpoint-unauthenticated",
|
|
253
|
+
"type": "config_value",
|
|
254
|
+
"value": "A /metrics (Prometheus) or debug/telemetry endpoint is exposed without authentication, leaking internal state (build info, internal hostnames, queue depths, user counts, sometimes tokens in labels).",
|
|
255
|
+
"description": "An unauthenticated metrics/debug endpoint is a reconnaissance goldmine — internal topology and operational state an attacker uses to plan, and occasionally secrets leaked into metric labels.",
|
|
256
|
+
"confidence": "medium",
|
|
257
|
+
"deterministic": false,
|
|
258
|
+
"attack_ref": "T1213",
|
|
259
|
+
"false_positive_checks_required": [
|
|
260
|
+
"Confirm the metrics/telemetry endpoint requires authentication or is bound to a private interface / scrape network not reachable by untrusted clients — an internet-reachable unauthenticated /metrics is the finding.",
|
|
261
|
+
"A metrics endpoint on a loopback / private scrape subnet that the public cannot reach is safe; confirm the effective network exposure, not just that auth is off."
|
|
262
|
+
]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"id": "secrets-or-pii-logged-without-redaction",
|
|
266
|
+
"type": "config_value",
|
|
267
|
+
"value": "Sensitive values (API keys, tokens, passwords, PII) reach the logs without a redaction pass on the log/telemetry path.",
|
|
268
|
+
"description": "Secrets or PII in logs persist in every sink and downstream store (SIEM, cloud log service), widening the exposure surface and turning a log breach into a credential/PII breach.",
|
|
269
|
+
"confidence": "high",
|
|
270
|
+
"deterministic": false,
|
|
271
|
+
"attack_ref": "T1530",
|
|
272
|
+
"false_positive_checks_required": [
|
|
273
|
+
"Confirm a redaction pass strips known secret patterns / PII before values reach any sink — log statements that interpolate credentials/PII with no redaction are the finding.",
|
|
274
|
+
"A field-level allowlist that structurally cannot include sensitive fields is safe; the finding is free-form logging of objects that may contain secrets without redaction."
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"id": "telemetry-egress-endpoints-not-inventoried",
|
|
279
|
+
"type": "config_value",
|
|
280
|
+
"value": "Log/trace/metric exporters (OTLP, CloudWatch, webhook) ship to destination endpoints that are not inventoried or are configurable from attacker-influenceable input, with no allowlist.",
|
|
281
|
+
"description": "An un-inventoried or input-derived exporter destination is an exfiltration channel — telemetry (which often contains sensitive operational data) is shipped wherever the (possibly poisoned) config points.",
|
|
282
|
+
"confidence": "medium",
|
|
283
|
+
"deterministic": false,
|
|
284
|
+
"attack_ref": "T1530",
|
|
285
|
+
"false_positive_checks_required": [
|
|
286
|
+
"Confirm exporter destinations are a fixed, inventoried allowlist not derived from untrusted input — an exporter endpoint taken from an env/config that an attacker could influence, with no allowlist, is the finding.",
|
|
287
|
+
"A hard-coded or operator-pinned exporter endpoint is safe; the finding is a dynamically-sourced destination with no allowlist."
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"id": "telemetry-sink-credentials-embedded",
|
|
292
|
+
"type": "config_value",
|
|
293
|
+
"value": "Exporter/sink credentials (API keys, bearer tokens, webhook secrets) are embedded in source or plaintext config rather than a secret store.",
|
|
294
|
+
"description": "Embedded sink credentials leak through the same source/config exposure as any hard-coded secret, granting an attacker access to the telemetry pipeline (read others' telemetry, or inject forged data).",
|
|
295
|
+
"confidence": "medium",
|
|
296
|
+
"deterministic": false,
|
|
297
|
+
"attack_ref": "T1530",
|
|
298
|
+
"false_positive_checks_required": [
|
|
299
|
+
"Confirm sink credentials are resolved from a secret store / injected at runtime, not committed in source or plaintext config — a token literal in the exporter config is the finding.",
|
|
300
|
+
"A credential injected via a mounted secret / vault reference is safe; the finding is a literal in tracked source/config."
|
|
301
|
+
]
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"id": "telemetry-egress-tls-relaxed",
|
|
305
|
+
"type": "config_value",
|
|
306
|
+
"value": "Telemetry is exported over plaintext, or over TLS with certificate verification disabled / a relaxed trust setting.",
|
|
307
|
+
"description": "Plaintext or unverified-TLS export exposes the telemetry stream (and any secrets/PII it carries) to on-path interception or redirection, and undermines the integrity of the shipped records.",
|
|
308
|
+
"confidence": "low",
|
|
309
|
+
"deterministic": false,
|
|
310
|
+
"attack_ref": "T1530",
|
|
311
|
+
"false_positive_checks_required": [
|
|
312
|
+
"Confirm exporters use TLS with verification enabled — plaintext export, or TLS with insecure_skip_verify / disabled cert validation, is the finding.",
|
|
313
|
+
"An exporter shipping over a mutually-authenticated private link (mTLS / a trusted in-cluster path) is safe even if the public CA chain is not used."
|
|
314
|
+
]
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"id": "webhook-log-sink-ssrf-unguarded",
|
|
318
|
+
"type": "config_value",
|
|
319
|
+
"value": "A webhook log/alert sink URL is taken from configuration and requested without an SSRF guard (no allowlist, follows redirects, resolves to private/link-local/metadata addresses).",
|
|
320
|
+
"description": "An attacker who can set or poison the webhook sink URL turns the logging pipeline into an SSRF primitive — reaching internal services or the cloud metadata endpoint from the telemetry process.",
|
|
321
|
+
"confidence": "medium",
|
|
322
|
+
"deterministic": false,
|
|
323
|
+
"attack_ref": "T1530",
|
|
324
|
+
"false_positive_checks_required": [
|
|
325
|
+
"Confirm the webhook sink URL is allowlisted and the request refuses private/link-local/metadata addresses and redirects — a sink URL from config requested with no SSRF guard is the finding.",
|
|
326
|
+
"A fixed, operator-pinned webhook to a known external endpoint with no dynamic component is lower-risk; the finding is a dynamically-set sink without an SSRF guard."
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
],
|
|
330
|
+
"false_positive_profile": [
|
|
331
|
+
{
|
|
332
|
+
"indicator_id": "crlf-log-injection-unsanitized",
|
|
333
|
+
"benign_pattern": "A control enforced at a lower layer (a structured-only sink, a private scrape network, a secret store, a pinned exporter) or a sink that handles only non-sensitive structured data.",
|
|
334
|
+
"distinguishing_test": "Confirm every sink (not just syslog) neutralizes CR/LF + control characters in interpolated values (or uses a structured/encoded format that cannot be line-split) — a sink writing raw user-influenced text is the finding."
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"indicator_id": "metrics-endpoint-unauthenticated",
|
|
338
|
+
"benign_pattern": "A control enforced at a lower layer (a structured-only sink, a private scrape network, a secret store, a pinned exporter) or a sink that handles only non-sensitive structured data.",
|
|
339
|
+
"distinguishing_test": "Confirm the metrics/telemetry endpoint requires authentication or is bound to a private interface / scrape network not reachable by untrusted clients — an internet-reachable unauthenticated /metrics is the finding."
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"indicator_id": "secrets-or-pii-logged-without-redaction",
|
|
343
|
+
"benign_pattern": "A control enforced at a lower layer (a structured-only sink, a private scrape network, a secret store, a pinned exporter) or a sink that handles only non-sensitive structured data.",
|
|
344
|
+
"distinguishing_test": "Confirm a redaction pass strips known secret patterns / PII before values reach any sink — log statements that interpolate credentials/PII with no redaction are the finding."
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"indicator_id": "telemetry-egress-endpoints-not-inventoried",
|
|
348
|
+
"benign_pattern": "A control enforced at a lower layer (a structured-only sink, a private scrape network, a secret store, a pinned exporter) or a sink that handles only non-sensitive structured data.",
|
|
349
|
+
"distinguishing_test": "Confirm exporter destinations are a fixed, inventoried allowlist not derived from untrusted input — an exporter endpoint taken from an env/config that an attacker could influence, with no allowlist, is the finding."
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"indicator_id": "telemetry-sink-credentials-embedded",
|
|
353
|
+
"benign_pattern": "A control enforced at a lower layer (a structured-only sink, a private scrape network, a secret store, a pinned exporter) or a sink that handles only non-sensitive structured data.",
|
|
354
|
+
"distinguishing_test": "Confirm sink credentials are resolved from a secret store / injected at runtime, not committed in source or plaintext config — a token literal in the exporter config is the finding."
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"indicator_id": "telemetry-egress-tls-relaxed",
|
|
358
|
+
"benign_pattern": "A control enforced at a lower layer (a structured-only sink, a private scrape network, a secret store, a pinned exporter) or a sink that handles only non-sensitive structured data.",
|
|
359
|
+
"distinguishing_test": "Confirm exporters use TLS with verification enabled — plaintext export, or TLS with insecure_skip_verify / disabled cert validation, is the finding."
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"indicator_id": "webhook-log-sink-ssrf-unguarded",
|
|
363
|
+
"benign_pattern": "A control enforced at a lower layer (a structured-only sink, a private scrape network, a secret store, a pinned exporter) or a sink that handles only non-sensitive structured data.",
|
|
364
|
+
"distinguishing_test": "Confirm the webhook sink URL is allowlisted and the request refuses private/link-local/metadata addresses and redirects — a sink URL from config requested with no SSRF guard is the finding."
|
|
365
|
+
}
|
|
366
|
+
],
|
|
367
|
+
"minimum_signal": {
|
|
368
|
+
"detected": "At least one telemetry path lacks an integrity/confidentiality control — un-neutralized CR/LF on a sink, secrets/PII logged without redaction, an unauthenticated reachable metrics endpoint, an un-allowlisted exporter destination, embedded sink credentials, relaxed export TLS, or an SSRF-unguarded webhook sink.",
|
|
369
|
+
"inconclusive": "A control is enforced at a layer the audit could not read (a logging sidecar that sanitizes, a private scrape network) — record as visibility_gap, not a clean result.",
|
|
370
|
+
"not_detected": "Every sink neutralizes CR/LF (or is structured-only), a redaction pass strips secrets/PII, metrics/debug endpoints are authenticated or private, exporter destinations are allowlisted with secret-store credentials over verified TLS, and webhook sinks are SSRF-guarded."
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
"analyze": {
|
|
374
|
+
"rwep_inputs": [
|
|
375
|
+
{
|
|
376
|
+
"signal_id": "secrets-or-pii-logged-without-redaction",
|
|
377
|
+
"rwep_factor": "blast_radius",
|
|
378
|
+
"weight": 20
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"signal_id": "crlf-log-injection-unsanitized",
|
|
382
|
+
"rwep_factor": "public_poc",
|
|
383
|
+
"weight": 20
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
"signal_id": "webhook-log-sink-ssrf-unguarded",
|
|
387
|
+
"rwep_factor": "active_exploitation",
|
|
388
|
+
"weight": 10
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
"blast_radius_model": {
|
|
392
|
+
"scope_question": "Does the gap corrupt the audit record for the whole system, leak secrets/PII across every downstream sink, or turn the telemetry process into an SSRF/exfil channel?",
|
|
393
|
+
"scoring_rubric": [
|
|
394
|
+
{
|
|
395
|
+
"condition": "Secrets/PII logged without redaction across all sinks, or a webhook sink usable for SSRF to cloud metadata",
|
|
396
|
+
"blast_radius_score": 5,
|
|
397
|
+
"description": "Credential/PII exposure fleet-wide or internal-network reach from the telemetry process"
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"condition": "CR/LF log forging that corrupts the audit record incident response depends on",
|
|
401
|
+
"blast_radius_score": 4,
|
|
402
|
+
"description": "Attacker can hide actions / forge entries across the observability record"
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"condition": "Unauthenticated internal-only metrics leaking recon data",
|
|
406
|
+
"blast_radius_score": 2,
|
|
407
|
+
"description": "Reconnaissance value, limited direct impact"
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
},
|
|
411
|
+
"compliance_theater_check": {
|
|
412
|
+
"claim": "We centralize all logs to a SIEM with access controls, so logging is handled.",
|
|
413
|
+
"audit_evidence": "A SIEM, log-store IAM policies, a logging dashboard.",
|
|
414
|
+
"reality_test": "Inject CR/LF into a logged value and check whether the sink forges a line; log a secret and check redaction; reach /metrics unauthenticated; inspect exporter destinations + credentials + TLS. If forging, secret leakage, or exfil/SSRF succeeds, centralization did not protect the pipeline.",
|
|
415
|
+
"theater_verdict_if_gap": "theater"
|
|
416
|
+
},
|
|
417
|
+
"framework_gap_mapping": [
|
|
418
|
+
{
|
|
419
|
+
"finding_id": "crlf-log-injection-unsanitized",
|
|
420
|
+
"framework": "nist-800-53",
|
|
421
|
+
"claimed_control": "AU-9 (protection of audit information)",
|
|
422
|
+
"actual_gap": "AU-9 protects the store; it does not address injection at write time that forges entries before they reach the store."
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
"finding_id": "secrets-or-pii-logged-without-redaction",
|
|
426
|
+
"framework": "iso-27001-2022",
|
|
427
|
+
"claimed_control": "A.8.15 (logging)",
|
|
428
|
+
"actual_gap": "A.8.15 is attested by \"we log and protect logs\"; it does not require redaction of secrets/PII before they reach the sinks."
|
|
429
|
+
}
|
|
430
|
+
],
|
|
431
|
+
"escalation_criteria": [
|
|
432
|
+
{
|
|
433
|
+
"condition": "Secrets/PII are logged without redaction, or a webhook sink reaches cloud metadata via SSRF",
|
|
434
|
+
"action": "raise_severity"
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"condition": "CR/LF log forging is possible on the audit sink incident response relies on",
|
|
438
|
+
"action": "trigger_playbook"
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
},
|
|
442
|
+
"validate": {
|
|
443
|
+
"remediation_paths": [
|
|
444
|
+
{
|
|
445
|
+
"id": "neutralize-and-redact",
|
|
446
|
+
"description": "Neutralize CR/LF + control characters in interpolated values on every sink (or move to a structured-only format), and run a redaction pass that strips secrets/PII before any sink.",
|
|
447
|
+
"preconditions": [
|
|
448
|
+
"operator controls the logging path"
|
|
449
|
+
],
|
|
450
|
+
"priority": 1,
|
|
451
|
+
"for_signals": [
|
|
452
|
+
"crlf-log-injection-unsanitized",
|
|
453
|
+
"secrets-or-pii-logged-without-redaction"
|
|
454
|
+
]
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
"id": "authenticate-metrics",
|
|
458
|
+
"description": "Require authentication on metrics/debug/telemetry endpoints or bind them to a private scrape network unreachable by untrusted clients.",
|
|
459
|
+
"preconditions": [],
|
|
460
|
+
"priority": 2,
|
|
461
|
+
"for_signals": [
|
|
462
|
+
"metrics-endpoint-unauthenticated"
|
|
463
|
+
]
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
"id": "lock-down-exporters",
|
|
467
|
+
"description": "Allowlist exporter destinations (not derived from untrusted input), resolve sink credentials from a secret store, and require verified TLS on export.",
|
|
468
|
+
"preconditions": [],
|
|
469
|
+
"priority": 1,
|
|
470
|
+
"for_signals": [
|
|
471
|
+
"telemetry-egress-endpoints-not-inventoried",
|
|
472
|
+
"telemetry-sink-credentials-embedded",
|
|
473
|
+
"telemetry-egress-tls-relaxed"
|
|
474
|
+
]
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"id": "ssrf-guard-webhook-sinks",
|
|
478
|
+
"description": "Allowlist webhook sink URLs and refuse private/link-local/metadata addresses and redirects on the sink request.",
|
|
479
|
+
"preconditions": [],
|
|
480
|
+
"priority": 2,
|
|
481
|
+
"for_signals": [
|
|
482
|
+
"webhook-log-sink-ssrf-unguarded"
|
|
483
|
+
]
|
|
484
|
+
}
|
|
485
|
+
],
|
|
486
|
+
"validation_tests": [
|
|
487
|
+
{
|
|
488
|
+
"id": "crlf-injection-neutralized",
|
|
489
|
+
"test": "Log a value containing CR/LF and control characters.",
|
|
490
|
+
"expected_result": "The sink records a single neutralized/encoded entry; no forged second line.",
|
|
491
|
+
"test_type": "negative"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
"id": "secret-redacted",
|
|
495
|
+
"test": "Log an object containing a token and a PII field.",
|
|
496
|
+
"expected_result": "The token and PII are redacted before reaching any sink.",
|
|
497
|
+
"test_type": "negative"
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
"id": "metrics-requires-auth",
|
|
501
|
+
"test": "Request the metrics/debug endpoint from an untrusted client without credentials.",
|
|
502
|
+
"expected_result": "The request is refused (auth required) or unreachable (private binding).",
|
|
503
|
+
"test_type": "negative"
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"id": "webhook-sink-ssrf-blocked",
|
|
507
|
+
"test": "Point a webhook sink at a private/metadata address.",
|
|
508
|
+
"expected_result": "The sink request is refused by the SSRF guard / allowlist.",
|
|
509
|
+
"test_type": "negative"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"id": "legitimate-telemetry-flows",
|
|
513
|
+
"test": "Emit a normal log line, metric, and trace through the hardened pipeline.",
|
|
514
|
+
"expected_result": "Telemetry flows normally — no regression on the legitimate path.",
|
|
515
|
+
"test_type": "functional"
|
|
516
|
+
}
|
|
517
|
+
],
|
|
518
|
+
"residual_risk_statement": {
|
|
519
|
+
"risk": "Telemetry necessarily contains some operational data; a compromise of the (legitimate) sink store still exposes it.",
|
|
520
|
+
"why_remains": "Pipeline hardening prevents injection, secret leakage, and exfil channels; it does not eliminate the inherent sensitivity of telemetry held in a legitimate, access-controlled store.",
|
|
521
|
+
"acceptance_level": "ciso"
|
|
522
|
+
},
|
|
523
|
+
"evidence_requirements": [
|
|
524
|
+
{
|
|
525
|
+
"evidence_type": "config_diff",
|
|
526
|
+
"description": "The per-sink neutralization, redaction, metrics-auth, exporter allowlist/credential/TLS, and webhook-SSRF configuration as audited.",
|
|
527
|
+
"retention_period": "1 year"
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
"evidence_type": "exploit_replay_negative",
|
|
531
|
+
"description": "Outcomes of the CR/LF-injection / secret-redaction / metrics-auth / webhook-SSRF negative tests plus the legitimate-telemetry functional test.",
|
|
532
|
+
"retention_period": "1 year"
|
|
533
|
+
}
|
|
534
|
+
],
|
|
535
|
+
"regression_trigger": [
|
|
536
|
+
{
|
|
537
|
+
"condition": "A new sink, exporter, or metrics/debug endpoint is added",
|
|
538
|
+
"interval": "on change"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
"condition": "Periodic re-attestation of telemetry-pipeline integrity",
|
|
542
|
+
"interval": "quarterly"
|
|
543
|
+
}
|
|
544
|
+
]
|
|
545
|
+
},
|
|
546
|
+
"close": {
|
|
547
|
+
"evidence_package": {
|
|
548
|
+
"bundle_format": "csaf-2.0",
|
|
549
|
+
"contents": [
|
|
550
|
+
"telemetry-pipeline config snapshot",
|
|
551
|
+
"per-indicator findings + false-positive disposition",
|
|
552
|
+
"negative + functional test results",
|
|
553
|
+
"framework gap mapping"
|
|
554
|
+
],
|
|
555
|
+
"destination": ".exceptd/attestations/<session_id>/attestation.json"
|
|
556
|
+
},
|
|
557
|
+
"learning_loop": {
|
|
558
|
+
"enabled": true,
|
|
559
|
+
"lesson_template": {
|
|
560
|
+
"attack_vector": "Log forging via CR/LF injection, secret/PII leakage through un-redacted logs, internal-state disclosure via an unauthenticated metrics endpoint, or exfil/SSRF through an un-allowlisted exporter / webhook sink.",
|
|
561
|
+
"control_gap": "Telemetry pipeline lacks per-sink CR/LF neutralization, secret redaction, metrics auth, exporter allowlisting/secret-store credentials, or webhook SSRF guarding.",
|
|
562
|
+
"framework_gap": "NIST AU-9 + SI-11 + ISO A.8.15 cover log presence and store ACLs but not pipeline integrity/confidentiality.",
|
|
563
|
+
"new_control_requirement": "Every sink MUST neutralize CR/LF and redact secrets; metrics endpoints MUST be authenticated/private; exporters MUST be allowlisted with secret-store credentials over verified TLS."
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
"notification_actions": [
|
|
567
|
+
{
|
|
568
|
+
"obligation_ref": "EU/GDPR Art.33 72h",
|
|
569
|
+
"deadline": "72h from detect_confirmed",
|
|
570
|
+
"recipient": "data protection authority",
|
|
571
|
+
"evidence_attached": [
|
|
572
|
+
"attestation"
|
|
573
|
+
]
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
"obligation_ref": "EU/NIS2 Art.23 24h",
|
|
577
|
+
"deadline": "24h from detect_confirmed",
|
|
578
|
+
"recipient": "national CSIRT / competent authority",
|
|
579
|
+
"evidence_attached": [
|
|
580
|
+
"attestation"
|
|
581
|
+
]
|
|
582
|
+
}
|
|
583
|
+
],
|
|
584
|
+
"exception_generation": {
|
|
585
|
+
"trigger_condition": "A legacy sink cannot adopt neutralization/redaction immediately.",
|
|
586
|
+
"exception_template": {
|
|
587
|
+
"scope": "the specific sink / exporter",
|
|
588
|
+
"duration": "90 days",
|
|
589
|
+
"compensating_controls": [
|
|
590
|
+
"sanitize at the logging sidecar/collector",
|
|
591
|
+
"restrict the metrics endpoint at the network layer",
|
|
592
|
+
"rotate any credential observed in logs"
|
|
593
|
+
],
|
|
594
|
+
"risk_acceptance_owner": "ciso"
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
"regression_schedule": {
|
|
598
|
+
"next_run": "quarterly or on any new sink / exporter / metrics endpoint",
|
|
599
|
+
"trigger": "change to the telemetry pipeline, or quarterly re-attestation"
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
"directives": [
|
|
604
|
+
{
|
|
605
|
+
"id": "all-telemetry-sinks-and-exporters",
|
|
606
|
+
"title": "Inventory + integrity-test every log/metric/trace sink, exporter, and metrics endpoint",
|
|
607
|
+
"applies_to": {
|
|
608
|
+
"always": true
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
"id": "log-forging-and-exfil-sweep",
|
|
613
|
+
"title": "Targeted CR/LF-injection + secret-in-logs + exporter-SSRF sweep",
|
|
614
|
+
"applies_to": {
|
|
615
|
+
"attack_technique": "T1565.001"
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
]
|
|
619
|
+
}
|