diff --git a/bazelfe-core/src/bep_junit/junit_xml_error_writer.rs b/bazelfe-core/src/bep_junit/junit_xml_error_writer.rs
index b72c39f3c..f7b481be6 100644
--- a/bazelfe-core/src/bep_junit/junit_xml_error_writer.rs
+++ b/bazelfe-core/src/bep_junit/junit_xml_error_writer.rs
@@ -40,7 +40,7 @@ impl XmlWritable for TestSuite {
.attr("tests", tests.as_str())
.attr("failures", failures.as_str());
- writer.write(e).unwrap();
+ writer.write(e)?;
for s in self.testcases.iter() {
s.write_xml(writer)?;
@@ -93,11 +93,32 @@ impl XmlWritable for Failure {
writer.write(e)?;
- writer.write(XmlEvent::CData(self.value.as_str()))?;
+ let msg = self.value.as_str();
+ if contains_disallowed_xml_chars(msg) || msg.contains("]]>") {
+ // CDATA can't have escapes inside so here we use normal character data and
+ // let the library escape
+ writer.write(XmlEvent::Characters(msg))?;
+ } else {
+ // we should just be able to use a raw CData here without bothering to escape
+ // which is easier to inspect
+ writer.write(XmlEvent::CData(msg))?;
+ }
writer.write(XmlEvent::end_element())
}
}
+fn contains_disallowed_xml_chars(input: &str) -> bool {
+ input.chars().any(|c| {
+ let u = c as u32;
+ // Convert character to its Unicode code point
+ // Check for disallowed characters:
+ // - Control characters except tab (U+0009), line feed (U+000A), and carriage return (U+000D)
+ // - Null character (U+0000)
+ // - Characters in the range U+007F to U+009F
+ (u <= 0x001F && u != 0x0009 && u != 0x000A && u != 0x000D) || (0x007F..=0x009F).contains(&u)
+ })
+}
+
#[cfg(test)]
mod tests {
@@ -119,6 +140,63 @@ mod tests {
);
}
+ #[test]
+ fn test_failure_with_control_serialization() {
+ let f = Failure {
+ message: "Failed to build".to_string(),
+ tpe_name: "BuildFailure".to_string(),
+ value: "System failed to build\u{0000}".to_string(),
+ };
+
+ assert_eq!(
+ xml_writable_to_string(&f),
+ "System failed to build\0".to_string()
+ );
+
+ let f1 = Failure {
+ message: "Failed to build".to_string(),
+ tpe_name: "BuildFailure".to_string(),
+ value: "System failed to build]]>".to_string(),
+ };
+
+ assert_eq!(
+ xml_writable_to_string(&f1),
+ "System failed to build]]>".to_string()
+ );
+
+ let f2 = Failure {
+ message: "Failed to build".to_string(),
+ tpe_name: "BuildFailure".to_string(),
+ value: "System failed to build ".to_string(),
+ };
+
+ assert_eq!(
+ xml_writable_to_string(&f2),
+ "]]>".to_string()
+ );
+
+ let f3 = Failure {
+ message: "Failed to build".to_string(),
+ tpe_name: "BuildFailure".to_string(),
+ value: "System failed to build and ]]>".to_string(),
+ };
+
+ assert_eq!(
+ xml_writable_to_string(&f3),
+ "System failed to build <sometag> and ]]>".to_string()
+ );
+ let f4 = Failure {
+ message: "Failed to build".to_string(),
+ tpe_name: "BuildFailure".to_string(),
+ value: "System failed to build \u{009F}".to_string(),
+ };
+
+ assert_eq!(
+ xml_writable_to_string(&f4),
+ "System failed to build \u{9F}".to_string()
+ );
+ }
+
#[test]
fn test_testsuites_serialization() {
let f = Failure {