mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-10 21:11:52 +00:00
M12: an XFA template parses up to what a packet the reader accepts can come to as text
A UTF-16 packet within the reader's 64 MiB grows by up to half when it becomes UTF-8, so parse() now takes up to 96 MiB rather than 64: a packet read_packets accepts always parses. The refusal gives the size in bytes (review). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
This commit is contained in:
1 parent
0481b98cda
commit
d3fdc2f41c
3 files changed
+23
-16
No files matched your search
@@ -5,10 +5,14 @@ use pdfcraft_cos::{Document, Object};
|
||||
|
||||
use crate::XfaError;
|
||||
|
||||
/// Most decoded packet bytes read in total; a template handed to [`crate::parse`] directly is held
|
||||
/// to the same.
|
||||
/// Most decoded packet bytes read in total.
|
||||
pub(crate) const MAX_XDP: usize = 64 << 20;
|
||||
|
||||
/// Most bytes of a template [`crate::parse`] takes: the most a packet within [`MAX_XDP`] can come
|
||||
/// to as UTF-8 text. A UTF-16 packet grows by at most half going over (a two-byte unit becomes at
|
||||
/// most three bytes; a four-byte pair stays four), so a packet the reader accepts always parses.
|
||||
pub(crate) const MAX_TEMPLATE_TEXT: usize = MAX_XDP / 2 * 3;
|
||||
|
||||
/// What was found in the XFA entry.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct Packets {
|
||||
|
||||
@@ -969,15 +969,12 @@ fn collect_page_roles(nodes: &[Node], out: &mut Vec<(String, PageRole)>, depth:
|
||||
|
||||
/// Parse a template packet. Returns the template and the warnings met on the way.
|
||||
pub fn parse(xml: &str) -> Result<(Template, Vec<String>), XfaError> {
|
||||
// As read from a document ([`crate::read_packets`]): a template parsed directly is no bigger,
|
||||
// so what parsing keeps of it (item lists are held by the field and its properties) stays
|
||||
// within a few times that.
|
||||
if xml.len() > crate::packets::MAX_XDP {
|
||||
return Err(XfaError::TooLarge(format!(
|
||||
"the template is {} MiB, past the {} MiB the reader accepts",
|
||||
xml.len() >> 20,
|
||||
crate::packets::MAX_XDP >> 20
|
||||
)));
|
||||
// No bigger than a packet the reader accepts can come to as text ([`crate::read_packets`]), so
|
||||
// what parsing keeps of it (item lists are held by the field and its properties) stays within
|
||||
// a few times that, whoever calls it.
|
||||
let limit = crate::packets::MAX_TEMPLATE_TEXT;
|
||||
if xml.len() > limit {
|
||||
return Err(XfaError::TooLarge(format!("the template is {} bytes, past the {limit} bytes the reader accepts", xml.len())));
|
||||
}
|
||||
let depth = crate::xml_nesting_depth(xml);
|
||||
if depth > crate::MAX_XML_DEPTH {
|
||||
|
||||
@@ -222,15 +222,21 @@ fn a_caption_s_rich_text_and_a_list_s_items_are_kept() {
|
||||
assert_eq!(f.items.len(), 400, "the list shows the same entries");
|
||||
}
|
||||
|
||||
/// A template handed to `parse` directly is held to the bytes a document's packets may hold: one
|
||||
/// past them is refused before any of it is parsed, so the item lists a field and its properties
|
||||
/// keep of it can't grow past a few times that.
|
||||
/// A template handed to `parse` directly is held to what a document's packets may come to as text:
|
||||
/// one byte past it is refused before any of it is parsed (the error giving its size in bytes), and
|
||||
/// a template of exactly that size, as a UTF-16 packet the reader accepted can become, is parsed.
|
||||
#[test]
|
||||
fn a_template_past_the_packet_limit_is_refused_before_parsing() {
|
||||
let limit = crate::packets::MAX_XDP;
|
||||
let limit = crate::packets::MAX_TEMPLATE_TEXT;
|
||||
assert!(limit > crate::packets::MAX_XDP, "room for a UTF-16 packet grown into UTF-8");
|
||||
let head = r#"<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/"><subform name="form"><field name="f"><items><text>"#;
|
||||
let tail = "</text></items></field></subform></template>";
|
||||
let xml = format!("{head}{}{tail}", "a".repeat(limit + 1 - head.len() - tail.len()));
|
||||
assert_eq!(xml.len(), limit + 1);
|
||||
assert!(matches!(crate::parse(&xml), Err(crate::XfaError::TooLarge(_))));
|
||||
match crate::parse(&xml) {
|
||||
Err(crate::XfaError::TooLarge(m)) => assert!(m.contains(&format!("{} bytes", limit + 1)), "{m}"),
|
||||
other => panic!("{other:?}"),
|
||||
}
|
||||
let at = format!("{head}{}{tail}", "a".repeat(limit - head.len() - tail.len()));
|
||||
assert!(crate::parse(&at).is_ok(), "a template at the limit is parsed");
|
||||
}
|
||||
Reference in new issue
Block a user