From 10e173c0e49696ad335e3da8a069889c089b09a6 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:00:16 +0100 Subject: [PATCH] Skip whole CSS at-rule blocks in the SVG parser by brace depth Found while testing crates/svg/src/lib.rs:510-520 (parse_css) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/svg/src/lib.rs | 24 +++++++++++++++++++++--- crates/svg/src/tests.rs | 12 ++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/svg/src/lib.rs b/crates/svg/src/lib.rs index fe686dd6b..d2720bb0d 100644 --- a/crates/svg/src/lib.rs +++ b/crates/svg/src/lib.rs @@ -510,12 +510,30 @@ fn parse_css(css: &str, sheet: &mut StyleSheet) -> Result<(), Error> { let mut rest = css.as_str(); while let Some(open) = rest.find('{') { let sel = rest[..open].trim(); + if sel.starts_with('@') { + // Skip the whole at-rule block, matching nested braces by depth. + let mut depth = 0usize; + let mut end = None; + for (i, c) in rest[open..].char_indices() { + match c { + '{' => depth = depth.saturating_add(1), + '}' => { + depth = depth.saturating_sub(1); + if depth == 0 { + end = Some(open + i + 1); + break; + } + } + _ => {} + } + } + let Some(end) = end else { break }; + rest = &rest[end..]; + continue; + } let Some(close) = rest[open..].find('}') else { break }; let body = &rest[open + 1..open + close]; rest = &rest[open + close + 1..]; - if sel.starts_with('@') { - continue; - } let mut decls = parse_decls(body); sheet.decls = sheet.decls.saturating_add(decls.len()); if sheet.decls > MAX_CSS_DECLARATIONS { diff --git a/crates/svg/src/tests.rs b/crates/svg/src/tests.rs index 466c9053e..31c95e62f 100644 --- a/crates/svg/src/tests.rs +++ b/crates/svg/src/tests.rs @@ -422,3 +422,15 @@ fn font_size_percentage_resolves_against_the_parent_font_size() { assert!(matches!(shapes[0].2.geom, Geom::Rect { w, .. } if w == expected), "{body}: {:?}", shapes[0].2.geom); } } + +#[test] +fn at_rule_blocks_are_skipped_by_brace_depth() { + let mut sheet = StyleSheet::default(); + parse_css("@media print { .a{fill:#ff0000} .b{fill:#0000ff} } .c{fill:red}", &mut sheet).unwrap(); + assert_eq!(sheet.rules.len(), 1); + assert_eq!(sheet.rules[0].classes, vec!["c".to_string()]); + let mut sheet = StyleSheet::default(); + parse_css("@media x { @supports y { .c{} } .d{} } .e{fill:red}", &mut sheet).unwrap(); + assert_eq!(sheet.rules.len(), 1); + assert_eq!(sheet.rules[0].classes, vec!["e".to_string()]); +}