From 380675e2144ac1cbc732009e4ea5ed6c35e3e379 Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Fri, 10 Jul 2026 16:03:53 +0300 Subject: [PATCH] ifcparse: strip XML-illegal control characters in escape_xml (#2043, #3074) escape_xml escaped the five XML metacharacters but passed control characters (0x00 to 0x1F other than tab, newline and carriage return) through unchanged. Those bytes are illegal in XML 1.0 and cannot be represented even as numeric character references, so any IFC string containing them produced non-well-formed XML and SVG output. Strip those illegal control characters before escaping. Bytes belonging to a valid UTF-8 multibyte sequence are always >= 0x80, so filtering on the low control range leaves real text intact. This is the shared helper used by the SVG serializer text and attribute sites (audited: all route through it) and by the XML/Collada paths, so both reports are resolved at one place. Co-Authored-By: Claude Opus 4.8 --- src/ifcparse/IfcUtil.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ifcparse/IfcUtil.cpp b/src/ifcparse/IfcUtil.cpp index efb5426021..4f0204b44f 100644 --- a/src/ifcparse/IfcUtil.cpp +++ b/src/ifcparse/IfcUtil.cpp @@ -187,6 +187,15 @@ void IfcUtil::sanitate_material_name(std::string& str) { } void IfcUtil::escape_xml(std::string& str) { + // Strip characters that are illegal in XML 1.0. Control characters other + // than tab (0x09), newline (0x0A) and carriage return (0x0D) are not valid + // XML 1.0 characters and cannot even be represented as numeric character + // references, so they would otherwise make the serialized XML/SVG output + // non-well-formed. Bytes belonging to a valid UTF-8 multibyte sequence are + // always >= 0x80, so filtering on the low control range leaves them intact. + str.erase(std::remove_if(str.begin(), str.end(), [](unsigned char c) { + return c < 0x20 && c != '\t' && c != '\n' && c != '\r'; + }), str.end()); boost::replace_all(str, "&", "&"); boost::replace_all(str, "\"", """); boost::replace_all(str, "'", "'");