Skip to main content

vole_document/
limits.rs

1//! Centralized resource bounds.
2//!
3//! Every decode and encode path takes a [`Limits`]. Untrusted descriptors must
4//! be rejected *before* catastrophic work is performed, so all arithmetic on
5//! declared lengths and offsets is checked against these bounds and uses
6//! checked integer operations.
7
8/// Hard upper bounds applied while parsing and materializing a descriptor.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct Limits {
11    /// Maximum accepted source/descriptor input size.
12    pub max_input_bytes: u64,
13    /// Maximum reconstructed output size for a single materialization.
14    pub max_output_bytes: u64,
15    /// Admission cap on the output of a single `DEFLATE_REPLAY`.
16    ///
17    /// This is a **VOLE replay-profile policy limit**, not an RFC 1951 maximum.
18    /// RFC 1951 permits arbitrarily many empty non-final stored blocks, so it
19    /// gives no finite `f(decompressed_size)` bound on `compressed_size`; a
20    /// bitstream that inflates to zero bytes may be arbitrarily large. VOLE
21    /// therefore declines to replay a descriptor whose declared output exceeds
22    /// this policy cap (see ADR-0016).
23    pub max_replay_bytes: u64,
24    /// Maximum length of a single record payload.
25    pub max_record_len: u32,
26    /// Maximum number of records in a container.
27    pub max_record_count: u32,
28    /// Maximum number of distinct byte objects (`OBJECT` records).
29    pub max_object_count: u32,
30    /// Maximum number of DRA instructions in a reconstruction graph.
31    pub max_graph_ops: u32,
32    /// Maximum repeat count for a single `REPEAT_LAST` instruction.
33    pub max_repeat_count: u64,
34    /// Maximum number of symbols in a single entropy channel.
35    pub max_channel_symbols: u64,
36    /// Maximum number of distinct entropy models (`MODEL` records).
37    pub max_model_count: u32,
38    /// Maximum number of entropy channels (`ENTROPY_CHANNEL` records).
39    pub max_channel_count: u32,
40    /// Maximum encoded size of a single entropy model payload.
41    pub max_entropy_model_bytes: u32,
42    /// Maximum number of lexical spans produced for a PDF input.
43    pub max_pdf_spans: u32,
44    /// Maximum number of selectors in a single `OBSERVATION_INDEX` record.
45    ///
46    /// Bounds the admissions of the optional partial-decode index (Phase 7.3)
47    /// before allocation: an index whose selector table would exceed this is
48    /// rejected at parse and declined by the index builder. It mirrors the
49    /// object/graph scale so the table cannot dwarf the document it describes.
50    pub max_index_selectors: u32,
51    /// Maximum accepted size of an optional `DIRECTORY` record payload.
52    ///
53    /// Bounds the seek directory (Phase 8) before allocation: a directory larger
54    /// than this is declined at decode rather than trusted. A directory is roughly
55    /// `13 * record_count` bytes, so this also caps the record count a directory
56    /// can describe.
57    pub max_directory_bytes: u32,
58    /// Maximum accepted size of an optional `CHECKPOINT` record payload.
59    ///
60    /// Bounds the byte-level partial-materialization checkpoint (Phase 13.4)
61    /// before allocation: a checkpoint larger than this is declined at decode
62    /// rather than trusted. A checkpoint is `20 + 16 * op_count` bytes, so this
63    /// also caps the op count a checkpoint can describe.
64    pub max_checkpoint_bytes: u32,
65    // The ZIP caps below mirror the threat model and DEFAULT/STRICT values frozen
66    // in `research/subagents/phase-12/I-security.md` §6 (threat ids Z1–Z15), as
67    // required by plan §DEC-3/§DEC-9.
68    /// Maximum number of ZIP members accepted in one archive (Phase 12, Z2).
69    pub max_zip_members: u32,
70    /// Maximum declared compressed size of a single ZIP member (Phase 12, Z1).
71    pub max_zip_member_compressed: u64,
72    /// Maximum declared uncompressed size of a single ZIP member (Phase 12, Z1).
73    pub max_zip_member_uncompressed: u64,
74    /// Maximum sum of declared uncompressed sizes across all members (Z2).
75    pub max_zip_aggregate_uncompressed: u64,
76    /// Maximum declared uncompressed/compressed ratio for one member (Z1).
77    pub max_zip_compression_ratio: u32,
78    /// Maximum raw name byte length of one member (Phase 12, Z13/Z14).
79    pub max_zip_name_bytes: u32,
80    /// Maximum raw extra-field byte length of one member (Z7/Z14).
81    pub max_zip_extra_bytes: u32,
82    /// Maximum per-entry comment byte length (Z14).
83    pub max_zip_entry_comment_bytes: u32,
84    /// Maximum archive comment byte length (Z14/Z15).
85    pub max_zip_archive_comment_bytes: u32,
86    /// Maximum central-directory byte length (Z14).
87    pub max_zip_central_dir_bytes: u64,
88    /// Maximum leading bytes before the first local header (Z14).
89    pub max_zip_prefix_bytes: u64,
90    /// Maximum trailing bytes after the EOCD record (Z14).
91    pub max_zip_trailing_bytes: u64,
92    // The XML/OPC caps below mirror the threat model and DEFAULT/STRICT values
93    // frozen in `research/subagents/phase-12/I-security.md` §6 (§2 XML, §3 OPC),
94    // as required by plan §DEC-4/§DEC-9. XML is derived (`Q_gen`) state only.
95    /// Maximum XML element nesting depth before a typed decline (Phase 12, §2).
96    pub max_xml_depth: u32,
97    /// Maximum decoded byte length of a single XML part (Phase 12, §2).
98    pub max_xml_part_bytes: u64,
99    /// Maximum number of XML pull events in a single part (Phase 12, §2).
100    pub max_xml_events: u64,
101    /// Maximum number of XML element nodes in a single part (Phase 12, §2).
102    pub max_xml_nodes: u64,
103    /// Maximum number of attributes on a single XML element (Phase 12, §2).
104    pub max_xml_attrs_per_element: u32,
105    /// Maximum total text bytes accepted across a single XML part (Phase 12, §2).
106    pub max_xml_text_bytes: u64,
107    /// Maximum number of `<!DOCTYPE` declarations accepted (Phase 12: always 0).
108    pub max_xml_doctype: u32,
109    /// Maximum relationships across all `.rels` parts (Phase 12, §3).
110    pub max_opc_rels: u32,
111    /// Maximum internal relationship traversal depth (Phase 12 cycles, §3).
112    pub max_opc_rel_depth: u32,
113    /// Maximum `Default`+`Override` entries in `[Content_Types].xml` (Phase 12, §3).
114    pub max_opc_content_types_overrides: u32,
115    /// Maximum byte length of an OPC part name (Phase 12, §3).
116    pub max_opc_part_name_bytes: u32,
117    // The EPUB/OCF caps below mirror the threat model and DEFAULT/STRICT values
118    // frozen in `research/subagents/phase-12/I-security.md` §6 (§4 EPUB), as
119    // required by plan §DEC-5/§DEC-9. EPUB semantics are derived (`Q_gen`) only.
120    /// Maximum `rootfile` entries accepted in `META-INF/container.xml` (Phase 12, §4).
121    pub max_epub_rootfiles: u32,
122    /// Maximum Package Document manifest items accepted (Phase 12, §4).
123    pub max_epub_manifest_items: u32,
124    /// Maximum Package Document spine `itemref`s accepted (Phase 12, §4).
125    pub max_epub_spine_items: u32,
126    /// Maximum navigation-document nesting depth accepted (Phase 12, §4).
127    pub max_epub_nav_depth: u32,
128    /// Maximum manifest `fallback` chain length followed (Phase 12, §4).
129    pub max_epub_fallback_chain: u32,
130    /// Maximum XHTML element nodes accepted in one content/nav document (Phase 12, §4).
131    pub max_xhtml_nodes: u32,
132    // The ODT/ODF caps below mirror the EPUB caps above (Phase 13.3 applies the same
133    // bounded-XML policy to the OpenDocument content model). ODT semantics are derived
134    // (`Q_gen`) only.
135    /// Maximum `file-entry` elements accepted in `META-INF/manifest.xml` (Phase 13.3).
136    pub max_odt_manifest_entries: u32,
137    /// Maximum block elements accepted in one OpenDocument content part (Phase 13.3).
138    pub max_odt_blocks: u32,
139    /// Maximum notes accepted in one OpenDocument content part (Phase 13.3).
140    pub max_odt_notes: u32,
141}
142
143impl Limits {
144    /// The default archival limits: generous, but always finite.
145    pub const DEFAULT: Limits = Limits {
146        max_input_bytes: 1 << 40,  // 1 TiB
147        max_output_bytes: 1 << 40, // 1 TiB
148        max_replay_bytes: 1 << 34, // 16 GiB
149        max_record_len: 1 << 31,   // 2 GiB
150        max_record_count: 1 << 20, // ~1M records
151        max_object_count: 1 << 20,
152        max_graph_ops: 1 << 20,
153        max_repeat_count: 1 << 32,
154        max_channel_symbols: 1 << 40,
155        max_model_count: 1 << 16,
156        max_channel_count: 1 << 16,
157        max_entropy_model_bytes: 4096,
158        max_pdf_spans: 1 << 26,
159        max_index_selectors: 1 << 20,
160        max_directory_bytes: 1 << 20,
161        max_checkpoint_bytes: 1 << 20,
162        max_zip_members: 1 << 20,
163        max_zip_member_compressed: 1 << 34,
164        max_zip_member_uncompressed: 1 << 34,
165        max_zip_aggregate_uncompressed: 1 << 36,
166        max_zip_compression_ratio: 1024,
167        max_zip_name_bytes: 1 << 16,
168        max_zip_extra_bytes: 1 << 16,
169        max_zip_entry_comment_bytes: 1 << 16,
170        max_zip_archive_comment_bytes: 1 << 16,
171        max_zip_central_dir_bytes: 1 << 28,
172        max_zip_prefix_bytes: 1 << 20,
173        max_zip_trailing_bytes: 1 << 20,
174        max_xml_depth: 256,
175        max_xml_part_bytes: 1 << 28,
176        max_xml_events: 1 << 24,
177        max_xml_nodes: 1 << 24,
178        max_xml_attrs_per_element: 4096,
179        max_xml_text_bytes: 1 << 28,
180        max_xml_doctype: 0,
181        max_opc_rels: 1 << 20,
182        max_opc_rel_depth: 64,
183        max_opc_content_types_overrides: 1 << 20,
184        max_opc_part_name_bytes: 1 << 16,
185        max_epub_rootfiles: 16,
186        max_epub_manifest_items: 1 << 20,
187        max_epub_spine_items: 1 << 20,
188        max_epub_nav_depth: 64,
189        max_epub_fallback_chain: 32,
190        max_xhtml_nodes: 1 << 24,
191        max_odt_manifest_entries: 1 << 20,
192        max_odt_blocks: 1 << 20,
193        max_odt_notes: 1 << 20,
194    };
195
196    /// Tight limits for hostile-input testing and fuzzing.
197    pub const STRICT: Limits = Limits {
198        max_input_bytes: 1 << 26,  // 64 MiB
199        max_output_bytes: 1 << 26, // 64 MiB
200        max_replay_bytes: 1 << 26, // 64 MiB
201        max_record_len: 1 << 24,   // 16 MiB
202        max_record_count: 1 << 16, // 65536
203        max_object_count: 1 << 16,
204        max_graph_ops: 1 << 16,
205        max_repeat_count: 1 << 24,
206        max_channel_symbols: 1 << 26,
207        max_model_count: 1 << 12,
208        max_channel_count: 1 << 12,
209        max_entropy_model_bytes: 4096,
210        max_pdf_spans: 1 << 16,
211        max_index_selectors: 1 << 16,
212        max_directory_bytes: 1 << 18,
213        max_checkpoint_bytes: 1 << 18,
214        max_zip_members: 1 << 16,
215        max_zip_member_compressed: 1 << 26,
216        max_zip_member_uncompressed: 1 << 26,
217        max_zip_aggregate_uncompressed: 1 << 27,
218        max_zip_compression_ratio: 256,
219        max_zip_name_bytes: 4096,
220        max_zip_extra_bytes: 4096,
221        max_zip_entry_comment_bytes: 4096,
222        max_zip_archive_comment_bytes: 4096,
223        max_zip_central_dir_bytes: 1 << 20,
224        max_zip_prefix_bytes: 1 << 16,
225        max_zip_trailing_bytes: 1 << 16,
226        max_xml_depth: 64,
227        max_xml_part_bytes: 1 << 20,
228        max_xml_events: 1 << 16,
229        max_xml_nodes: 1 << 16,
230        max_xml_attrs_per_element: 256,
231        max_xml_text_bytes: 1 << 20,
232        max_xml_doctype: 0,
233        max_opc_rels: 1 << 14,
234        max_opc_rel_depth: 16,
235        max_opc_content_types_overrides: 1 << 12,
236        max_opc_part_name_bytes: 4096,
237        max_epub_rootfiles: 4,
238        max_epub_manifest_items: 1 << 14,
239        max_epub_spine_items: 1 << 14,
240        max_epub_nav_depth: 16,
241        max_epub_fallback_chain: 8,
242        max_xhtml_nodes: 1 << 16,
243        max_odt_manifest_entries: 1 << 14,
244        max_odt_blocks: 1 << 14,
245        max_odt_notes: 1 << 12,
246    };
247}
248
249impl Default for Limits {
250    fn default() -> Self {
251        Limits::DEFAULT
252    }
253}