Skip to content

Commit 0a0e806

Browse files
authored
Fix serialization of entity references in attributes (#13884)
1 parent 5363054 commit 0a0e806

File tree

3 files changed

+62
-13
lines changed

3 files changed

+62
-13
lines changed

ext/dom/html5_serializer.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,21 @@ static zend_result dom_html5_serialize_element_start(dom_html5_serialize_context
196196
TRY(ctx->write_string(ctx->application_data, (const char *) attr->name));
197197
}
198198
}
199+
199200
TRY(ctx->write_string_len(ctx->application_data, "=\"", strlen("=\"")));
200-
xmlChar *content = xmlNodeGetContent((const xmlNode *) attr);
201-
if (content != NULL) {
202-
zend_result result = dom_html5_escape_string(ctx, (const char *) content, true);
203-
xmlFree(content);
204-
TRY(result);
201+
202+
for (xmlNodePtr child = attr->children; child != NULL; child = child->next) {
203+
if (child->type == XML_TEXT_NODE) {
204+
if (child->content != NULL) {
205+
TRY(dom_html5_escape_string(ctx, (const char *) child->content, true));
206+
}
207+
} else if (child->type == XML_ENTITY_REF_NODE) {
208+
TRY(ctx->write_string_len(ctx->application_data, "&", strlen("&")));
209+
TRY(dom_html5_escape_string(ctx, (const char *) child->name, true));
210+
TRY(ctx->write_string_len(ctx->application_data, ";", strlen(";")));
211+
}
205212
}
213+
206214
TRY(ctx->write_string_len(ctx->application_data, "\"", strlen("\"")));
207215
}
208216

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Serialize entity reference within attribute
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$xml = DOM\XMLDocument::createFromString(<<<XML
9+
<!DOCTYPE root [
10+
<!ENTITY foo "foo">
11+
]>
12+
<root><el x="&foo;bar&foo;"/></root>
13+
XML);
14+
15+
$el = $xml->documentElement->firstChild;
16+
echo $xml->saveXML(), "\n";
17+
18+
$html = DOM\HTMLDocument::createEmpty();
19+
$html->append($html->importNode($el, true));
20+
echo $html->saveHTML(), "\n";
21+
22+
?>
23+
--EXPECT--
24+
<?xml version="1.0" encoding="UTF-8"?>
25+
<!DOCTYPE root [
26+
<!ENTITY foo "foo">
27+
]>
28+
<root><el x="&foo;bar&foo;"/></root>
29+
<el x="&foo;bar&foo;"></el>

ext/dom/xml_serializer.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -542,17 +542,32 @@ static zend_always_inline int dom_xml_serialize_text_node(xmlOutputBufferPtr out
542542
return dom_xml_common_text_serialization(out, (const char *) text->content, false);
543543
}
544544

545+
static int dom_xml_serialize_attribute_node_value(xmlOutputBufferPtr out, xmlAttrPtr attr)
546+
{
547+
TRY(xmlOutputBufferWriteString(out, (const char *) attr->name));
548+
TRY(xmlOutputBufferWrite(out, strlen("=\""), "=\""));
549+
for (xmlNodePtr child = attr->children; child != NULL; child = child->next) {
550+
if (child->type == XML_TEXT_NODE) {
551+
if (child->content != NULL) {
552+
TRY(dom_xml_common_text_serialization(out, (const char *) child->content, true));
553+
}
554+
} else if (child->type == XML_ENTITY_REF_NODE) {
555+
TRY(xmlOutputBufferWrite(out, strlen("&"), "&"));
556+
TRY(dom_xml_common_text_serialization(out, (const char *) child->name, true));
557+
TRY(xmlOutputBufferWrite(out, strlen(";"), ";"));
558+
}
559+
}
560+
return xmlOutputBufferWrite(out, strlen("\""), "\"");
561+
}
562+
545563
/* Spec says to do nothing, but that's inconsistent/wrong, see https://github.com/w3c/DOM-Parsing/issues/28 */
546564
static int dom_xml_serialize_attribute_node(xmlOutputBufferPtr out, xmlNodePtr attr)
547565
{
548566
if (attr->ns != NULL && attr->ns->prefix != NULL) {
549567
TRY(xmlOutputBufferWriteString(out, (const char *) attr->ns->prefix));
550568
TRY(xmlOutputBufferWrite(out, strlen(":"), ":"));
551569
}
552-
TRY(xmlOutputBufferWriteString(out, (const char *) attr->name));
553-
TRY(xmlOutputBufferWrite(out, strlen("=\""), "=\""));
554-
TRY(dom_xml_common_text_serialization(out, (const char *) dom_get_attribute_value((xmlAttrPtr) attr), true));
555-
return xmlOutputBufferWrite(out, strlen("\""), "\"");
570+
return dom_xml_serialize_attribute_node_value(out, (xmlAttrPtr) attr);
556571
}
557572

558573
/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-a-comment-node */
@@ -730,10 +745,7 @@ static int dom_xml_serialize_attributes(
730745
* => N/A */
731746

732747
/* 3.9. Append the following strings to result, in the order listed: */
733-
TRY(xmlOutputBufferWriteString(out, (const char *) attr->name));
734-
TRY(xmlOutputBufferWrite(out, strlen("=\""), "=\""));
735-
TRY(dom_xml_common_text_serialization(out, (const char *) dom_get_attribute_value(attr), true));
736-
TRY(xmlOutputBufferWrite(out, strlen("\""), "\""));
748+
dom_xml_serialize_attribute_node_value(out, attr);
737749
}
738750

739751
/* 4. Return the value of result.

0 commit comments

Comments
 (0)