From ce640e1cc4ca8b6593ec843ec2f50be69f8b2025 Mon Sep 17 00:00:00 2001 From: Ibrahim Rahhal Date: Mon, 17 Aug 2026 14:02:13 +0300 Subject: [PATCH] Raise the blocking-rules poll timeout to outlast reachability triage SCA reachability blocking rules make doghouse answer `pending` for up to 30 minutes while Fusion triages a scan's direct dependencies. This side fails closed on its own deadline, so at the previous 15 minutes whichever clock expired first decided the outcome and a pipeline could hard-fail on a rule doghouse was about to resolve as non-blocking. 35 minutes leaves margin over that window for the poll interval and request latency, so doghouse always resolves first. Adds an assertion tying the two together, since the constants live in separate repos and nothing else would catch them drifting. Co-authored-by: Cursor --- src/scanners/blast.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/scanners/blast.rs b/src/scanners/blast.rs index 9841ba2..8cd7baa 100644 --- a/src/scanners/blast.rs +++ b/src/scanners/blast.rs @@ -1057,8 +1057,12 @@ pub fn wait_for_scan(config: &Config, scan_id: &str, budget: WaitBudget) { } } -/// Match doghouse `LICENSE_DEPS_WAIT_TIMEOUT` (15 minutes). -const DEFAULT_BLOCKING_RULES_TIMEOUT: Duration = Duration::from_secs(15 * 60); +/// Must outlast the longest doghouse wait window, currently +/// `SCA_REACHABILITY_WAIT_TIMEOUT` (30 minutes), with margin for the poll +/// interval and request latency. Whichever side's clock expires first decides +/// the outcome, and this side fails closed: expiring early would hard-fail a +/// pipeline on data doghouse was about to resolve as non-blocking. +const DEFAULT_BLOCKING_RULES_TIMEOUT: Duration = Duration::from_secs(35 * 60); const BLOCKING_RULES_POLL_INTERVAL: Duration = Duration::from_secs(2); fn stop_blocking_rules_spinner(stop_signal: &Arc>, spinner: thread::JoinHandle<()>) { @@ -1122,7 +1126,7 @@ fn decide_blocking_rules_poll( } /// Poll until blocking-rules status is `complete`, or `BLOCKING_RULES_TIMEOUT_ENV` -/// (15m by default) runs out. +/// (35m by default) runs out. /// Older backends omit status (serde defaults to complete: one-shot). /// `block_on` is forwarded as the CI rule-slug filter (`--block-on`); `None` /// keeps legacy `--fail` "all active rules" behavior. @@ -1732,12 +1736,26 @@ mod tests { // The docs promise these two numbers; drifting from them silently is // the failure mode worth catching. assert_eq!(DEFAULT_SCAN_TIMEOUT, Duration::from_secs(10 * 60 * 60)); - assert_eq!(DEFAULT_BLOCKING_RULES_TIMEOUT, Duration::from_secs(15 * 60)); + assert_eq!(DEFAULT_BLOCKING_RULES_TIMEOUT, Duration::from_secs(35 * 60)); assert_eq!(format_timeout(DEFAULT_SCAN_TIMEOUT), "10h"); - assert_eq!(format_timeout(DEFAULT_BLOCKING_RULES_TIMEOUT), "15m"); + assert_eq!(format_timeout(DEFAULT_BLOCKING_RULES_TIMEOUT), "35m"); assert_eq!(format_timeout(Duration::from_secs(90)), "90s"); } + #[test] + fn blocking_rules_timeout_outlasts_the_doghouse_wait_windows() { + // This side fails closed on its own deadline, so it must never expire + // while doghouse is still answering `pending`. The longest doghouse + // window is SCA_REACHABILITY_WAIT_TIMEOUT at 30 minutes. + let longest_doghouse_window = Duration::from_secs(30 * 60); + assert!( + DEFAULT_BLOCKING_RULES_TIMEOUT > longest_doghouse_window, + "poll deadline {DEFAULT_BLOCKING_RULES_TIMEOUT:?} must outlast the \ + doghouse wait window {longest_doghouse_window:?}, or a pipeline \ + hard-fails on a rule doghouse was about to resolve" + ); + } + #[test] fn budget_reports_what_is_left_and_then_nothing() { let spent = WaitBudget::with_timeout(Duration::ZERO);