From 23a3430ea5de3c1f885ce19a044d9cd1cf1637c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Wed, 24 Jun 2026 13:59:04 +0200 Subject: [PATCH] fix: skip blank xml nodes and attributes when anonymizing data --- src/Service/BpnXmlAnonymizer.php | 80 ++++++++++++++++---------- tests/Service/BpnXmlAnonymizerTest.php | 79 +++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 31 deletions(-) diff --git a/src/Service/BpnXmlAnonymizer.php b/src/Service/BpnXmlAnonymizer.php index e8ce701..b031aa5 100644 --- a/src/Service/BpnXmlAnonymizer.php +++ b/src/Service/BpnXmlAnonymizer.php @@ -336,24 +336,24 @@ class BpnXmlAnonymizer { $nameNode = $this->getDirectChild($personNode, 'name'); if (null !== $nameNode) { - $nameNode->nodeValue = $identity['lastName']; + $this->replaceNodeValue($nameNode, $identity['lastName']); } $firstNameNode = $this->getDirectChild($personNode, 'vorname'); if (null !== $firstNameNode) { - $firstNameNode->nodeValue = $identity['firstName']; + $this->replaceNodeValue($firstNameNode, $identity['firstName']); } if (true === $personNode->hasAttribute('name')) { - $personNode->setAttribute('name', $identity['lastName']); + $this->replaceAttributeValue($personNode, 'name', $identity['lastName']); } if (true === $personNode->hasAttribute('vorname')) { - $personNode->setAttribute('vorname', $identity['firstName']); + $this->replaceAttributeValue($personNode, 'vorname', $identity['firstName']); } if (true === $personNode->hasAttribute('id') && 'kunde' === $personNode->tagName) { - $personNode->setAttribute('id', $identity['customerId']); + $this->replaceAttributeValue($personNode, 'id', $identity['customerId']); } } @@ -367,7 +367,7 @@ class BpnXmlAnonymizer if ('anfrage' === $personNode->tagName) { $emailNode = $this->getDirectChild($personNode, 'email'); if (null !== $emailNode) { - $emailNode->nodeValue = $identity['email']; + $this->replaceNodeValue($emailNode, $identity['email']); } } @@ -376,22 +376,22 @@ class BpnXmlAnonymizer $emailNode = $this->getDirectChild($communicationNode, 'email'); if (null !== $emailNode) { - $emailNode->nodeValue = $identity['email']; + $this->replaceNodeValue($emailNode, $identity['email']); } $mobileNode = $this->getDirectChild($communicationNode, 'telefonmobil'); if (null !== $mobileNode) { - $mobileNode->nodeValue = $identity['mobilePhone']; + $this->replaceNodeValue($mobileNode, $identity['mobilePhone']); } $phoneNode = $this->getDirectChild($communicationNode, 'telefonprivat'); if (null !== $phoneNode) { - $phoneNode->nodeValue = $identity['phone']; + $this->replaceNodeValue($phoneNode, $identity['phone']); } $newsletterNode = $this->getDirectChild($communicationNode, 'newsletter'); if (null !== $newsletterNode) { - $newsletterNode->nodeValue = $identity['newsletter']; + $this->replaceNodeValue($newsletterNode, $identity['newsletter']); } } @@ -407,32 +407,32 @@ class BpnXmlAnonymizer $streetNode = $this->getDirectChild($addressNode, 'strasse'); if (null !== $streetNode) { - $streetNode->nodeValue = $identity['street']; + $this->replaceNodeValue($streetNode, $identity['street']); } $postalCodeNode = $this->getDirectChild($addressNode, 'plz'); if (null !== $postalCodeNode) { - $postalCodeNode->nodeValue = $identity['postalCode']; + $this->replaceNodeValue($postalCodeNode, $identity['postalCode']); } $cityNode = $this->getDirectChild($addressNode, 'ort'); if (null !== $cityNode) { - $cityNode->nodeValue = $identity['city']; + $this->replaceNodeValue($cityNode, $identity['city']); } $districtNode = $this->getDirectChild($addressNode, 'ortsteil'); if (null !== $districtNode) { - $districtNode->nodeValue = $identity['district']; + $this->replaceNodeValue($districtNode, $identity['district']); } $countryNode = $this->getDirectChild($addressNode, 'land'); if (null !== $countryNode) { - $countryNode->nodeValue = $identity['country']; + $this->replaceNodeValue($countryNode, $identity['country']); } $addressIdNode = $this->getDirectChild($addressNode, 'id'); if (null !== $addressIdNode) { - $addressIdNode->nodeValue = $identity['addressId']; + $this->replaceNodeValue($addressIdNode, $identity['addressId']); } } @@ -440,42 +440,42 @@ class BpnXmlAnonymizer { $salutationNode = $this->getDirectChild($personNode, 'anrede'); if (null !== $salutationNode) { - $salutationNode->nodeValue = $identity['salutation']; + $this->replaceNodeValue($salutationNode, $identity['salutation']); } $titleNode = $this->getDirectChild($personNode, 'titel'); if (null !== $titleNode) { - $titleNode->nodeValue = $identity['title']; + $this->replaceNodeValue($titleNode, $identity['title']); } $genderNode = $this->getDirectChild($personNode, 'geschlecht'); if (null !== $genderNode) { - $genderNode->nodeValue = $identity['gender']; + $this->replaceNodeValue($genderNode, $identity['gender']); } $nationalityNode = $this->getDirectChild($personNode, 'nationalitaet'); if (null !== $nationalityNode) { - $nationalityNode->nodeValue = $identity['nationality']; + $this->replaceNodeValue($nationalityNode, $identity['nationality']); } $birthDateNode = $this->getDirectChild($personNode, 'geburtsdatum'); if (null !== $birthDateNode) { - $birthDateNode->nodeValue = $identity['birthDate']; + $this->replaceNodeValue($birthDateNode, $identity['birthDate']); } $heightNode = $this->getDirectChild($personNode, 'sonstiges1'); if (null !== $heightNode) { - $heightNode->nodeValue = $identity['height']; + $this->replaceNodeValue($heightNode, $identity['height']); } $weightNode = $this->getDirectChild($personNode, 'sonstiges2'); if (null !== $weightNode) { - $weightNode->nodeValue = $identity['weight']; + $this->replaceNodeValue($weightNode, $identity['weight']); } $shoeSizeNode = $this->getDirectChild($personNode, 'sonstiges3'); if (null !== $shoeSizeNode) { - $shoeSizeNode->nodeValue = $identity['shoeSize']; + $this->replaceNodeValue($shoeSizeNode, $identity['shoeSize']); } } @@ -488,7 +488,7 @@ class BpnXmlAnonymizer $ibanNode = $this->getDirectChild($bankNode, 'iban'); if (null !== $ibanNode) { - $ibanNode->nodeValue = $identity['iban']; + $this->replaceNodeValue($ibanNode, $identity['iban']); } } @@ -496,9 +496,9 @@ class BpnXmlAnonymizer { if (true === $personNode->hasAttribute('id')) { if ('kunde' === $personNode->tagName) { - $personNode->setAttribute('id', $identity['customerId']); + $this->replaceAttributeValue($personNode, 'id', $identity['customerId']); } elseif ('teilnehmer' === $personNode->tagName || 'anmelder' === $personNode->tagName) { - $personNode->setAttribute('id', $identity['personId']); + $this->replaceAttributeValue($personNode, 'id', $identity['personId']); } } } @@ -507,25 +507,43 @@ class BpnXmlAnonymizer { $userNode = $this->getDirectChild($personNode, 'user'); if (null !== $userNode) { - $userNode->nodeValue = $identity['userName']; + $this->replaceNodeValue($userNode, $identity['userName']); } $keyNode = $this->getDirectChild($personNode, 'key'); if (null !== $keyNode) { - $keyNode->nodeValue = $identity['requestKey']; + $this->replaceNodeValue($keyNode, $identity['requestKey']); } $emailNode = $this->getDirectChild($personNode, 'email'); if (null !== $emailNode) { - $emailNode->nodeValue = $identity['email']; + $this->replaceNodeValue($emailNode, $identity['email']); } $passwordNode = $this->getDirectChild($personNode, 'passwort'); if (null !== $passwordNode) { - $passwordNode->nodeValue = $identity['requestPassword']; + $this->replaceNodeValue($passwordNode, $identity['requestPassword']); } } + private function replaceNodeValue(\DOMElement $node, string $replacement): void + { + if ('' === trim($node->textContent)) { + return; + } + + $node->nodeValue = $replacement; + } + + private function replaceAttributeValue(\DOMElement $node, string $attributeName, string $replacement): void + { + if ('' === trim((string) $node->getAttribute($attributeName))) { + return; + } + + $node->setAttribute($attributeName, $replacement); + } + private function findAncestorDirectChild(\DOMElement $personNode, string $ancestorTagName, string $childTagName): ?\DOMElement { $currentNode = $personNode; diff --git a/tests/Service/BpnXmlAnonymizerTest.php b/tests/Service/BpnXmlAnonymizerTest.php index 99e8557..0c93f9f 100644 --- a/tests/Service/BpnXmlAnonymizerTest.php +++ b/tests/Service/BpnXmlAnonymizerTest.php @@ -236,6 +236,85 @@ XML; $this->assertSame('900002', $secondCustomerId); } + public function testAnonymizePreservesEmptyNodesAndAttributes(): void + { + $xml = <<<'XML' + + + + + + + + + + + W + + + + + + 12345 + + + + + + 01701234567 + + + + + + + + 75 + + + + + + +XML; + + $anonymizer = new BpnXmlAnonymizer(); + $result = $anonymizer->anonymize($xml); + + $document = new \DOMDocument(); + $this->assertTrue($document->loadXML($result)); + + $xpath = new \DOMXPath($document); + + $this->assertSame('', trim((string) $xpath->evaluate('string(/anfrage/user)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(/anfrage/key)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(/anfrage/email)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(/anfrage/passwort)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/@id)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/name)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/vorname)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/anrede)'))); + $this->assertSame('D', trim((string) $xpath->evaluate('string(//anmelder/geschlecht)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/nationalitaet)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/geburtsdatum)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/anschrift/id)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/anschrift/strasse)'))); + $this->assertNotSame('12345', trim((string) $xpath->evaluate('string(//anmelder/anschrift/plz)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/anschrift/ort)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/anschrift/land)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/kommunikation/email)'))); + $this->assertSame('01000000002', trim((string) $xpath->evaluate('string(//anmelder/kommunikation/telefonmobil)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/kommunikation/telefonprivat)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/kommunikation/newsletter)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/bankverbindung/iban)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/sonstiges1)'))); + $this->assertSame('70', trim((string) $xpath->evaluate('string(//anmelder/sonstiges2)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//anmelder/sonstiges3)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//kundennamen/kunde/@id)'))); + $this->assertSame('', trim((string) $xpath->evaluate('string(//kundennamen/kunde/@name)'))); + $this->assertNotSame('Ada', trim((string) $xpath->evaluate('string(//kundennamen/kunde/@vorname)'))); + } + public function testAnonymizeThrowsOnInvalidXml(): void { $anonymizer = new BpnXmlAnonymizer();