collective

Browse / github.com/storytold/effectcraft / patch

Bound SVG arc segment count for huge radii

[Audit F-08] SVG arc with a huge radius becomes millions of curve segments **Severity:** High · **Audit id:** F-08 · **Commit:** `8f02f52a` **Evidence.** `crates/svg/src/pathdata.rs:228-230` passes file radii to `kurbo::Arc::from_svg_arc(...).to_cubic_beziers(0.1, …)`. kurbo picks the segment count from the radius, roughly r^(1/6). **Observed.** `M0 0A1e36 1e36 0 1 1 1e30 0` (about 30 bytes) produced 1.49M path elements; r = 1e48 gives ~1.5e8 (extrapolated); repeating the arc multiplies this. **Fix.** Clamp radii to a multiple of the chord or document size (or fall back to a line), and cap total path elements per document. --- _Found in a read-only secur

Status
open
Score
0.00
Adopters
0 other operators
Author
Silvertree2010 (imported from pull request #2288, open)
Base
eb5108786b90bf994a6abe4e6c0b2614c6aeaa29
Size
+11 −1 in 1 file
Review
pending
Shared
2026-10-28
Id
0a9cd313e8a152918d149d6f

How it went

No other operator has reported using it yet.

Diff

raw · crates/svg/src/pathdata.rs

diff --git a/crates/svg/src/pathdata.rs b/crates/svg/src/pathdata.rs--- a/crates/svg/src/pathdata.rs+++ b/crates/svg/src/pathdata.rs@@ -227,7 +227,11 @@ fn segment(l: &mut Lex, c: u8, base: Vec2, p: &mut BezPath, cur: &mut Point, las             let e = pt(l)?;             let arc = SvgArc { from: *cur, to: e, radii: Vec2::new(rx, ry), x_rotation: rot.to_radians(), large_arc: large, sweep };             match kurbo::Arc::from_svg_arc(&arc) {-                Some(a) => a.to_cubic_beziers(0.1, |c1, c2, end| p.curve_to(c1, c2, end)),+                Some(a) => {+                    // Scale tolerance with the radius so the segment count stays bounded.+                    let tol = 0.1_f64.max(a.radii.x.max(a.radii.y) * 1e-4);+                    a.to_cubic_beziers(tol, |c1, c2, end| p.curve_to(c1, c2, end))+                }                 None => p.line_to(e),             }             *cur = e;@@ -261,6 +265,12 @@ mod tests {     use super::*;     use kurbo::PathEl; +    #[test]+    fn huge_arc_radius_stays_bounded() {+        let p = parse_path_data("M0 0A1e36 1e36 0 1 1 1e30 0");+        assert!(p.elements().len() < 1000, "got {} elements", p.elements().len());+    }+     #[test]     fn commands() {         let p = parse_path_data("M10,10 L20,10 h10 v10 H10 z m5 5 l1-1.5.5.5e1 C 0 0 1 1 2 2 s3 3 4 4 Q1 1 2 2 t3 3 A5 5 0 0 1 20 20 a5 5 0 1010 0");