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 relationships across all `.rels` parts (Phase 12, §3).
108    pub max_opc_rels: u32,
109    /// Maximum internal relationship traversal depth (Phase 12 cycles, §3).
110    pub max_opc_rel_depth: u32,
111    /// Maximum `Default`+`Override` entries in `[Content_Types].xml` (Phase 12, §3).
112    pub max_opc_content_types_overrides: u32,
113    /// Maximum byte length of an OPC part name (Phase 12, §3).
114    pub max_opc_part_name_bytes: u32,
115    // The EPUB/OCF caps below mirror the threat model and DEFAULT/STRICT values
116    // frozen in `research/subagents/phase-12/I-security.md` §6 (§4 EPUB), as
117    // required by plan §DEC-5/§DEC-9. EPUB semantics are derived (`Q_gen`) only.
118    /// Maximum `rootfile` entries accepted in `META-INF/container.xml` (Phase 12, §4).
119    pub max_epub_rootfiles: u32,
120    /// Maximum Package Document manifest items accepted (Phase 12, §4).
121    pub max_epub_manifest_items: u32,
122    /// Maximum Package Document spine `itemref`s accepted (Phase 12, §4).
123    pub max_epub_spine_items: u32,
124    /// Maximum navigation-document nesting depth accepted (Phase 12, §4).
125    pub max_epub_nav_depth: u32,
126    /// Maximum manifest `fallback` chain length followed (Phase 12, §4).
127    pub max_epub_fallback_chain: u32,
128    /// Maximum XHTML element nodes accepted in one content/nav document (Phase 12, §4).
129    pub max_xhtml_nodes: u32,
130    // The ODT/ODF caps below mirror the EPUB caps above (Phase 13.3 applies the same
131    // bounded-XML policy to the OpenDocument content model). ODT semantics are derived
132    // (`Q_gen`) only.
133    /// Maximum `file-entry` elements accepted in `META-INF/manifest.xml` (Phase 13.3).
134    pub max_odt_manifest_entries: u32,
135    /// Maximum block elements accepted in one OpenDocument content part (Phase 13.3).
136    pub max_odt_blocks: u32,
137    /// Maximum notes accepted in one OpenDocument content part (Phase 13.3).
138    pub max_odt_notes: u32,
139    // The ODS/OpenDocument-Spreadsheet caps below bound the derived spreadsheet
140    // model (Phase 21.3.1). ODS semantics are derived (`Q_gen`) only.
141    /// Maximum `table:table` sheets accepted in one OpenDocument spreadsheet
142    /// content part (Phase 21.3.1).
143    pub max_ods_sheets: u32,
144    /// Maximum *expanded* grid slots accepted across one spreadsheet, after
145    /// `table:number-rows-repeated`/`table:number-columns-repeated` expansion
146    /// (Phase 21.3.1). Each expanded row charges at least one slot even when it
147    /// declares no cells, so an empty-row repeat bomb still declines. This is the
148    /// ODS analogue of the XLSX coordinate bound (ADR-0059): a repeated span is
149    /// bounded and the *expanded* count declines typed rather than allocating.
150    pub max_ods_cells: u64,
151    /// Maximum repeat count admitted for a single
152    /// `table:number-columns-repeated`/`table:number-rows-repeated` attribute
153    /// (Phase 21.3.1). A declaration above this bound is a typed resource-limit
154    /// decline, never an allocation.
155    pub max_ods_repeated_span: u32,
156    /// Maximum merged spans (`table:number-columns-spanned`>1 or
157    /// `table:number-rows-spanned`>1) accepted across one spreadsheet
158    /// (Phase 21.3.1).
159    pub max_ods_merges: u32,
160    /// Maximum named expressions
161    /// (`table:named-range`/`table:named-expression`) accepted across one
162    /// spreadsheet (Phase 21.3.1).
163    pub max_ods_named_expressions: u32,
164    /// Maximum `style:style` cell-style records accepted in one spreadsheet's
165    /// automatic styles or styles part (Phase 21.3.1).
166    pub max_ods_styles: u32,
167    /// Maximum `office:annotation` cell comments accepted across one spreadsheet
168    /// (Phase 21.3.1).
169    pub max_ods_comments: u32,
170    // The XLSX/SpreadsheetML caps below bound the derived semantic model
171    // (Phase 21.1.1). XLSX semantics are derived (`Q_gen`) only.
172    /// Maximum `<sheet>` declarations accepted in one workbook (Phase 21.1.1).
173    pub max_xlsx_sheets: u32,
174    /// Maximum cells accepted in one worksheet part (Phase 21.1.1).
175    pub max_xlsx_cells: u64,
176    /// Maximum shared strings accepted in `xl/sharedStrings.xml` (Phase 21.1.1).
177    pub max_xlsx_shared_strings: u32,
178    /// Maximum merged ranges accepted in one worksheet (Phase 21.1.1).
179    pub max_xlsx_merges: u32,
180    /// Maximum hyperlinks accepted in one worksheet (Phase 21.1.2).
181    pub max_xlsx_hyperlinks: u32,
182    /// Maximum comments accepted in one comments part (Phase 21.1.2).
183    pub max_xlsx_comments: u32,
184    /// Maximum table parts accepted across a workbook (Phase 21.1.2).
185    pub max_xlsx_tables: u32,
186    /// Maximum columns accepted in one table part (Phase 21.1.2).
187    pub max_xlsx_table_columns: u32,
188    /// Maximum defined/named ranges accepted in one workbook (Phase 21.1.2).
189    pub max_xlsx_defined_names: u32,
190    /// Maximum drawing parts accepted across a workbook (Phase 21.1.2).
191    pub max_xlsx_drawings: u32,
192    /// Maximum style records (fonts/fills/`cellXfs`) accepted in `styles.xml` (Phase 21.1.2).
193    pub max_xlsx_style_records: u32,
194    /// Maximum 0-based column index accepted in a cell reference (Phase 21.1.2).
195    ///
196    /// The Excel-conformant grid is 16,384 columns wide (A..XFD), so a valid
197    /// 0-based column is `< 16384`; a coordinate at or beyond this bound is a
198    /// typed resource-limit decline. Bounding the coordinate here keeps a single
199    /// hostile reference from driving an unbounded projection downstream.
200    pub max_xlsx_col: u32,
201    /// Maximum 0-based row index accepted in a cell/row reference (Phase 21.1.2).
202    ///
203    /// The Excel-conformant grid is 1,048,576 rows tall (1..1048576), so a valid
204    /// 0-based row is `< 1 << 20`; a coordinate at or beyond this bound is a
205    /// typed resource-limit decline.
206    pub max_xlsx_row: u32,
207    // The PPTX/PresentationML caps below bound the derived semantic model
208    // (Phase 21.2.1). PPTX semantics are derived (`Q_gen`) only.
209    /// Maximum slides accepted in one presentation (Phase 21.2.1).
210    pub max_pptx_slides: u32,
211    /// Maximum shapes accepted in one slide's shape tree, including group
212    /// descendants (Phase 21.2.1).
213    pub max_pptx_shapes_per_slide: u32,
214    /// Maximum text runs (`a:t`) accepted in one slide (Phase 21.2.1).
215    pub max_pptx_text_runs: u32,
216    /// Maximum group-shape nesting depth accepted in one slide (Phase 21.2.1).
217    pub max_pptx_group_depth: u32,
218    /// Maximum media parts (images/audio/video) exposed by one presentation
219    /// (Phase 21.2.1).
220    pub max_pptx_media: u32,
221    /// Maximum embedded tables accepted in one slide (Phase 21.2.1).
222    pub max_pptx_tables: u32,
223    /// Maximum table cells accepted across one slide's tables (Phase 21.2.1).
224    pub max_pptx_table_cells: u32,
225    /// Maximum notes-slide parts accepted in one presentation (Phase 21.2.1).
226    pub max_pptx_notes: u32,
227    /// Maximum slide-layout parts accepted in one presentation (Phase 21.2.1).
228    pub max_pptx_layouts: u32,
229    /// Maximum slide-master parts accepted in one presentation, and the bound
230    /// applied to theme parts (Phase 21.2.1).
231    pub max_pptx_masters: u32,
232    // The ODP/OpenDocument-Presentation caps below bound the derived presentation
233    // model (Phase 21.4.1). ODP semantics are derived (`Q_gen`) only.
234    /// Maximum `draw:page` slides accepted in one OpenDocument presentation
235    /// content part (Phase 21.4.1).
236    pub max_odp_slides: u32,
237    /// Maximum shapes accepted in one slide, including group descendants
238    /// (Phase 21.4.1).
239    pub max_odp_shapes_per_slide: u32,
240    /// Maximum text runs (`text:span`) accepted in one slide (Phase 21.4.1).
241    pub max_odp_text_runs: u32,
242    /// Maximum `draw:g` group nesting depth accepted in one slide (Phase 21.4.1).
243    pub max_odp_group_depth: u32,
244    /// Maximum `Pictures/*` media parts exposed by one presentation (Phase 21.4.1).
245    pub max_odp_media: u32,
246    /// Maximum embedded tables (`table:table`) accepted in one slide
247    /// (Phase 21.4.1).
248    pub max_odp_tables: u32,
249    /// Maximum table cells accepted across one slide's tables, after
250    /// `table:number-columns-repeated`/`table:number-rows-repeated` expansion
251    /// (Phase 21.4.1). An over-large repeat declines typed rather than allocating.
252    pub max_odp_table_cells: u32,
253    /// Maximum notes pages (`presentation:notes`) accepted in one presentation
254    /// (Phase 21.4.1).
255    pub max_odp_notes: u32,
256    /// Maximum `style:master-page` master pages accepted, and the bound applied to
257    /// `style:style` records, in one presentation (Phase 21.4.1).
258    pub max_odp_masters: u32,
259}
260
261impl Limits {
262    /// The default archival limits: generous, but always finite.
263    pub const DEFAULT: Limits = Limits {
264        max_input_bytes: 1 << 40,  // 1 TiB
265        max_output_bytes: 1 << 40, // 1 TiB
266        max_replay_bytes: 1 << 34, // 16 GiB
267        max_record_len: 1 << 31,   // 2 GiB
268        max_record_count: 1 << 20, // ~1M records
269        max_object_count: 1 << 20,
270        max_graph_ops: 1 << 20,
271        max_repeat_count: 1 << 32,
272        max_channel_symbols: 1 << 40,
273        max_model_count: 1 << 16,
274        max_channel_count: 1 << 16,
275        max_entropy_model_bytes: 4096,
276        max_pdf_spans: 1 << 26,
277        max_index_selectors: 1 << 20,
278        max_directory_bytes: 1 << 20,
279        max_checkpoint_bytes: 1 << 20,
280        max_zip_members: 1 << 20,
281        max_zip_member_compressed: 1 << 34,
282        max_zip_member_uncompressed: 1 << 34,
283        max_zip_aggregate_uncompressed: 1 << 36,
284        max_zip_compression_ratio: 1024,
285        max_zip_name_bytes: 1 << 16,
286        max_zip_extra_bytes: 1 << 16,
287        max_zip_entry_comment_bytes: 1 << 16,
288        max_zip_archive_comment_bytes: 1 << 16,
289        max_zip_central_dir_bytes: 1 << 28,
290        max_zip_prefix_bytes: 1 << 20,
291        max_zip_trailing_bytes: 1 << 20,
292        max_xml_depth: 256,
293        max_xml_part_bytes: 1 << 28,
294        max_xml_events: 1 << 24,
295        max_xml_nodes: 1 << 24,
296        max_xml_attrs_per_element: 4096,
297        max_xml_text_bytes: 1 << 28,
298        max_opc_rels: 1 << 20,
299        max_opc_rel_depth: 64,
300        max_opc_content_types_overrides: 1 << 20,
301        max_opc_part_name_bytes: 1 << 16,
302        max_epub_rootfiles: 16,
303        max_epub_manifest_items: 1 << 20,
304        max_epub_spine_items: 1 << 20,
305        max_epub_nav_depth: 64,
306        max_epub_fallback_chain: 32,
307        max_xhtml_nodes: 1 << 24,
308        max_odt_manifest_entries: 1 << 20,
309        max_odt_blocks: 1 << 20,
310        max_odt_notes: 1 << 20,
311        max_ods_sheets: 4096,
312        max_ods_cells: 1 << 24,
313        max_ods_repeated_span: 1 << 20,
314        max_ods_merges: 1 << 20,
315        max_ods_named_expressions: 1 << 20,
316        max_ods_styles: 1 << 16,
317        max_ods_comments: 1 << 20,
318        max_xlsx_sheets: 4096,
319        max_xlsx_cells: 1 << 24,
320        max_xlsx_shared_strings: 1 << 20,
321        max_xlsx_merges: 1 << 20,
322        max_xlsx_hyperlinks: 1 << 20,
323        max_xlsx_comments: 1 << 20,
324        max_xlsx_tables: 1 << 20,
325        max_xlsx_table_columns: 1 << 16,
326        max_xlsx_defined_names: 1 << 20,
327        max_xlsx_drawings: 1 << 14,
328        max_xlsx_style_records: 1 << 16,
329        max_xlsx_col: 16384,
330        max_xlsx_row: 1 << 20,
331        max_pptx_slides: 4096,
332        max_pptx_shapes_per_slide: 1 << 20,
333        max_pptx_text_runs: 1 << 22,
334        max_pptx_group_depth: 64,
335        max_pptx_media: 1 << 14,
336        max_pptx_tables: 1 << 14,
337        max_pptx_table_cells: 1 << 20,
338        max_pptx_notes: 1 << 16,
339        max_pptx_layouts: 1 << 14,
340        max_pptx_masters: 1 << 14,
341        max_odp_slides: 4096,
342        max_odp_shapes_per_slide: 1 << 20,
343        max_odp_text_runs: 1 << 22,
344        max_odp_group_depth: 64,
345        max_odp_media: 1 << 14,
346        max_odp_tables: 1 << 14,
347        max_odp_table_cells: 1 << 20,
348        max_odp_notes: 1 << 16,
349        max_odp_masters: 1 << 14,
350    };
351
352    /// Tight limits for hostile-input testing and fuzzing.
353    pub const STRICT: Limits = Limits {
354        max_input_bytes: 1 << 26,  // 64 MiB
355        max_output_bytes: 1 << 26, // 64 MiB
356        max_replay_bytes: 1 << 26, // 64 MiB
357        max_record_len: 1 << 24,   // 16 MiB
358        max_record_count: 1 << 16, // 65536
359        max_object_count: 1 << 16,
360        max_graph_ops: 1 << 16,
361        max_repeat_count: 1 << 24,
362        max_channel_symbols: 1 << 26,
363        max_model_count: 1 << 12,
364        max_channel_count: 1 << 12,
365        max_entropy_model_bytes: 4096,
366        max_pdf_spans: 1 << 16,
367        max_index_selectors: 1 << 16,
368        max_directory_bytes: 1 << 18,
369        max_checkpoint_bytes: 1 << 18,
370        max_zip_members: 1 << 16,
371        max_zip_member_compressed: 1 << 26,
372        max_zip_member_uncompressed: 1 << 26,
373        max_zip_aggregate_uncompressed: 1 << 27,
374        max_zip_compression_ratio: 256,
375        max_zip_name_bytes: 4096,
376        max_zip_extra_bytes: 4096,
377        max_zip_entry_comment_bytes: 4096,
378        max_zip_archive_comment_bytes: 4096,
379        max_zip_central_dir_bytes: 1 << 20,
380        max_zip_prefix_bytes: 1 << 16,
381        max_zip_trailing_bytes: 1 << 16,
382        max_xml_depth: 64,
383        max_xml_part_bytes: 1 << 20,
384        max_xml_events: 1 << 16,
385        max_xml_nodes: 1 << 16,
386        max_xml_attrs_per_element: 256,
387        max_xml_text_bytes: 1 << 20,
388        max_opc_rels: 1 << 14,
389        max_opc_rel_depth: 16,
390        max_opc_content_types_overrides: 1 << 12,
391        max_opc_part_name_bytes: 4096,
392        max_epub_rootfiles: 4,
393        max_epub_manifest_items: 1 << 14,
394        max_epub_spine_items: 1 << 14,
395        max_epub_nav_depth: 16,
396        max_epub_fallback_chain: 8,
397        max_xhtml_nodes: 1 << 16,
398        max_odt_manifest_entries: 1 << 14,
399        max_odt_blocks: 1 << 14,
400        max_odt_notes: 1 << 12,
401        max_ods_sheets: 64,
402        max_ods_cells: 1 << 16,
403        max_ods_repeated_span: 1 << 14,
404        max_ods_merges: 1 << 14,
405        max_ods_named_expressions: 1 << 14,
406        max_ods_styles: 1 << 12,
407        max_ods_comments: 1 << 14,
408        max_xlsx_sheets: 64,
409        max_xlsx_cells: 1 << 16,
410        max_xlsx_shared_strings: 1 << 14,
411        max_xlsx_merges: 1 << 14,
412        max_xlsx_hyperlinks: 1 << 14,
413        max_xlsx_comments: 1 << 14,
414        max_xlsx_tables: 1 << 14,
415        max_xlsx_table_columns: 1 << 12,
416        max_xlsx_defined_names: 1 << 14,
417        max_xlsx_drawings: 1 << 12,
418        max_xlsx_style_records: 1 << 12,
419        max_xlsx_col: 16384,
420        max_xlsx_row: 1 << 20,
421        max_pptx_slides: 64,
422        max_pptx_shapes_per_slide: 1 << 16,
423        max_pptx_text_runs: 1 << 18,
424        max_pptx_group_depth: 16,
425        max_pptx_media: 1 << 12,
426        max_pptx_tables: 1 << 12,
427        max_pptx_table_cells: 1 << 16,
428        max_pptx_notes: 1 << 12,
429        max_pptx_layouts: 1 << 12,
430        max_pptx_masters: 1 << 12,
431        max_odp_slides: 64,
432        max_odp_shapes_per_slide: 1 << 16,
433        max_odp_text_runs: 1 << 18,
434        max_odp_group_depth: 16,
435        max_odp_media: 1 << 12,
436        max_odp_tables: 1 << 12,
437        max_odp_table_cells: 1 << 16,
438        max_odp_notes: 1 << 12,
439        max_odp_masters: 1 << 12,
440    };
441}
442
443impl Default for Limits {
444    fn default() -> Self {
445        Limits::DEFAULT
446    }
447}