avm2: Throw TypeError when trying to parse malformed XML

Scratch detects if a string is valid XML by trying to construct
an XML object, and catching the thrown error.
This commit is contained in:
Aaron Hill 2023-07-03 06:25:31 -05:00 committed by Nathan Adams
parent a99b30927f
commit 2d1da4d18e
5 changed files with 35 additions and 16 deletions

View File

@ -11,7 +11,9 @@ use quick_xml::{
use crate::{avm2::TObject, xml::custom_unescape}; use crate::{avm2::TObject, xml::custom_unescape};
use super::{object::E4XOrXml, string::AvmString, Activation, Error, Multiname, Value}; use super::{
error::type_error, object::E4XOrXml, string::AvmString, Activation, Error, Multiname, Value,
};
use crate::string::{WStr, WString}; use crate::string::{WStr, WString};
/// The underlying XML node data, based on E4XNode in avmplus /// The underlying XML node data, based on E4XNode in avmplus
@ -39,6 +41,17 @@ impl<'gc> Debug for E4XNodeData<'gc> {
} }
} }
fn malformed_element<'gc>(activation: &mut Activation<'_, 'gc>) -> Error<'gc> {
Error::AvmError(
type_error(
activation,
"Error #1090: XML parser failure: element is malformed.",
1090,
)
.expect("Failed to construct XML TypeError"),
)
}
#[derive(Collect, Debug)] #[derive(Collect, Debug)]
#[collect(no_drop)] #[collect(no_drop)]
pub enum E4XNodeKind<'gc> { pub enum E4XNodeKind<'gc> {
@ -365,13 +378,14 @@ impl<'gc> E4XNode<'gc> {
} }
loop { loop {
let event = parser.read_event().map_err(|error| { let event = parser
Error::RustError(format!("XML parsing error: {error:?}").into()) .read_event()
})?; .map_err(|_| malformed_element(activation))?;
match &event { match &event {
Event::Start(bs) => { Event::Start(bs) => {
let child = E4XNode::from_start_event(activation, bs, parser.decoder())?; let child = E4XNode::from_start_event(activation, bs, parser.decoder())
.map_err(|_| malformed_element(activation))?;
if let Some(current_tag) = open_tags.last_mut() { if let Some(current_tag) = open_tags.last_mut() {
current_tag.append_child(activation.context.gc_context, child)?; current_tag.append_child(activation.context.gc_context, child)?;
@ -380,7 +394,8 @@ impl<'gc> E4XNode<'gc> {
depth += 1; depth += 1;
} }
Event::Empty(bs) => { Event::Empty(bs) => {
let node = E4XNode::from_start_event(activation, bs, parser.decoder())?; let node = E4XNode::from_start_event(activation, bs, parser.decoder())
.map_err(|_| malformed_element(activation))?;
push_childless_node(node, &mut open_tags, &mut top_level, depth, activation)?; push_childless_node(node, &mut open_tags, &mut top_level, depth, activation)?;
} }
Event::End(_) => { Event::End(_) => {
@ -392,7 +407,9 @@ impl<'gc> E4XNode<'gc> {
} }
Event::Text(bt) => { Event::Text(bt) => {
handle_text_cdata( handle_text_cdata(
custom_unescape(bt, parser.decoder())?.as_bytes(), custom_unescape(bt, parser.decoder())
.map_err(|_| malformed_element(activation))?
.as_bytes(),
ignore_white, ignore_white,
&mut open_tags, &mut open_tags,
&mut top_level, &mut top_level,
@ -419,7 +436,8 @@ impl<'gc> E4XNode<'gc> {
{ {
continue; continue;
} }
let text = custom_unescape(bt, parser.decoder())?; let text = custom_unescape(bt, parser.decoder())
.map_err(|_| malformed_element(activation))?;
let text = let text =
AvmString::new_utf8_bytes(activation.context.gc_context, text.as_bytes()); AvmString::new_utf8_bytes(activation.context.gc_context, text.as_bytes());
let kind = match event { let kind = match event {

View File

@ -305,11 +305,3 @@ impl<'gc> From<ruffle_render::error::Error> for Error<'gc> {
Error::RustError(val.into()) Error::RustError(val.into())
} }
} }
// TODO - Remove this, and convert `quick_xml` errors into AVM errors,
// specific to the XML method that was original invoked.
impl<'gc> From<quick_xml::Error> for Error<'gc> {
fn from(val: quick_xml::Error) -> Error<'gc> {
Error::RustError(val.into())
}
}

View File

@ -69,6 +69,13 @@
trace("Caught error: " + e); trace("Caught error: " + e);
trace(e.errorID); trace(e.errorID);
} }
try {
trace(new XML("<Hello<"));
} catch (e) {
trace("Caught parsing error: " + e);
trace(e.errorID);
}
} }
} }

View File

@ -19,3 +19,5 @@ castCopy equal: true
ctorCopy equal: false ctorCopy equal: false
Caught error: TypeError: Error #1088: The markup in the document following the root element must be well-formed. Caught error: TypeError: Error #1088: The markup in the document following the root element must be well-formed.
1088 1088
Caught parsing error: TypeError: Error #1090: XML parser failure: element is malformed.
1090