Skip to content

Commit ae403fa

Browse files
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7wpfleger96
andcommitted
fix(relay): fail-visible on DB error resolving approval-note channel
enforce_latched_approval_note swallowed every get_workflow error via .ok(), collapsing a transient DB error to None and skipping the latch check. A PgPool blip during channel resolution would let a plaintext approval note reach a latched DM — the leak this PR closes, reopened on the security boundary itself. Match enforce_latched_body's posture: NotFound passes (no resolvable channel, cannot be latched), any other DbError is fail-visible. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
1 parent 8e6dfaf commit ae403fa

1 file changed

Lines changed: 11 additions & 7 deletions

File tree

crates/buzz-relay/src/handlers/command_executor.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,22 @@ async fn enforce_latched_body(
120120
/// latched-body rule on the approval note. Approvals reference no `h` tag of
121121
/// their own — the channel is the workflow's (`get_workflow().channel_id`).
122122
/// A workflow with no channel (global) cannot be latched, so it passes through.
123+
///
124+
/// The `get_workflow` lookup honors the same fail-VISIBLE posture as
125+
/// [`enforce_latched_body`]: a missing workflow (`NotFound`) cannot be latched
126+
/// and passes, but a genuine DB error rejects rather than silently skipping the
127+
/// check — otherwise a transient pool blip would let a plaintext note slip into
128+
/// a latched channel.
123129
async fn enforce_latched_approval_note(
124130
state: &Arc<AppState>,
125131
content: &str,
126132
workflow_id: Uuid,
127133
) -> Result<(), IngestError> {
128-
let channel_id = state
129-
.db
130-
.get_workflow(workflow_id)
131-
.await
132-
.ok()
133-
.and_then(|w| w.channel_id);
134-
enforce_latched_body(state, content, channel_id).await
134+
match state.db.get_workflow(workflow_id).await {
135+
Ok(w) => enforce_latched_body(state, content, w.channel_id).await,
136+
Err(buzz_db::DbError::NotFound(_)) => Ok(()),
137+
Err(e) => Err(IngestError::Rejected(format!("error: database error: {e}"))),
138+
}
135139
}
136140

137141
/// Persist a command event inside a transaction. Returns the OPEN transaction

0 commit comments

Comments
 (0)