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 <noreply@anthropic.com>
This commit is contained in:
Petru Conduraru
2026-07-10 16:03:53 +03:00
committed by Thomas Krijnen
parent 3e55c5126c
commit 380675e214
+9
View File
@@ -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, "&", "&amp;");
boost::replace_all(str, "\"", "&quot;");
boost::replace_all(str, "'", "&apos;");