mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Importers: a PDF dictionary value the reader can't parse no longer crashes the nesting check
The shared PDF reader (craft_pdf::syntax, craft-libs ca50d63b) indexes a dictionary by skipping its values. Its skip accepts some values that its parse refuses, such as a name with a broken # escape (/#zz) and a number such as 12-.5. Dict::entries unwraps the parse of each value (vendor/hayro-syntax/src/object/dict.rs:104). The check in pages.rs that refuses functions and color spaces built on one another without end (nests_without_end) calls entries on every dictionary of a file when the file opens. So document.pdfInfo, PDF import, clipboard.importPdf and loading a .ai panicked on such a file, and three import fuzz tests fail on main with PROPTEST_RNG_SEED=1182 and PROPTEST_CASES=20000. The check now reads a dictionary's values with keys and get_raw and leaves out a value that doesn't parse. A test in crates/pdf puts each of those values in a file with a plain function, which opens, and in a file with a function that stitches itself, which is still refused.
This commit is contained in:
@@ -46,6 +46,11 @@ const MAX_LINKS: usize = 16;
|
||||
/// The application's calculator-procedure depth policy, layered over the shared reader's limits.
|
||||
const MAX_PROCEDURES: usize = 64;
|
||||
|
||||
/// The values of `d` that the reader can parse (`Dict::entries` panics on one it can't).
|
||||
fn values<'a>(d: &Dict<'a>) -> impl Iterator<Item = MaybeRef<Object<'a>>> {
|
||||
d.keys().filter_map(|k| d.get_raw::<Object<'a>>(k.as_ref()))
|
||||
}
|
||||
|
||||
/// What the function or colour space `o` is built on: the functions a stitching function joins;
|
||||
/// the base, alternate and tint of a colour space; the alternate of a colour profile. Nothing for
|
||||
/// any other object.
|
||||
@@ -144,7 +149,7 @@ fn nests_without_end(pdf: &Pdf) -> bool {
|
||||
}
|
||||
match &o {
|
||||
Object::Array(a) => pending.extend(a.raw_iter().filter_map(direct)),
|
||||
_ => pending.extend(dict_or_stream(&o).into_iter().flat_map(|(d, _)| d.entries()).filter_map(|(_, v)| direct(v))),
|
||||
_ => pending.extend(dict_or_stream(&o).into_iter().flat_map(|(d, _)| values(d)).filter_map(direct)),
|
||||
}
|
||||
}
|
||||
false
|
||||
|
||||
@@ -796,3 +796,26 @@ fn functions_and_colour_spaces_built_on_themselves_are_refused() {
|
||||
assert!(d.layers.iter().any(|l| l.children().is_some_and(|c| !c.is_empty())), "{what}: its art is read");
|
||||
}
|
||||
}
|
||||
|
||||
/// A dictionary value the reader can't parse (a name with a broken `#` escape, a number with a
|
||||
/// stray `-`) is left out of the nesting check, which goes on with the rest of the file: a file
|
||||
/// whose shading has a plain function is read, and one whose function stitches itself is refused.
|
||||
#[test]
|
||||
fn dictionary_values_the_reader_cannot_parse_are_skipped() {
|
||||
use vectorcraft_testkit::pdf::{PdfPage, first_extra, pdf_with};
|
||||
let me = format!("{} 0 R", first_extra(1));
|
||||
let shaded = PdfPage {
|
||||
resources: format!("/Shading << /S << /ShadingType 2 /ColorSpace /DeviceRGB /Coords [0 0 100 0] /Function {me} >> >>"),
|
||||
..PdfPage::new(100.0, 100.0, "/S sh")
|
||||
};
|
||||
let ramp = "<< /FunctionType 2 /Domain [0 1] /C0 [1 0 0] /C1 [0 0 1] /N 1 >>";
|
||||
let stitch = format!("<< /FunctionType 3 /Domain [0 1] /Functions [{me}] /Bounds [] /Encode [0 1] >>");
|
||||
for junk in ["<< /A /#zz >>", "<< /A 12-.5 >>"] {
|
||||
let [plain, looped] = [ramp, stitch.as_str()].map(|f| pdf_with(std::slice::from_ref(&shaded), &[f, junk], None));
|
||||
assert_eq!(info(&plain, None).map(|i| i.pages.len()).ok(), Some(1), "{junk}");
|
||||
let d = import(&plain).unwrap_or_else(|e| panic!("{junk}: {e}"));
|
||||
assert!(d.layers.iter().any(|l| l.children().is_some_and(|c| !c.is_empty())), "{junk}: its art is read");
|
||||
let e = import(&looped).err().map(|e| e.to_string());
|
||||
assert!(e.as_deref().is_some_and(|e| e.contains("built on one another without end")), "{junk}: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user