Files
cadcraft/crates/io/tests/svg_arc_budget.rs
T
Dylan M. Taylorandskylartaylor 8085745a22 security: bound native DXF/SVG/PDF import allocation and quadratic work (CWE-400/407/789) (#1846)
* security: bound native DXF/SVG/PDF import allocation and quadratic work (CWE-400/407/789)

Several native import paths let a crafted drawing cause quadratic CPU work or
unbounded allocation:

- DXF layer decoding scanned the whole layer list for every LAYER record
  (`d.layer_mut`), so many distinct layer names made import quadratic. Keep a
  case-normalised name→index map and update/append in O(1) (row 29).
- ACAD_TABLE cells were materialised per table under a per-table cap, but many
  small tables could still add up. Add a document-wide table-cell budget in
  parse_entities; a table that would overflow it is reduced to empty (row 8).
- DXF image name allocation restarted its `_2, _3…` suffix probe for every
  definition sharing a base name (quadratic). Keep a per-base next-suffix
  counter so probing resumes where it left off (row 13).
- Image resolution cloned each definition's path/name into every referring
  image. Add an aggregate byte budget; once spent, string copies stop while
  geometry fields are still applied (row 42).
- PDF link annotation scanned the whole selected-link vector for every
  primitive. Build a HashSet of linked handles once for O(1) membership (row 15).
- SVG arc/curve flattening retained each flushed run with only a per-run cap, so
  repeated arcs amplified a compact path into unbounded geometry. Add a
  document-wide generated-vertex budget charged in `add` (row 31).

New test `svg_arc_budget` covers the SVG budget. All `cadcraft-io` tests pass;
clippy adds no new warnings.

Addresses findings rows 8, 13, 15, 29, 31, 42.

* fix: share DXF table budgets before allocation

---------

Co-authored-by: skylartaylor <26588250+skylartaylor@users.noreply.github.com>
2026-10-22 17:35:43 -04:00

33 lines
1.5 KiB
Rust

//! Reading an SVG whose paths flatten into a huge number of retained vertices must be rejected by
//! the document-wide generated-geometry budget, not amplified into gigabytes (CWE-400, row 31).
use cadcraft_io::read;
/// A path with many elliptical-arc subpaths under a non-uniform group transform; each subpath
/// flushes to its own retained polyline, so enough of them must exceed the document vertex budget.
fn arc_bomb(arcs: usize) -> Vec<u8> {
// `scale(1 2)` makes the transform non-uniform, forcing per-sample flattening rather than a
// single bulge arc. Each `M … a …` is a separate subpath that is flushed and retained.
let mut d = String::new();
for i in 0..arcs {
let y = i as i32;
d.push_str(&format!("M 0 {y} a 1000 500 0 1 1 1 1 "));
}
let svg = format!(r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g transform="scale(1 2)"><path d="{d}"/></g></svg>"#);
svg.into_bytes()
}
#[test]
fn a_modest_arc_svg_still_reads() {
// A handful of arcs is well within budget and parses fine.
let d = read(&arc_bomb(4), "a.svg").expect("a small arc SVG should read");
assert!(d.model.iter().count() >= 1, "expected some geometry");
}
#[test]
fn an_arc_heavy_svg_is_rejected_by_the_vertex_budget() {
// ~1000 arcs x up to 10_000 chords each would far exceed the 8M vertex budget.
let err = read(&arc_bomb(30000), "a.svg");
assert!(err.unwrap_err().to_string().contains("SVG: too much generated geometry"));
}