From 3f7fba66d4bb4eb3e2f5acff1c3a819622f0e14d Mon Sep 17 00:00:00 2001 From: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz> Date: Wed, 14 Oct 2026 11:21:56 -0400 Subject: [PATCH] buzz-db: reject weakened single-table guards in source policy A guard now admits a statement only as a plain conjunct of its WHERE (no OR outside the frozen-or-equal groups, no subquery), and every destructive_storage_* assignment must preserve a frozen value with COALESCE. Signed-off-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz> --- crates/buzz-db/tests/observability_source.rs | 59 +++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/crates/buzz-db/tests/observability_source.rs b/crates/buzz-db/tests/observability_source.rs index e74ec4dfa6..b4655a8a81 100644 --- a/crates/buzz-db/tests/observability_source.rs +++ b/crates/buzz-db/tests/observability_source.rs @@ -1988,6 +1988,19 @@ fn sql_update_sets<'a>(source: &'a str, table: &str) -> Vec<(&'a str, &'a str)> .collect() } +const DESTRUCTIVE_FROZEN_OR_EQUAL: &str = + "destructive_storage_frozen_at is null or destructive_storage_manifest ="; + +/// A guard admits a statement only as a plain conjunct of its own WHERE: no +/// `OR` outside the known frozen-or-equal groups, and no subquery. +fn plain_guard(predicate: &str, guard: &str) -> bool { + let rest = predicate.replace(DESTRUCTIVE_FROZEN_OR_EQUAL, "").replace( + "destructive_storage_manifest is null or destructive_storage_manifest =", + "", + ); + predicate.contains(guard) && !rest.contains(" or ") && !rest.contains("select ") +} + fn sql_assigned_columns(set: &str) -> Vec<&str> { set.match_indices('=') .flat_map(|(at, _)| -> Vec<&str> { @@ -2088,7 +2101,7 @@ fn single_table_guard_violations( rule_hits[3].push(name.to_owned()); if community_sets .iter() - .any(|(_, predicate)| !predicate.contains("deletion_state = 'active'")) + .any(|(_, predicate)| !plain_guard(predicate, "deletion_state = 'active'")) { reject("community UPDATE requires an active predicate in its statement"); } @@ -2148,7 +2161,7 @@ fn single_table_guard_violations( .contains(column) }) { freezes_inventory = true; - if !predicate.contains("inventory_frozen_at is null") { + if !plain_guard(predicate, "inventory_frozen_at is null") { reject( "request inventory UPDATE requires inventory_frozen_at IS NULL in WHERE", ); @@ -2159,11 +2172,16 @@ fn single_table_guard_violations( .any(|column| column.starts_with("destructive_storage_")) { freezes_destructive_storage = true; - if !predicate.contains( - "destructive_storage_frozen_at is null or destructive_storage_manifest =", - ) { + if !plain_guard(predicate, DESTRUCTIVE_FROZEN_OR_EQUAL) { reject("request destructive storage UPDATE requires the frozen-or-equal guard in WHERE"); } + if columns + .iter() + .filter(|column| column.starts_with("destructive_storage_")) + .any(|column| !set.contains(&format!("{column} = coalesce({column},"))) + { + reject("request destructive storage UPDATE must preserve a frozen value with COALESCE"); + } } } if freezes_inventory || freezes_destructive_storage { @@ -2257,6 +2275,25 @@ fn single_table_policy_requires_active_community_updates() { ); } +#[test] +fn single_table_policy_rejects_weakened_guards() { + for sql in [ + "UPDATE communities SET icon = NULL WHERE id = $1 AND (deletion_state = 'active' OR true)", + "UPDATE communities SET icon = NULL WHERE id = $1 OR deletion_state = 'active'", + "UPDATE communities SET icon = NULL WHERE id IN (SELECT id FROM communities WHERE deletion_state = 'active')", + "UPDATE community_deletion_requests SET inventory_digest = NULL WHERE id = $1 AND (inventory_frozen_at IS NULL OR true)", + "UPDATE community_deletion_requests SET destructive_storage_manifest = COALESCE(destructive_storage_manifest, $2) WHERE id = $1 AND (destructive_storage_frozen_at IS NULL OR destructive_storage_manifest = $2 OR true)", + ] { + let source = format!("async fn bypass() {{ query(\"{sql}\"); }}"); + assert_eq!( + single_table_guard_violations(DELETION_STORE_PATH, &source, &mut Default::default()) + .len(), + 1, + "{sql}" + ); + } +} + #[test] fn single_table_policy_requires_inventory_freeze_predicate() { for column in [ @@ -2291,7 +2328,7 @@ fn single_table_policy_requires_destructive_freeze_predicate() { "destructive_storage_manifest", "destructive_storage_frozen_at", ] { - let source = format!("async fn bypass() {{ query(\"UPDATE community_deletion_requests SET {column} = NULL WHERE id = $1\"); }}"); + let source = format!("async fn bypass() {{ query(\"UPDATE community_deletion_requests SET {column} = COALESCE({column}, $2) WHERE id = $1\"); }}"); assert_eq!( single_table_guard_violations(DELETION_STORE_PATH, &source, &mut Default::default()) .len(), @@ -2304,6 +2341,14 @@ fn single_table_policy_requires_destructive_freeze_predicate() { .is_empty(), "{column}" ); + // The guard keeps an equal manifest admissible, so SET must not overwrite it. + let overwrite = guarded.replace(&format!("COALESCE({column}, $2)"), "NULL"); + let violations = + single_table_guard_violations(DELETION_STORE_PATH, &overwrite, &mut Default::default()); + assert!( + violations.len() == 1 && violations[0].contains("preserve a frozen value"), + "{column}: {violations:?}" + ); } } @@ -2323,7 +2368,7 @@ fn single_table_policy_checks_conflict_updates() { let source = r#"async fn guarded() { query("INSERT INTO communities (host) VALUES ($1) ON CONFLICT (host) DO UPDATE SET host = communities.host WHERE communities.deletion_state = 'active'"); query("INSERT INTO community_deletion_requests (id) VALUES ($1) ON CONFLICT (id) DO UPDATE SET inventory_digest = $2 WHERE inventory_frozen_at IS NULL"); - query("INSERT INTO community_deletion_requests (id) VALUES ($1) ON CONFLICT (id) DO UPDATE SET destructive_storage_manifest = $2 WHERE destructive_storage_frozen_at IS NULL OR destructive_storage_manifest = $2"); + query("INSERT INTO community_deletion_requests (id) VALUES ($1) ON CONFLICT (id) DO UPDATE SET destructive_storage_manifest = COALESCE(destructive_storage_manifest, $2) WHERE destructive_storage_frozen_at IS NULL OR destructive_storage_manifest = $2"); }"#; assert!( single_table_guard_violations(DELETION_STORE_PATH, source, &mut Default::default())