Skip to content

Commit e89e480

Browse files
thewrzclaude
andauthored
fix(ui): harden responsive_columns against stride-canceling spacing (#189)
* fix(ui): harden responsive_columns against stride-canceling spacing Review follow-ups for the merged #187 responsive-grid work. - responsive_columns clamps the per-tile stride to a positive value. A caller passing a spacing that cancels MIN_TILE_CELL_W previously divided by zero into an inf-derived usize::MAX column count, so the helper reported `preferred` columns even when far fewer tiles fit. Pinned with a regression test, plus a test for the preferred_columns == 0 -> 1 floor that had no coverage. - deny.toml: mark the two unmaintained-crate advisory ignores with explicit drop-conditions, and record that RUSTSEC-2026-0192 (ttf-parser) is an unmaintained notice, not a memory-safety CVE (verified against the local RustSec advisory DB: informational = "unmaintained"). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style: apply cargo fmt to tile_layout Fixes the CI lint (cargo fmt --check) failure on this branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(deny): ignore quick-xml RUSTSEC-2026-0194/0195 pending upstream bump Both advisories are DoS-via-untrusted-XML in quick-xml < 0.41. The sole consumer is wayland-scanner, a build-time proc-macro parsing vendored Wayland protocol XML; no untrusted XML is parsed at runtime, so neither issue is reachable in the shipped binary. wayland-scanner 0.31.10 (latest) still pins quick-xml ^0.39 — drop these ignores when it moves to 0.41. Closes #192. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ddc122a commit e89e480

2 files changed

Lines changed: 27 additions & 5 deletions

File tree

deny.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ db-urls = ["https://github.com/rustsec/advisory-db"]
1313
ignore = [
1414
# paste is unmaintained; transitive dep via lofty. No fix available without
1515
# lofty dropping its paste dependency upstream.
16+
# TODO: drop this ignore once lofty releases a build free of paste.
1617
{ id = "RUSTSEC-2024-0436", reason = "transitive via lofty, no patched version available upstream" },
17-
# ttf-parser is unmaintained; transitive via iced's font stack. No safe
18-
# upgrade is available until iced/cosmic-text/winit move off it upstream.
18+
# ttf-parser is unmaintained (informational advisory, not a memory-safety
19+
# CVE); transitive via iced's font stack. No safe upgrade until
20+
# iced/cosmic-text move off it upstream.
21+
# TODO: drop this ignore once the iced font stack no longer pulls ttf-parser.
1922
{ id = "RUSTSEC-2026-0192", reason = "transitive via iced font dependencies, no patched version available upstream" },
2023
# quick-xml < 0.41 DoS via untrusted XML (quadratic attribute check /
2124
# unbounded namespace allocations). Sole consumer here is wayland-scanner,

src/ui/tile_layout.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ pub fn fitted_tile_size(slot: Size) -> Size {
3434
pub fn responsive_columns(available_width: f32, preferred_columns: usize, spacing: f32) -> usize {
3535
let preferred = preferred_columns.max(1);
3636
let usable_width = available_width.max(0.0);
37-
let columns_that_fit = ((usable_width + spacing) / (MIN_TILE_CELL_W + spacing))
38-
.floor()
39-
.max(1.0) as usize;
37+
// Clamp the per-tile stride to a positive value: a caller-supplied spacing
38+
// that cancels MIN_TILE_CELL_W would otherwise divide by zero into an
39+
// inf-derived (usize::MAX) column count.
40+
let stride = (MIN_TILE_CELL_W + spacing).max(1.0);
41+
let columns_that_fit = ((usable_width + spacing) / stride).floor().max(1.0) as usize;
4042

4143
preferred.min(columns_that_fit)
4244
}
@@ -161,4 +163,21 @@ mod tests {
161163
fn responsive_columns_never_exceeds_preferred_columns() {
162164
assert_eq!(responsive_columns(1128.0, 1, theme::space::LG), 1);
163165
}
166+
167+
#[test]
168+
fn responsive_columns_floors_zero_preferred_to_one() {
169+
assert_eq!(responsive_columns(1128.0, 0, theme::space::LG), 1);
170+
}
171+
172+
#[test]
173+
fn responsive_columns_does_not_overflow_on_stride_canceling_spacing() {
174+
// A pathological spacing that cancels the tile stride must not divide by
175+
// zero into an inf-derived usize::MAX column count: a 185px width fits
176+
// far fewer than the 8 preferred tiles, so it must report fewer.
177+
let cols = responsive_columns(185.0, 8, -MIN_TILE_CELL_W);
178+
assert!(
179+
cols < 8,
180+
"stride-canceling spacing inflated columns to {cols}"
181+
);
182+
}
164183
}

0 commit comments

Comments
 (0)