PHP/XML - Problem with default namespaces when append fragment

Viewed 39

I have the original file and I want to append fragment in security tag.

<!-- Original File -->
<example>
    <header>
        <facticeA>123</facticeA>
        <facticeB>456</facticeB>
    </header>
    <body>
        <facticeC>789</facticeC>
    </body>
    <security></security>
</example>
<!-- ------------ -->
<!-- Original Fragment -->
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03" IssueInstant="2022-07-18T14:08:46.138Z" Version="2.0">
    <saml2:Issuer></saml2:Issuer>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</Signature>
</saml2:Assertion>
<!-- ----------------- -->

I use "createDocumentFragment()" and "appendXml()" PHP functions And I have this result.

<!-- Final File -->
<example>
    <header>
        <facticeA>123</facticeA>
        <facticeB>456</facticeB>
    </header>
    <body>
        <facticeC>789</facticeC>
    </body>
    <security>
        <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:default="http://www.w3.org/2000/09/xmldsig#" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03" IssueInstant="2022-07-18T14:08:46.138Z" Version="2.0">
            <saml2:Issuer/>
            <default:Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</default:Signature>
        </saml2:Assertion>
    </security>
</example>
<!-- ---------- -->

The inserted fragment is not the same than original fragment. "Signature" tag become "default:Signature" tag. And the namespace "xmldsig" present in Signature tag is append to Assertion tag with word "default"

If I delete namespace "xmldsig" in the Signature tag I have no problem. The inserted fragment is the same than original fragment.

1 Answers

This seems to be a bug in the node import. A possible workaround is to define a prefix for this namespace on the document element.

$document = new DOMDocument();
$document->preserveWhiteSpace = false;
$document->loadXML($exampleXML);
$xpath = new DOMXpath($document);

$document->documentElement->setAttributeNS(
  'http://www.w3.org/2000/xmlns/', 'xmlns:sig', 'http://www.w3.org/2000/09/xmldsig#'
);

$fragment = $document->createDocumentFragment();
$fragment->appendXML($samlXML);

foreach ($xpath->evaluate('(//security)[1]') as $security) {
  $security->appendChild($fragment);
}

$document->formatOutput = true;
echo $document->saveXML();

Output:

<?xml version="1.0"?>
<example xmlns:sig="http://www.w3.org/2000/09/xmldsig#">
  <header>
    <facticeA>123</facticeA>
    <facticeB>456</facticeB>
  </header>
  <body>
    <facticeC>789</facticeC>
  </body>
  <security>
    <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03" IssueInstant="2022-07-18T14:08:46.138Z" Version="2.0">
    <saml2:Issuer/>
    <sig:Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</sig:Signature>
</saml2:Assertion>
  </security>
</example>

Interesting enough, if you create the nodes using DOM methods it works correctly:


$document = new DOMDocument();
$document->preserveWhiteSpace = false;
$document->loadXML($exampleXML);
$xpath = new DOMXpath($document);

foreach ($xpath->evaluate('(//security)[1]') as $security) {
  $security->appendChild(
    $saml = $document->createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml2:Assertion')
  );
  $saml->setAttribute('ID', '_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03');
  $saml->appendChild(
    $document->createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml2:Issuer')
  );
  $saml->appendChild(
    $signature = $document->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'Signature')
  );
  $signature->textContent = 'XXXXXXXX';
}

$document->formatOutput = true;
echo $document->saveXML();

Output:

<?xml version="1.0"?>
<example>
  <header>
    <facticeA>123</facticeA>
    <facticeB>456</facticeB>
  </header>
  <body>
    <facticeC>789</facticeC>
  </body>
  <security>
    <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_eb0b47cc-d4b0-44ba-a08c-90047e3a8b03">
      <saml2:Issuer/>
      <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">XXXXXXXX</Signature>
    </saml2:Assertion>
  </security>
</example>

You could create a recursive function the recreates the nodes from the fragment.

Related