From 78cf3faf1cfee96a1dcdad6a5fb4b3d97be39c35 Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Tue, 7 Jul 2026 16:53:24 -0400 Subject: [PATCH] event redaction: document content-only vs exact-match entry points build_redacted_event_payload and redacted_event_json apply only the content-based pass; redacted_run_event additionally applies the per-run exact-match SecretRedactor. The names didn't signal that difference, so a caller holding a run redactor could silently pick a content-only path. Document each entry point and steer such callers to redacted_run_event. Also drop a no-op guard in the node_label branch: the redacted value was already computed, so the conditional assignment saved nothing. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/crates/fabro-workflow/src/event/redaction.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/crates/fabro-workflow/src/event/redaction.rs b/lib/crates/fabro-workflow/src/event/redaction.rs index 7565e64c8..23da35c81 100644 --- a/lib/crates/fabro-workflow/src/event/redaction.rs +++ b/lib/crates/fabro-workflow/src/event/redaction.rs @@ -5,6 +5,11 @@ use fabro_store::EventPayload; use fabro_util::json::normalize_json_value; use serde_json::Value; +/// Build a stored event payload with **content-based** redaction only. +/// +/// This does not apply the per-run exact-match [`SecretRedactor`]; on any +/// surface that has the run's redactor, use [`redacted_run_event`] instead so +/// declared low-entropy secrets are removed as well. pub fn build_redacted_event_payload(event: &RunEvent, run_id: &RunId) -> Result { EventPayload::new(redacted_event_value(event)?, run_id).map_err(anyhow::Error::from) } @@ -22,6 +27,11 @@ pub fn redacted_run_event(event: &RunEvent, redactor: &SecretRedactor) -> Result RunEvent::try_from(&payload).map_err(anyhow::Error::from) } +/// Serialize an event to JSON with **content-based** redaction only. +/// +/// Like [`build_redacted_event_payload`], this applies the content pass but not +/// the per-run exact-match [`SecretRedactor`]. Prefer [`redacted_run_event`] +/// wherever the run redactor is available. pub fn redacted_event_json(event: &RunEvent) -> Result { serde_json::to_string(&redacted_event_value(event)?).map_err(anyhow::Error::from) } @@ -42,10 +52,7 @@ fn redact_event_payload_secrets(value: &mut Value, redactor: &SecretRedactor) { redact_redactable_event_properties(properties, redactor); } if let Some(Value::String(label)) = value.get_mut("node_label") { - let redacted = redactor.redact_into(label); - if redacted != *label { - *label = redacted; - } + *label = redactor.redact_into(label); } }