1use std::borrow::Cow;
4use std::io::{Read, Write};
5use std::num::NonZeroU32;
6use std::ops::Range;
7use std::sync::Arc;
8
9use aster_forge_utils::numbers::{u32_to_usize, u64_to_usize, usize_to_u32, usize_to_u64};
10use quick_xml::Reader;
11use quick_xml::XmlVersion;
12use quick_xml::escape::unescape;
13use quick_xml::events::{BytesStart, Event};
14
15use crate::syntax::{
16 XML_NAMESPACE_URI, map_quick_xml_error_at, split_qualified_name, validate_namespace_binding,
17 validate_qualified_name,
18};
19use crate::{Error, ParseOptions, XmlSafetyError, XmlSafetyPolicy};
20
21const OWNED_VALUE_OFFSET: u64 = u64::MAX;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub struct NodeId(NonZeroU32);
26
27impl NodeId {
28 fn from_index(index: usize) -> Result<Self, Error> {
29 let value = usize_to_u32(index, "XML node index")
30 .ok()
31 .and_then(|index| index.checked_add(1))
32 .and_then(NonZeroU32::new)
33 .ok_or(XmlSafetyError::TooManyElements)?;
34 Ok(Self(value))
35 }
36
37 fn index(self) -> usize {
38 stored_index(self.0.get() - 1, "XML node index")
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43struct ScopeId(NonZeroU32);
44
45impl ScopeId {
46 fn from_index(index: usize) -> Result<Self, Error> {
47 let value = usize_to_u32(index, "XML namespace scope index")
48 .ok()
49 .and_then(|index| index.checked_add(1))
50 .and_then(NonZeroU32::new)
51 .ok_or_else(|| Error::InvalidXml("too many namespace scopes".into()))?;
52 Ok(Self(value))
53 }
54
55 fn index(self) -> usize {
56 stored_index(self.0.get() - 1, "XML namespace scope index")
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub struct SourceSpan {
63 pub start: u64,
64 pub end: u64,
65}
66
67impl SourceSpan {
68 fn as_range(self, source_len: usize) -> Option<Range<usize>> {
69 let start = u64_to_usize(self.start, "XML source span start").ok()?;
70 let end = u64_to_usize(self.end, "XML source span end").ok()?;
71 (start <= end && end <= source_len).then_some(start..end)
72 }
73}
74
75#[derive(Debug, Clone, Copy)]
76struct ValueRef {
77 offset: u64,
78 length: u32,
79 owned_index: u32,
80}
81
82impl ValueRef {
83 fn source(offset: u64, length: u32) -> Self {
84 Self {
85 offset,
86 length,
87 owned_index: 0,
88 }
89 }
90
91 fn owned(index: u32, length: u32) -> Self {
92 Self {
93 offset: OWNED_VALUE_OFFSET,
94 length,
95 owned_index: index,
96 }
97 }
98}
99
100#[derive(Debug)]
101struct ArenaNode {
102 parent: Option<NodeId>,
103 first_child: Option<NodeId>,
104 last_child: Option<NodeId>,
105 next_sibling: Option<NodeId>,
106 kind: NodeKind,
107}
108
109#[derive(Debug)]
110enum NodeKind {
111 Element(ElementData),
112 Text(ValueRef),
113 CData(ValueRef),
114 Comment(ValueRef),
115 ProcessingInstruction {
116 target: ValueRef,
117 content: Option<ValueRef>,
118 },
119}
120
121#[derive(Debug)]
122struct ElementData {
123 qualified_name: ValueRef,
124 attributes: Range<u32>,
125 namespace_scope: Option<ScopeId>,
126 source: SourceSpan,
127}
128
129#[derive(Debug)]
130struct AttributeData {
131 qualified_name: ValueRef,
132 value: ValueRef,
133}
134
135#[derive(Debug)]
136struct NamespaceScope {
137 parent: Option<ScopeId>,
138 bindings: Range<u32>,
139}
140
141#[derive(Debug)]
142struct NamespaceBinding {
143 prefix: ValueRef,
144 uri: Option<ValueRef>,
145}
146
147#[derive(Clone, Copy)]
148struct ArenaView<'a> {
149 source: &'a [u8],
150 namespace_scopes: &'a [NamespaceScope],
151 namespace_bindings: &'a [NamespaceBinding],
152 owned_values: &'a [Box<str>],
153}
154
155impl<'a> ArenaView<'a> {
156 fn value(self, value: ValueRef) -> &'a str {
157 let resolved = self.checked_value(value);
158 debug_assert!(resolved.is_some(), "invalid internal XML value reference");
159 resolved.unwrap_or("")
160 }
161
162 fn checked_value(self, value: ValueRef) -> Option<&'a str> {
163 let length = u32_to_usize(value.length, "XML value length").ok()?;
164 if value.offset == OWNED_VALUE_OFFSET {
165 let index = u32_to_usize(value.owned_index, "owned XML value index").ok()?;
166 let value = self.owned_values.get(index)?.as_ref();
167 return (value.len() == length).then_some(value);
168 }
169
170 let start = u64_to_usize(value.offset, "XML value offset").ok()?;
171 let end = start.checked_add(length)?;
172 std::str::from_utf8(self.source.get(start..end)?).ok()
173 }
174
175 fn resolve_namespace(self, scope: Option<ScopeId>, prefix: &str) -> Option<&'a str> {
176 if let Ok(namespace) = self.checked_resolve_namespace(scope, prefix) {
177 namespace
178 } else {
179 debug_assert!(false, "invalid internal XML namespace reference");
180 None
181 }
182 }
183
184 fn checked_resolve_namespace(
185 self,
186 mut scope: Option<ScopeId>,
187 prefix: &str,
188 ) -> Result<Option<&'a str>, ()> {
189 if prefix == "xml" {
190 return Ok(Some(XML_NAMESPACE_URI));
191 }
192 while let Some(scope_id) = scope {
193 let scope_data = self.namespace_scopes.get(scope_id.index()).ok_or(())?;
194 for binding_index in scope_data.bindings.clone().rev() {
195 let binding_index =
196 u32_to_usize(binding_index, "XML namespace binding index").map_err(|_| ())?;
197 let binding = self.namespace_bindings.get(binding_index).ok_or(())?;
198 if self.checked_value(binding.prefix).ok_or(())? == prefix {
199 return binding
200 .uri
201 .map(|uri| self.checked_value(uri).ok_or(()))
202 .transpose();
203 }
204 }
205 scope = scope_data.parent;
206 }
207 Ok(None)
208 }
209}
210
211#[derive(Debug)]
215pub struct XmlDocument<S> {
216 source: S,
217 nodes: Box<[ArenaNode]>,
218 attributes: Box<[AttributeData]>,
219 namespace_scopes: Box<[NamespaceScope]>,
220 namespace_bindings: Box<[NamespaceBinding]>,
221 owned_values: Box<[Box<str>]>,
222 root: NodeId,
223}
224
225pub type BorrowedDocument<'a> = XmlDocument<&'a [u8]>;
227
228pub type OwnedDocument = XmlDocument<Arc<[u8]>>;
230
231impl<S: AsRef<[u8]>> XmlDocument<S> {
232 pub fn parse(source: S) -> Result<Self, Error> {
239 Self::parse_with_options(source, &ParseOptions::default())
240 }
241
242 pub fn parse_with_options(source: S, options: &ParseOptions) -> Result<Self, Error> {
249 options.safety.validate()?;
250 if source.as_ref().len() > options.safety.max_input_bytes {
251 return Err(XmlSafetyError::InputTooLarge.into());
252 }
253
254 let (nodes, attributes, namespace_scopes, namespace_bindings, owned_values, root) = {
255 let mut builder = DocumentBuilder::new(source.as_ref(), options);
256 builder.parse()?;
257 let root = builder.root.ok_or(XmlSafetyError::Malformed)?;
258 (
259 builder.nodes.into_boxed_slice(),
260 builder.attributes.into_boxed_slice(),
261 builder.namespace_scopes.into_boxed_slice(),
262 builder.namespace_bindings.into_boxed_slice(),
263 builder.owned_values.into_boxed_slice(),
264 root,
265 )
266 };
267 Ok(Self {
268 source,
269 nodes,
270 attributes,
271 namespace_scopes,
272 namespace_bindings,
273 owned_values,
274 root,
275 })
276 }
277
278 pub fn source(&self) -> &[u8] {
279 self.source.as_ref()
280 }
281
282 pub fn into_source(self) -> S {
283 self.source
284 }
285
286 pub fn root(&self) -> ElementRef<'_, S> {
287 ElementRef {
288 document: self,
289 id: self.root,
290 }
291 }
292
293 pub fn node(&self, id: NodeId) -> Option<NodeRef<'_, S>> {
294 let node = self.nodes.get(id.index())?;
295 Some(match &node.kind {
296 NodeKind::Element(_) => NodeRef::Element(ElementRef { document: self, id }),
297 NodeKind::Text(value) => NodeRef::Text(self.value(*value)),
298 NodeKind::CData(value) => NodeRef::CData(self.value(*value)),
299 NodeKind::Comment(value) => NodeRef::Comment(self.value(*value)),
300 NodeKind::ProcessingInstruction { target, content } => {
301 NodeRef::ProcessingInstruction(ProcessingInstructionRef {
302 target: self.value(*target),
303 content: content.map(|value| self.value(value)),
304 })
305 }
306 })
307 }
308
309 pub fn node_count(&self) -> usize {
310 self.nodes.len()
311 }
312
313 pub fn allocated_value_count(&self) -> usize {
314 self.owned_values.len()
315 }
316
317 pub fn write_original<W: Write>(&self, mut writer: W) -> Result<(), Error> {
323 writer.write_all(self.source())?;
324 Ok(())
325 }
326
327 fn value(&self, value: ValueRef) -> &str {
328 self.arena_view().value(value)
329 }
330
331 fn element_data(&self, id: NodeId) -> Option<&ElementData> {
332 match &self.nodes.get(id.index())?.kind {
333 NodeKind::Element(element) => Some(element),
334 _ => None,
335 }
336 }
337
338 fn resolve_namespace(&self, scope: Option<ScopeId>, prefix: &str) -> Option<&str> {
339 self.arena_view().resolve_namespace(scope, prefix)
340 }
341
342 fn arena_view(&self) -> ArenaView<'_> {
343 ArenaView {
344 source: self.source.as_ref(),
345 namespace_scopes: &self.namespace_scopes,
346 namespace_bindings: &self.namespace_bindings,
347 owned_values: &self.owned_values,
348 }
349 }
350}
351
352impl XmlDocument<Arc<[u8]>> {
353 pub fn from_reader<R: Read>(reader: R) -> Result<Self, Error> {
360 Self::from_reader_with_options(reader, &ParseOptions::default())
361 }
362
363 pub fn from_reader_with_options<R: Read>(
370 reader: R,
371 options: &ParseOptions,
372 ) -> Result<Self, Error> {
373 options.safety.validate()?;
374 let read_limit = options.safety.max_input_bytes.saturating_add(1);
375 let read_limit = usize_to_u64(read_limit, "XML reader byte limit").unwrap_or(u64::MAX);
376 let mut reader = reader.take(read_limit);
377 let mut source = Vec::new();
378 reader.read_to_end(&mut source)?;
379 if source.len() > options.safety.max_input_bytes {
380 return Err(XmlSafetyError::InputTooLarge.into());
381 }
382 Self::parse_with_options(Arc::from(source), options)
383 }
384}
385
386#[derive(Debug, Clone)]
388pub struct ValidatedXml(Arc<OwnedDocument>);
389
390impl ValidatedXml {
391 pub fn new(bytes: impl Into<Arc<[u8]>>) -> Result<Self, Error> {
397 Self::with_policy(bytes, XmlSafetyPolicy::untrusted())
398 }
399
400 pub fn with_policy(
407 bytes: impl Into<Arc<[u8]>>,
408 policy: XmlSafetyPolicy,
409 ) -> Result<Self, Error> {
410 let source = bytes.into();
411 let document =
412 XmlDocument::parse_with_options(source, &ParseOptions::new().safety_policy(policy))?;
413 Ok(Self(Arc::new(document)))
414 }
415
416 pub fn from_reader<R: Read>(reader: R) -> Result<Self, Error> {
423 let document = OwnedDocument::from_reader(reader)?;
424 Ok(Self(Arc::new(document)))
425 }
426
427 #[must_use]
428 pub fn as_bytes(&self) -> &[u8] {
429 self.0.source()
430 }
431
432 #[must_use]
433 pub fn document(&self) -> &OwnedDocument {
434 &self.0
435 }
436}
437
438pub struct ElementRef<'document, S> {
440 document: &'document XmlDocument<S>,
441 id: NodeId,
442}
443
444impl<S> Copy for ElementRef<'_, S> {}
445
446impl<S> Clone for ElementRef<'_, S> {
447 fn clone(&self) -> Self {
448 *self
449 }
450}
451
452impl<'document, S: AsRef<[u8]>> ElementRef<'document, S> {
453 #[must_use]
454 pub fn id(self) -> NodeId {
455 self.id
456 }
457
458 #[must_use]
459 pub fn parent(self) -> Option<ElementRef<'document, S>> {
460 self.document.nodes[self.id.index()]
461 .parent
462 .map(|id| ElementRef {
463 document: self.document,
464 id,
465 })
466 }
467
468 #[must_use]
469 pub fn qualified_name(self) -> &'document str {
470 let Some(data) = self.document.element_data(self.id) else {
471 return "";
472 };
473 self.document.value(data.qualified_name)
474 }
475
476 #[must_use]
477 pub fn prefix(self) -> Option<&'document str> {
478 split_qualified_name(self.qualified_name()).0
479 }
480
481 #[must_use]
482 pub fn name(self) -> &'document str {
483 split_qualified_name(self.qualified_name()).1
484 }
485
486 #[must_use]
487 pub fn namespace(self) -> Option<&'document str> {
488 let data = self.document.element_data(self.id)?;
489 self.document
490 .resolve_namespace(data.namespace_scope, self.prefix().unwrap_or(""))
491 }
492
493 #[must_use]
494 pub fn raw_xml(self) -> &'document [u8] {
495 let Some(data) = self.document.element_data(self.id) else {
496 return &[];
497 };
498 let Some(range) = data.source.as_range(self.document.source().len()) else {
499 return &[];
500 };
501 &self.document.source()[range]
502 }
503
504 #[must_use]
505 pub fn attributes(self) -> Attributes<'document, S> {
506 let range = self
507 .document
508 .element_data(self.id)
509 .map(|data| data.attributes.clone())
510 .unwrap_or(0..0);
511 Attributes {
512 element: self,
513 next: range.start,
514 end: range.end,
515 }
516 }
517
518 pub fn attribute(self, qualified_name: &str) -> Option<&'document str> {
519 self.attributes()
520 .find(|attribute| attribute.qualified_name() == qualified_name)
521 .map(AttributeRef::value)
522 }
523
524 pub fn attribute_ns(self, name: &str, namespace: Option<&str>) -> Option<&'document str> {
525 self.attributes()
526 .find(|attribute| attribute.name() == name && attribute.namespace() == namespace)
527 .map(AttributeRef::value)
528 }
529
530 #[must_use]
531 pub fn children(self) -> Children<'document, S> {
532 Children {
533 document: self.document,
534 next: self.document.nodes[self.id.index()].first_child,
535 }
536 }
537
538 #[must_use]
539 pub fn child_elements(self) -> ChildElements<'document, S> {
540 ChildElements {
541 children: self.children(),
542 }
543 }
544
545 #[must_use]
546 pub fn get_child(self, name: &str) -> Option<ElementRef<'document, S>> {
547 self.child_elements().find(|element| element.name() == name)
548 }
549
550 #[must_use]
551 pub fn get_child_ns(self, name: &str, namespace: &str) -> Option<ElementRef<'document, S>> {
552 self.child_elements()
553 .find(|element| element.name() == name && element.namespace() == Some(namespace))
554 }
555
556 #[must_use]
557 pub fn descendants(self) -> DescendantElements<'document, S> {
558 DescendantElements { stack: vec![self] }
559 }
560
561 #[must_use]
562 pub fn text(self) -> Option<Cow<'document, str>> {
563 let mut values = self.children().filter_map(|node| match node {
564 NodeRef::Text(text) | NodeRef::CData(text) => Some(text),
565 _ => None,
566 });
567 let first = values.next()?;
568 match values.next() {
569 None => Some(Cow::Borrowed(first)),
570 Some(second) => {
571 let mut output = String::with_capacity(first.len() + second.len());
572 output.push_str(first);
573 output.push_str(second);
574 values.for_each(|value| output.push_str(value));
575 Some(Cow::Owned(output))
576 }
577 }
578 }
579}
580
581pub enum NodeRef<'document, S> {
583 Element(ElementRef<'document, S>),
584 Text(&'document str),
585 CData(&'document str),
586 Comment(&'document str),
587 ProcessingInstruction(ProcessingInstructionRef<'document>),
588}
589
590impl<S> Copy for NodeRef<'_, S> {}
591
592impl<S> Clone for NodeRef<'_, S> {
593 fn clone(&self) -> Self {
594 *self
595 }
596}
597
598#[derive(Debug, Clone, Copy, PartialEq, Eq)]
599pub struct ProcessingInstructionRef<'a> {
600 pub target: &'a str,
601 pub content: Option<&'a str>,
602}
603
604pub struct Children<'document, S> {
605 document: &'document XmlDocument<S>,
606 next: Option<NodeId>,
607}
608
609impl<'document, S: AsRef<[u8]>> Iterator for Children<'document, S> {
610 type Item = NodeRef<'document, S>;
611
612 fn next(&mut self) -> Option<Self::Item> {
613 let id = self.next?;
614 self.next = self.document.nodes[id.index()].next_sibling;
615 self.document.node(id)
616 }
617}
618
619pub struct ChildElements<'document, S> {
620 children: Children<'document, S>,
621}
622
623impl<'document, S: AsRef<[u8]>> Iterator for ChildElements<'document, S> {
624 type Item = ElementRef<'document, S>;
625
626 fn next(&mut self) -> Option<Self::Item> {
627 self.children.find_map(|node| match node {
628 NodeRef::Element(element) => Some(element),
629 _ => None,
630 })
631 }
632}
633
634pub struct DescendantElements<'document, S> {
635 stack: Vec<ElementRef<'document, S>>,
636}
637
638impl<'document, S: AsRef<[u8]>> Iterator for DescendantElements<'document, S> {
639 type Item = ElementRef<'document, S>;
640
641 fn next(&mut self) -> Option<Self::Item> {
642 let element = self.stack.pop()?;
643 let child_start = self.stack.len();
644 self.stack.extend(element.child_elements());
645 self.stack[child_start..].reverse();
646 Some(element)
647 }
648}
649
650pub struct Attributes<'document, S> {
651 element: ElementRef<'document, S>,
652 next: u32,
653 end: u32,
654}
655
656impl<'document, S: AsRef<[u8]>> Iterator for Attributes<'document, S> {
657 type Item = AttributeRef<'document, S>;
658
659 fn next(&mut self) -> Option<Self::Item> {
660 if self.next >= self.end {
661 return None;
662 }
663 let index = self.next;
664 self.next += 1;
665 Some(AttributeRef {
666 element: self.element,
667 index,
668 })
669 }
670}
671
672pub struct AttributeRef<'document, S> {
673 element: ElementRef<'document, S>,
674 index: u32,
675}
676
677impl<S> Copy for AttributeRef<'_, S> {}
678
679impl<S> Clone for AttributeRef<'_, S> {
680 fn clone(&self) -> Self {
681 *self
682 }
683}
684
685impl<'document, S: AsRef<[u8]>> AttributeRef<'document, S> {
686 fn data(self) -> Option<&'document AttributeData> {
687 let data = self.checked_data();
688 debug_assert!(data.is_some(), "invalid internal XML attribute index");
689 data
690 }
691
692 fn checked_data(self) -> Option<&'document AttributeData> {
693 self.document()
694 .attributes
695 .get(stored_index(self.index, "XML attribute index"))
696 }
697
698 fn document(self) -> &'document XmlDocument<S> {
699 self.element.document
700 }
701
702 #[must_use]
703 pub fn qualified_name(self) -> &'document str {
704 self.data()
705 .map_or("", |data| self.document().value(data.qualified_name))
706 }
707
708 #[must_use]
709 pub fn prefix(self) -> Option<&'document str> {
710 split_qualified_name(self.qualified_name()).0
711 }
712
713 #[must_use]
714 pub fn name(self) -> &'document str {
715 split_qualified_name(self.qualified_name()).1
716 }
717
718 #[must_use]
719 pub fn namespace(self) -> Option<&'document str> {
720 let prefix = self.prefix()?;
721 let scope = self
722 .document()
723 .element_data(self.element.id)
724 .and_then(|element| element.namespace_scope);
725 self.document().resolve_namespace(scope, prefix)
726 }
727
728 #[must_use]
729 pub fn value(self) -> &'document str {
730 self.data()
731 .map_or("", |data| self.document().value(data.value))
732 }
733}
734
735struct DocumentBuilder<'a> {
736 source: &'a [u8],
737 options: &'a ParseOptions,
738 nodes: Vec<ArenaNode>,
739 attributes: Vec<AttributeData>,
740 namespace_scopes: Vec<NamespaceScope>,
741 namespace_bindings: Vec<NamespaceBinding>,
742 owned_values: Vec<Box<str>>,
743 open: Vec<NodeId>,
744 root: Option<NodeId>,
745 root_complete: bool,
746 element_count: usize,
747 text_bytes: usize,
748 event_count: usize,
749}
750
751impl<'a> DocumentBuilder<'a> {
752 fn new(source: &'a [u8], options: &'a ParseOptions) -> Self {
753 Self {
754 source,
755 options,
756 nodes: Vec::new(),
757 attributes: Vec::new(),
758 namespace_scopes: Vec::new(),
759 namespace_bindings: Vec::new(),
760 owned_values: Vec::new(),
761 open: Vec::new(),
762 root: None,
763 root_complete: false,
764 element_count: 0,
765 text_bytes: 0,
766 event_count: 0,
767 }
768 }
769
770 fn parse(&mut self) -> Result<(), Error> {
771 let mut reader = Reader::from_reader(self.source);
772 reader.config_mut().trim_text(false);
773 reader.config_mut().check_end_names = true;
774 loop {
775 let event_start = reader.buffer_position();
776 let event = reader.read_event().map_err(|error| {
777 let error_position =
778 usize::try_from(reader.error_position()).unwrap_or(self.source.len());
779 map_quick_xml_error_at(
780 error,
781 error_position,
782 self.source,
783 self.options.safety.reject_doctype,
784 )
785 })?;
786 let event_end = reader.buffer_position();
787 if !matches!(event, Event::Eof) {
788 self.count_event()?;
789 }
790 match event {
791 Event::Start(start) => {
792 self.start_element(&reader, &start, event_start, event_end)?;
793 }
794 Event::Empty(start) => {
795 self.empty_element(&reader, &start, event_start, event_end)?;
796 }
797 Event::End(_) => self.end_element(event_end)?,
798 Event::Text(text) => {
799 let raw = text.as_ref();
800 let value =
801 unescape(raw).map_err(|error| Error::InvalidXml(error.to_string()))?;
802 self.text_node(value, false)?;
803 }
804 Event::CData(text) => self.text_node(Cow::Borrowed(text.as_ref()), true)?,
805 Event::Comment(comment) => {
806 let value = self.source_value(comment.as_ref())?;
807 self.push_content(NodeKind::Comment(value))?;
808 }
809 Event::PI(pi) => {
810 let target = self.source_value(pi.target())?;
811 let content = pi
812 .content()
813 .trim_start_matches(|character: char| character.is_ascii_whitespace());
814 let content = (!content.is_empty())
815 .then(|| self.source_value(content))
816 .transpose()?;
817 self.push_content(NodeKind::ProcessingInstruction { target, content })?;
818 }
819 Event::GeneralRef(reference) => {
820 let value = if let Some(character) = reference
821 .resolve_char_ref()
822 .map_err(|error| Error::InvalidXml(error.to_string()))?
823 {
824 Cow::Owned(character.to_string())
825 } else {
826 Cow::Owned(
827 match reference.as_ref() {
828 "amp" => "&",
829 "lt" => "<",
830 "gt" => ">",
831 "apos" => "'",
832 "quot" => "\"",
833 _ => return Err(XmlSafetyError::ExternalEntity.into()),
834 }
835 .to_owned(),
836 )
837 };
838 self.text_node(value, false)?;
839 }
840 Event::Decl(_) => {
841 if self.root.is_some() || !self.open.is_empty() || self.root_complete {
842 return Err(XmlSafetyError::Malformed.into());
843 }
844 }
845 Event::DocType(_) => {
846 if self.options.safety.reject_doctype {
847 return Err(XmlSafetyError::ExternalEntity.into());
848 }
849 if self.root.is_some() || !self.open.is_empty() || self.root_complete {
850 return Err(XmlSafetyError::Malformed.into());
851 }
852 }
853 Event::Eof => {
854 if !self.open.is_empty() || !self.root_complete {
855 return Err(XmlSafetyError::Malformed.into());
856 }
857 return Ok(());
858 }
859 }
860 }
861 }
862
863 fn start_element(
864 &mut self,
865 reader: &Reader<&[u8]>,
866 start: &BytesStart<'a>,
867 source_start: u64,
868 source_end: u64,
869 ) -> Result<(), Error> {
870 self.check_element()?;
871 let id = self.build_element(reader, start, source_start, source_end)?;
872 self.open.push(id);
873 Ok(())
874 }
875
876 fn empty_element(
877 &mut self,
878 reader: &Reader<&[u8]>,
879 start: &BytesStart<'a>,
880 source_start: u64,
881 source_end: u64,
882 ) -> Result<(), Error> {
883 self.check_element()?;
884 self.build_element(reader, start, source_start, source_end)?;
885 if self.open.is_empty() {
886 self.root_complete = true;
887 }
888 Ok(())
889 }
890
891 fn end_element(&mut self, source_end: u64) -> Result<(), Error> {
892 let id = self.open.pop().ok_or(XmlSafetyError::Malformed)?;
893 let NodeKind::Element(element) = &mut self.nodes[id.index()].kind else {
894 return Err(XmlSafetyError::Malformed.into());
895 };
896 element.source.end = source_end;
897 if self.open.is_empty() {
898 self.root_complete = true;
899 }
900 Ok(())
901 }
902
903 #[expect(
904 clippy::too_many_lines,
905 reason = "Element construction keeps namespace scopes, attributes, source spans, and arena links atomic."
906 )]
907 fn build_element(
908 &mut self,
909 _reader: &Reader<&[u8]>,
910 start: &BytesStart<'a>,
911 source_start: u64,
912 source_end: u64,
913 ) -> Result<NodeId, Error> {
914 if self.open.is_empty() && self.root_complete {
915 return Err(XmlSafetyError::Malformed.into());
916 }
917 let start_name = start.name();
918 let qualified_name = start_name.as_ref();
919 let (prefix, _) = validate_qualified_name(qualified_name)?;
920 let parent_scope = self.open.last().and_then(|id| {
921 let NodeKind::Element(element) = &self.nodes[id.index()].kind else {
922 return None;
923 };
924 element.namespace_scope
925 });
926 let binding_start = arena_len(self.namespace_bindings.len(), "namespace bindings")?;
927 let mut attribute_count = 0usize;
928 for attribute in start.attributes() {
929 attribute_count = attribute_count
930 .checked_add(1)
931 .ok_or(XmlSafetyError::TooManyAttributes)?;
932 if attribute_count > self.options.safety.max_attributes_per_element {
933 return Err(XmlSafetyError::TooManyAttributes.into());
934 }
935 let attribute = attribute.map_err(|error| Error::InvalidXml(error.to_string()))?;
936 let name = attribute.key.as_ref();
937 validate_qualified_name(name)?;
938 if name == "xmlns" || name.starts_with("xmlns:") {
939 let namespace_prefix = name.strip_prefix("xmlns:").unwrap_or("");
940 let uri = attribute
941 .normalized_value(XmlVersion::Explicit1_0)
942 .map_err(|error| Error::InvalidXml(error.to_string()))?;
943 validate_namespace_binding(namespace_prefix, &uri)?;
944 let prefix_value = if namespace_prefix.is_empty() {
945 ValueRef::source(0, 0)
946 } else {
947 self.source_value(namespace_prefix)?
948 };
949 let uri_value = if uri.is_empty() {
950 None
951 } else {
952 Some(self.cow_value(uri)?)
953 };
954 self.namespace_bindings.push(NamespaceBinding {
955 prefix: prefix_value,
956 uri: uri_value,
957 });
958 }
959 }
960 let binding_end = arena_len(self.namespace_bindings.len(), "namespace bindings")?;
961 let namespace_scope = if binding_start == binding_end {
962 parent_scope
963 } else {
964 let id = ScopeId::from_index(self.namespace_scopes.len())?;
965 self.namespace_scopes.push(NamespaceScope {
966 parent: parent_scope,
967 bindings: binding_start..binding_end,
968 });
969 Some(id)
970 };
971 if let Some(prefix) = prefix
972 && self
973 .arena_view()
974 .resolve_namespace(namespace_scope, prefix)
975 .is_none()
976 {
977 return Err(XmlSafetyError::Malformed.into());
978 }
979
980 let attribute_start = arena_len(self.attributes.len(), "attributes")?;
981 for attribute in start.attributes() {
982 let attribute = attribute.map_err(|error| Error::InvalidXml(error.to_string()))?;
983 let name = attribute.key.as_ref();
984 if name == "xmlns" || name.starts_with("xmlns:") {
985 continue;
986 }
987 let (prefix, _) = split_qualified_name(name);
988 if let Some(prefix) = prefix
989 && prefix != "xml"
990 && self
991 .arena_view()
992 .resolve_namespace(namespace_scope, prefix)
993 .is_none()
994 {
995 return Err(XmlSafetyError::Malformed.into());
996 }
997 let value = attribute
998 .normalized_value(XmlVersion::Explicit1_0)
999 .map_err(|error| Error::InvalidXml(error.to_string()))?;
1000 let qualified_name = self.source_value(name)?;
1001 let value = self.cow_value(value)?;
1002 self.attributes.push(AttributeData {
1003 qualified_name,
1004 value,
1005 });
1006 }
1007 let attribute_end = arena_len(self.attributes.len(), "attributes")?;
1008 let qualified_name = self.source_value(qualified_name)?;
1009 let id = self.push_node(NodeKind::Element(ElementData {
1010 qualified_name,
1011 attributes: attribute_start..attribute_end,
1012 namespace_scope,
1013 source: SourceSpan {
1014 start: source_start,
1015 end: source_end,
1016 },
1017 }))?;
1018 if self.root.is_none() {
1019 self.root = Some(id);
1020 }
1021 Ok(id)
1022 }
1023
1024 fn check_element(&mut self) -> Result<(), Error> {
1025 let depth = self
1026 .open
1027 .len()
1028 .checked_add(1)
1029 .ok_or(XmlSafetyError::TooDeep)?;
1030 if depth > self.options.safety.max_depth {
1031 return Err(XmlSafetyError::TooDeep.into());
1032 }
1033 self.element_count = self
1034 .element_count
1035 .checked_add(1)
1036 .ok_or(XmlSafetyError::TooManyElements)?;
1037 if self.element_count > self.options.safety.max_elements {
1038 return Err(XmlSafetyError::TooManyElements.into());
1039 }
1040 Ok(())
1041 }
1042
1043 fn count_event(&mut self) -> Result<(), Error> {
1044 self.event_count = self
1045 .event_count
1046 .checked_add(1)
1047 .ok_or(XmlSafetyError::TooManyEvents)?;
1048 if self.event_count > self.options.safety.max_events {
1049 return Err(XmlSafetyError::TooManyEvents.into());
1050 }
1051 Ok(())
1052 }
1053
1054 fn text_node(&mut self, value: Cow<'_, str>, cdata: bool) -> Result<(), Error> {
1055 self.text_bytes = self
1056 .text_bytes
1057 .checked_add(value.len())
1058 .ok_or(XmlSafetyError::TextTooLarge)?;
1059 if self.text_bytes > self.options.safety.max_text_bytes {
1060 return Err(XmlSafetyError::TextTooLarge.into());
1061 }
1062 let value = if self.options.trim_whitespace {
1063 match value {
1064 Cow::Borrowed(value) => Cow::Borrowed(value.trim()),
1065 Cow::Owned(value) => Cow::Owned(value.trim().to_owned()),
1066 }
1067 } else {
1068 value
1069 };
1070 if value.is_empty() {
1071 return Ok(());
1072 }
1073 if self.open.is_empty() {
1074 if value.chars().all(char::is_whitespace) {
1075 return Ok(());
1076 }
1077 return Err(XmlSafetyError::Malformed.into());
1078 }
1079 let value = self.cow_value(value)?;
1080 self.push_content(if cdata {
1081 NodeKind::CData(value)
1082 } else {
1083 NodeKind::Text(value)
1084 })?;
1085 Ok(())
1086 }
1087
1088 fn push_content(&mut self, kind: NodeKind) -> Result<(), Error> {
1089 if self.open.is_empty() {
1090 return Ok(());
1091 }
1092 self.push_node(kind).map(|_| ())
1093 }
1094
1095 fn push_node(&mut self, kind: NodeKind) -> Result<NodeId, Error> {
1096 let id = NodeId::from_index(self.nodes.len())?;
1097 let parent = self.open.last().copied();
1098 self.nodes.push(ArenaNode {
1099 parent,
1100 first_child: None,
1101 last_child: None,
1102 next_sibling: None,
1103 kind,
1104 });
1105 if let Some(parent) = parent {
1106 if let Some(previous) = self.nodes[parent.index()].last_child {
1107 self.nodes[previous.index()].next_sibling = Some(id);
1108 } else {
1109 self.nodes[parent.index()].first_child = Some(id);
1110 }
1111 self.nodes[parent.index()].last_child = Some(id);
1112 }
1113 Ok(id)
1114 }
1115
1116 fn source_value(&self, value: &str) -> Result<ValueRef, Error> {
1117 let source_start = self.source.as_ptr() as usize;
1118 let value_start = value.as_ptr() as usize;
1119 let offset = value_start
1120 .checked_sub(source_start)
1121 .filter(|offset| offset.saturating_add(value.len()) <= self.source.len())
1122 .ok_or_else(|| Error::InvalidXml("borrowed value is outside XML source".into()))?;
1123 Ok(ValueRef::source(
1124 usize_to_u64(offset, "XML value offset").map_err(|_| XmlSafetyError::InputTooLarge)?,
1125 usize_to_u32(value.len(), "XML value length")
1126 .map_err(|_| XmlSafetyError::InputTooLarge)?,
1127 ))
1128 }
1129
1130 fn cow_value(&mut self, value: Cow<'_, str>) -> Result<ValueRef, Error> {
1131 match value {
1132 Cow::Borrowed(value) => self.source_value(value),
1133 Cow::Owned(value) => {
1134 let index = arena_len(self.owned_values.len(), "owned XML values")?;
1135 let length = usize_to_u32(value.len(), "owned XML value length")
1136 .map_err(|_| XmlSafetyError::InputTooLarge)?;
1137 self.owned_values.push(value.into_boxed_str());
1138 Ok(ValueRef::owned(index, length))
1139 }
1140 }
1141 }
1142
1143 fn arena_view(&self) -> ArenaView<'_> {
1144 ArenaView {
1145 source: self.source,
1146 namespace_scopes: &self.namespace_scopes,
1147 namespace_bindings: &self.namespace_bindings,
1148 owned_values: &self.owned_values,
1149 }
1150 }
1151}
1152
1153fn arena_len(length: usize, label: &str) -> Result<u32, Error> {
1154 usize_to_u32(length, label).map_err(|_| Error::InvalidXml(format!("too many {label}")))
1155}
1156
1157fn stored_index(value: u32, label: &str) -> usize {
1158 u32_to_usize(value, label).unwrap_or(usize::MAX)
1161}
1162
1163#[cfg(test)]
1164mod layout_tests {
1165 use std::fmt::Write as _;
1166 use std::mem::size_of_val;
1167
1168 use super::*;
1169
1170 fn arena_payload_bytes<S>(document: &XmlDocument<S>) -> usize {
1171 size_of_val(document.nodes.as_ref())
1172 + size_of_val(document.attributes.as_ref())
1173 + size_of_val(document.namespace_scopes.as_ref())
1174 + size_of_val(document.namespace_bindings.as_ref())
1175 + size_of_val(document.owned_values.as_ref())
1176 + document
1177 .owned_values
1178 .iter()
1179 .map(|value| value.len())
1180 .sum::<usize>()
1181 }
1182
1183 #[test]
1184 fn arena_view_rejects_invalid_value_ranges_and_namespace_indices() {
1185 let source = b"value\xFF";
1186 let owned_values = [Box::<str>::from("owned")];
1187 let scope = ScopeId::from_index(0).expect("scope id");
1188 let namespace_scopes = [NamespaceScope {
1189 parent: None,
1190 bindings: 0..1,
1191 }];
1192 let view = ArenaView {
1193 source,
1194 namespace_scopes: &namespace_scopes,
1195 namespace_bindings: &[],
1196 owned_values: &owned_values,
1197 };
1198
1199 assert_eq!(view.checked_value(ValueRef::source(0, 5)), Some("value"));
1200 assert_eq!(view.checked_value(ValueRef::owned(0, 5)), Some("owned"));
1201 assert_eq!(view.checked_value(ValueRef::source(5, 1)), None);
1202 assert_eq!(view.checked_value(ValueRef::source(u64::MAX, 2)), None);
1203 assert_eq!(view.checked_value(ValueRef::owned(1, 5)), None);
1204 assert_eq!(view.checked_value(ValueRef::owned(0, 4)), None);
1205 assert_eq!(view.checked_resolve_namespace(Some(scope), "p"), Err(()));
1206 }
1207
1208 #[test]
1209 fn attribute_lookup_reports_invalid_internal_indices_without_indexing() {
1210 let document = BorrowedDocument::parse(br#"<root id="7"/>"#.as_slice())
1211 .expect("document should parse");
1212 let attribute = AttributeRef {
1213 element: document.root(),
1214 index: u32::MAX,
1215 };
1216
1217 assert!(attribute.checked_data().is_none());
1218 }
1219
1220 #[test]
1221 fn large_owned_document_payload_stays_below_six_times_input() {
1222 const RESPONSES: usize = 10_000;
1223 let mut source = String::from("<D:multistatus xmlns:D=\"DAV:\">");
1224 for index in 0..RESPONSES {
1225 let _ = write!(
1226 source,
1227 "<D:response><D:href>/files/{index}</D:href><D:propstat><D:prop><D:displayname>file-{index}</D:displayname><D:getcontentlength>{}</D:getcontentlength><D:getetag>"etag-{index}"</D:getetag></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>",
1228 index * 1024
1229 );
1230 }
1231 source.push_str("</D:multistatus>");
1232 let input_bytes = source.len();
1233 let options = ParseOptions::new()
1234 .max_size(input_bytes)
1235 .max_elements(RESPONSES * 8 + 1);
1236 let document = XmlDocument::parse_with_options(Arc::from(source.into_bytes()), &options)
1237 .expect("large document");
1238 let retained_payload = input_bytes + arena_payload_bytes(&document);
1239
1240 assert!(
1241 retained_payload <= input_bytes * 6,
1242 "retained payload {retained_payload} exceeds 6x input {input_bytes}"
1243 );
1244 }
1245}