mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-29 04:28:20 -06:00
fix(preflight): decode an output-intent profile within 16 MiB
Reading the colour space from an ICC header when /N is missing decoded the whole profile under the general 1 GiB stream limit to look at four bytes. The profile now decodes within 16 MiB; a larger one leaves the component count unknown.
This commit is contained in:
@@ -214,6 +214,9 @@ fn has_pdfa_intent(doc: &Document) -> bool {
|
||||
pdfa_intent_profile(doc).is_some()
|
||||
}
|
||||
|
||||
/// Largest output-intent profile decoded to read its header; real ICC profiles stay well under it.
|
||||
const MAX_ICC_PROFILE: usize = 16 << 20;
|
||||
|
||||
/// The PDF/A output intent's destination profile: `Some(n)` with the profile's number of colour
|
||||
/// components (`/N`, else the ICC header's data colour space; `None` when neither says), or
|
||||
/// `None` when the document has no PDF/A output intent.
|
||||
@@ -229,7 +232,7 @@ fn pdfa_intent_profile(doc: &Document) -> Option<Option<i64>> {
|
||||
let Object::Stream(s) = &*profile else { return Some(None) };
|
||||
Some(s.dict.int(b"N").or_else(|| {
|
||||
// ICC.1 header: the data colour space is bytes 16–19.
|
||||
match s.decoded().ok()?.get(16..20)? {
|
||||
match s.decoded_within(MAX_ICC_PROFILE).ok()?.get(16..20)? {
|
||||
b"GRAY" => Some(1),
|
||||
b"RGB " => Some(3),
|
||||
b"CMYK" => Some(4),
|
||||
|
||||
@@ -417,3 +417,15 @@ fn oversized_xmp_is_refused_while_decoding() {
|
||||
assert_eq!(stream.decoded().unwrap().len(), xmp::MAX_XMP_BYTES + 1, "fixture must actually decompress");
|
||||
assert!(metadata(&doc).is_none(), "compressed metadata must be limited during decoding");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_output_intent_profile_header_is_read_within_a_size_cap() {
|
||||
let intent = "/OutputIntents [<< /S /GTS_PDFA1 /DestOutputProfile 2 0 R >>]";
|
||||
for (size, n) in [(128, Some(4)), (MAX_ICC_PROFILE + 1, None)] {
|
||||
let mut doc = build(&[&format!("<< /Type /Catalog {intent} >>"), "null"], "");
|
||||
let mut profile = vec![0; size];
|
||||
profile[16..20].copy_from_slice(b"CMYK");
|
||||
doc.set(pdfcraft_cos::ObjRef::new(2, 0), Object::Stream(pdfcraft_cos::Stream::flate(Dict::new(), &profile)));
|
||||
assert_eq!(pdfa_intent_profile(&doc), Some(n), "a {size}-byte profile");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user