From f6a7645731de387551e93caa2230d03db95a53df Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sat, 29 Aug 2026 08:00:19 -0400 Subject: [PATCH] [DOM] Fix getNamedItemNS() with empty URI not matching null namespace Normalize an empty URI to NULL in spec-following mode so xmlHasNsProp() matches null-namespace attributes, and skip XML_ATTRIBUTE_DECL results which cannot be wrapped as nodes. Closes GH-23498 --- NEWS | 2 ++ ext/dom/namednodemap.c | 6 +++++ .../spec/NamedNodeMap_getNamedItemNS.phpt | 25 +++++++++++++++++++ ...medNodeMap_getNamedItemNS_dtd_default.phpt | 24 ++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt create mode 100644 ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt diff --git a/NEWS b/NEWS index 519b0ccaf053..cf8ac1f9fdf2 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ PHP NEWS return value of php_cli_server_client_send_through()). (Lazizbek Ergashev) - DOM: + . Fixed NamedNodeMap::getNamedItemNS() with an empty URI not matching + the null namespace in spec-following mode. (iliaal) . Fixed a use-after-free when cloning a DOMNameSpaceNode after DOMDocument::xinclude(). (iliaal) . Fixed bug GH-23331 (UAF when node_list_unlink() skips attribute children diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c index bc867aba4384..ac368600e20e 100644 --- a/ext/dom/namednodemap.c +++ b/ext/dom/namednodemap.c @@ -213,6 +213,9 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) objmap = (dom_nnodemap_object *)intern->ptr; if (objmap != NULL) { + if (urilen == 0 && objmap->baseobj != NULL && objmap->nodetype != XML_NOTATION_NODE && objmap->nodetype != XML_ENTITY_NODE && php_dom_follow_spec_intern(objmap->baseobj)) { + uri = NULL; + } if ((objmap->nodetype == XML_NOTATION_NODE) || objmap->nodetype == XML_ENTITY_NODE) { if (objmap->ht) { @@ -229,6 +232,9 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) nodep = dom_object_get_node(objmap->baseobj); if (nodep) { itemnode = (xmlNodePtr)xmlHasNsProp(nodep, BAD_CAST named, BAD_CAST uri); + if (itemnode != NULL && itemnode->type == XML_ATTRIBUTE_DECL) { + itemnode = NULL; + } } } } diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt new file mode 100644 index 000000000000..17d0659678e6 --- /dev/null +++ b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt @@ -0,0 +1,25 @@ +--TEST-- +getNamedItemNS() with an empty URI must look up the null namespace +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); +$a = $d->documentElement->attributes->getNamedItemNS('', 'bar'); +var_dump($a === null ? null : $a->nodeValue); +$b = $d->documentElement->attributes->getNamedItemNS('urn:q', 'bar'); +var_dump($b === null ? null : $b->nodeValue); +$d2 = Dom\XMLDocument::createFromString(''); +$a2 = $d2->documentElement->attributes->getNamedItemNS('', 'bar'); +var_dump($a2 === null ? null : $a2->nodeValue); +var_dump($d2->documentElement->hasAttributeNS('', 'bar')); +$c = $d2->documentElement->attributes->getNamedItemNS('urn:q', 'bar'); +var_dump($c === null ? null : $c->nodeValue); +?> +--EXPECT-- +NULL +string(2) "ns" +string(5) "no-ns" +bool(true) +string(2) "ns" diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt new file mode 100644 index 000000000000..c661af974c77 --- /dev/null +++ b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt @@ -0,0 +1,24 @@ +--TEST-- +getNamedItemNS() with empty URI must not throw on DTD default attributes +--EXTENSIONS-- +dom +--FILE-- + + + +]> + +XML; + +$el = Dom\XMLDocument::createFromString($xml)->documentElement; +$defaulted = $el->attributes->getNamedItemNS('', 'defaulted'); +var_dump($defaulted === null ? null : $defaulted->nodeValue); +$real = $el->attributes->getNamedItemNS('', 'real'); +var_dump($real === null ? null : $real->nodeValue); +?> +--EXPECT-- +NULL +string(7) "present"