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
@@ -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");