From 62ed6aef97e4f84b004043b6bb3c132ffa7e2feb Mon Sep 17 00:00:00 2001 From: haradahinata Date: Mon, 18 May 2026 02:24:28 +0900 Subject: [PATCH 1/2] Escape DHL rate XML fields --- common/models/shipping/Dhlexpress.php | 41 +++++++++++++++------- tests/check-dhl-rate-xml-escaping.py | 50 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 tests/check-dhl-rate-xml-escaping.py diff --git a/common/models/shipping/Dhlexpress.php b/common/models/shipping/Dhlexpress.php index 2b807f220..301921687 100644 --- a/common/models/shipping/Dhlexpress.php +++ b/common/models/shipping/Dhlexpress.php @@ -97,14 +97,20 @@ public function getQuote($address) { $pieces = ShippingHelper::get_package_piece($dhl_packs); $weight_unit = 'KG'; $dim_unit = 'CM'; - $fetch_accountrates = ($rate_type == 'ACCOUNT') ? "" . $dhlexpress_account . "" : ""; + $fetch_accountrates = ($rate_type == 'ACCOUNT') ? "" . $this->escapeXmlValue($dhlexpress_account) . "" : ""; $mailing_date = date('Y-m-d'); $mailing_datetime = date('c'); - $origin_postcode_city = ShippingHelper::get_postcode_city($fromCountryCode, $fromCity, $fromPostcode); + $origin_postcode_city = ShippingHelper::get_postcode_city( + $fromCountryCode, + $this->escapeXmlValue($fromCity), + $this->escapeXmlValue($fromPostcode) + ); //$total_value = $this->cart->get_total(); - $dutiable_content = ($is_dutiable == "Y") ? "{$selected_currency}{$total_value}" : ""; + $declared_currency = $this->escapeXmlValue($selected_currency); + $declared_value = $this->escapeXmlValue($total_value); + $dutiable_content = ($is_dutiable == "Y") ? "{$declared_currency}{$declared_value}" : ""; //$insurance_details = ($this->config->get('shipping_dhlexpress_insurance') == true) ? "". $total_value ."". $this->config->get('config_currency') ."" : ""; //$additional_insurance_details = ($this->config->get('shipping_dhlexpress_insurance') == true) ? "IIXCH" : ""; // @@ -130,7 +136,11 @@ public function getQuote($address) { } }*/ - $destination_postcode_city = ShippingHelper::get_postcode_city($county_code_to, $to_city, $postcode); + $destination_postcode_city = ShippingHelper::get_postcode_city( + $county_code_to, + $this->escapeXmlValue($to_city), + $this->escapeXmlValue($postcode) + ); $payment_country = $county_code_to;// $this->config->get('shipping_dhlexpress_country_code'); /*if ( !empty($this->config->get('shipping_dhlexpress_pay_con')) && $this->config->get('shipping_dhlexpress_pay_con') == "R" ) { @@ -149,31 +159,31 @@ public function getQuote($address) { $xml .= ' '; $xml .= ' '.$mailing_datetime.''; $xml .= ' 1234567890123456789012345678901'; - $xml .= ' '.$dhlexpress_key.''; - $xml .= ' '.$dhlexpress_password.''; + $xml .= ' '.$this->escapeXmlValue($dhlexpress_key).''; + $xml .= ' '.$this->escapeXmlValue($dhlexpress_password).''; $xml .= ' '; $xml .= ' '; $xml .= ' '; - $xml .= ' '.$fromCountryCode.''; + $xml .= ' '.$this->escapeXmlValue($fromCountryCode).''; $xml .= ' '.$origin_postcode_city; $xml .= ' '; $xml .= ' '; - $xml .= ' '.$payment_country.''; + $xml .= ' '.$this->escapeXmlValue($payment_country).''; $xml .= ' '.$mailing_date.''; $xml .= ' PT10H21M'; - $xml .= ' '.$dim_unit.''; - $xml .= ' '.$weight_unit.''; + $xml .= ' '.$this->escapeXmlValue($dim_unit).''; + $xml .= ' '.$this->escapeXmlValue($weight_unit).''; $xml .= ' '; $xml .= ' '.$pieces; $xml .= ' '; $xml .= ' '.$fetch_accountrates; - $xml .= ' '.$is_dutiable.''; + $xml .= ' '.$this->escapeXmlValue($is_dutiable).''; $xml .= ' AL'; //$xml .= ' '.$additional_insurance_details; //$xml .= ' '.$insurance_details; $xml .= ' '; $xml .= ' '; - $xml .= ' '.$county_code_to.''; + $xml .= ' '.$this->escapeXmlValue($county_code_to).''; $xml .= ' '.$destination_postcode_city; $xml .= ' '; $xml .= ' '.$dutiable_content; @@ -261,4 +271,9 @@ public function getQuote($address) { } return $method_data; } -} \ No newline at end of file + + private function escapeXmlValue($value) + { + return htmlspecialchars((string) $value, ENT_XML1 | ENT_QUOTES, 'UTF-8'); + } +} diff --git a/tests/check-dhl-rate-xml-escaping.py b/tests/check-dhl-rate-xml-escaping.py new file mode 100644 index 000000000..8889b140d --- /dev/null +++ b/tests/check-dhl-rate-xml-escaping.py @@ -0,0 +1,50 @@ +from pathlib import Path + + +dhl = Path("common/models/shipping/Dhlexpress.php") +source = dhl.read_text(encoding="utf-8") + +required_snippets = [ + "private function escapeXmlValue($value)", + "htmlspecialchars((string) $value, ENT_XML1 | ENT_QUOTES, 'UTF-8')", + "\" . $this->escapeXmlValue($dhlexpress_account) . \"", + "$this->escapeXmlValue($fromCity)", + "$this->escapeXmlValue($fromPostcode)", + "$this->escapeXmlValue($to_city)", + "$this->escapeXmlValue($postcode)", + "{$declared_currency}", + "{$declared_value}", + "'\t\t\t\t\t'.$this->escapeXmlValue($dhlexpress_key).''", + "'\t\t\t\t\t'.$this->escapeXmlValue($dhlexpress_password).''", + "'\t\t\t'.$this->escapeXmlValue($fromCountryCode).''", + "'\t\t\t'.$this->escapeXmlValue($payment_country).''", + "'\t\t\t'.$this->escapeXmlValue($dim_unit).''", + "'\t\t\t'.$this->escapeXmlValue($weight_unit).''", + "'\t\t\t'.$this->escapeXmlValue($is_dutiable).''", + "'\t\t\t'.$this->escapeXmlValue($county_code_to).''", +] + +missing = [snippet for snippet in required_snippets if snippet not in source] +if missing: + raise SystemExit( + "DHL Express XML is missing expected escaping snippets: " + + ", ".join(missing) + ) + +unsafe_snippets = [ + "\" . $dhlexpress_account . \"", + "ShippingHelper::get_postcode_city($fromCountryCode, $fromCity, $fromPostcode)", + "ShippingHelper::get_postcode_city($county_code_to, $to_city, $postcode)", + "'\t\t\t\t\t'.$dhlexpress_key.''", + "'\t\t\t\t\t'.$dhlexpress_password.''", + "'\t\t\t'.$fromCountryCode.''", + "'\t\t\t'.$payment_country.''", + "'\t\t\t'.$dim_unit.''", + "'\t\t\t'.$weight_unit.''", + "'\t\t\t'.$is_dutiable.''", + "'\t\t\t'.$county_code_to.''", +] + +for unsafe_snippet in unsafe_snippets: + if unsafe_snippet in source: + raise SystemExit(f"DHL Express XML still writes raw value: {unsafe_snippet}") From 5246a918e39be1eb2d1b456683597102a0ba473c Mon Sep 17 00:00:00 2001 From: haradahinata Date: Mon, 18 May 2026 05:15:19 +0900 Subject: [PATCH 2/2] Harden DHL XML escape guard test --- tests/check-dhl-rate-xml-escaping.py | 73 +++++++++++++++------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/tests/check-dhl-rate-xml-escaping.py b/tests/check-dhl-rate-xml-escaping.py index 8889b140d..9696e1a44 100644 --- a/tests/check-dhl-rate-xml-escaping.py +++ b/tests/check-dhl-rate-xml-escaping.py @@ -1,50 +1,53 @@ from pathlib import Path +import re dhl = Path("common/models/shipping/Dhlexpress.php") source = dhl.read_text(encoding="utf-8") -required_snippets = [ - "private function escapeXmlValue($value)", - "htmlspecialchars((string) $value, ENT_XML1 | ENT_QUOTES, 'UTF-8')", - "\" . $this->escapeXmlValue($dhlexpress_account) . \"", - "$this->escapeXmlValue($fromCity)", - "$this->escapeXmlValue($fromPostcode)", - "$this->escapeXmlValue($to_city)", - "$this->escapeXmlValue($postcode)", - "{$declared_currency}", - "{$declared_value}", - "'\t\t\t\t\t'.$this->escapeXmlValue($dhlexpress_key).''", - "'\t\t\t\t\t'.$this->escapeXmlValue($dhlexpress_password).''", - "'\t\t\t'.$this->escapeXmlValue($fromCountryCode).''", - "'\t\t\t'.$this->escapeXmlValue($payment_country).''", - "'\t\t\t'.$this->escapeXmlValue($dim_unit).''", - "'\t\t\t'.$this->escapeXmlValue($weight_unit).''", - "'\t\t\t'.$this->escapeXmlValue($is_dutiable).''", - "'\t\t\t'.$this->escapeXmlValue($county_code_to).''", +required_patterns = [ + r"private\s+function\s+escapeXmlValue\s*\(\s*\$value\s*\)", + r"htmlspecialchars\s*\(\s*\(string\)\s*\$value\s*,\s*ENT_XML1\s*\|\s*ENT_QUOTES\s*,\s*'UTF-8'\s*\)", + r"\"\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$dhlexpress_account\s*\)\s*\.\s*\"", + r"\$this->escapeXmlValue\s*\(\s*\$fromCity\s*\)", + r"\$this->escapeXmlValue\s*\(\s*\$fromPostcode\s*\)", + r"\$this->escapeXmlValue\s*\(\s*\$to_city\s*\)", + r"\$this->escapeXmlValue\s*\(\s*\$postcode\s*\)", + r"\$declared_currency\s*=\s*\$this->escapeXmlValue\s*\(\s*\$selected_currency\s*\)", + r"\$declared_value\s*=\s*\$this->escapeXmlValue\s*\(\s*\$total_value\s*\)", + r"\{\$declared_currency\}", + r"\{\$declared_value\}", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$dhlexpress_key\s*\)\s*\.\s*'", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$dhlexpress_password\s*\)\s*\.\s*'", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$fromCountryCode\s*\)\s*\.\s*'", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$payment_country\s*\)\s*\.\s*'", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$dim_unit\s*\)\s*\.\s*'", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$weight_unit\s*\)\s*\.\s*'", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$is_dutiable\s*\)\s*\.\s*'", + r"'\s*\.\s*\$this->escapeXmlValue\s*\(\s*\$county_code_to\s*\)\s*\.\s*'", ] -missing = [snippet for snippet in required_snippets if snippet not in source] +missing = [pattern for pattern in required_patterns if not re.search(pattern, source)] if missing: raise SystemExit( - "DHL Express XML is missing expected escaping snippets: " + "DHL Express XML is missing expected escaping patterns: " + ", ".join(missing) ) -unsafe_snippets = [ - "\" . $dhlexpress_account . \"", - "ShippingHelper::get_postcode_city($fromCountryCode, $fromCity, $fromPostcode)", - "ShippingHelper::get_postcode_city($county_code_to, $to_city, $postcode)", - "'\t\t\t\t\t'.$dhlexpress_key.''", - "'\t\t\t\t\t'.$dhlexpress_password.''", - "'\t\t\t'.$fromCountryCode.''", - "'\t\t\t'.$payment_country.''", - "'\t\t\t'.$dim_unit.''", - "'\t\t\t'.$weight_unit.''", - "'\t\t\t'.$is_dutiable.''", - "'\t\t\t'.$county_code_to.''", +unsafe_patterns = [ + r"\"\s*\.\s*\$dhlexpress_account\s*\.\s*\"", + r"ShippingHelper::get_postcode_city\s*\(\s*\$fromCountryCode\s*,\s*\$fromCity\s*,\s*\$fromPostcode\s*\)", + r"ShippingHelper::get_postcode_city\s*\(\s*\$county_code_to\s*,\s*\$to_city\s*,\s*\$postcode\s*\)", + r"'\s*\.\s*\$dhlexpress_key\s*\.\s*'", + r"'\s*\.\s*\$dhlexpress_password\s*\.\s*'", + r"'\s*\.\s*\$fromCountryCode\s*\.\s*'", + r"'\s*\.\s*\$payment_country\s*\.\s*'", + r"'\s*\.\s*\$dim_unit\s*\.\s*'", + r"'\s*\.\s*\$weight_unit\s*\.\s*'", + r"'\s*\.\s*\$is_dutiable\s*\.\s*'", + r"'\s*\.\s*\$county_code_to\s*\.\s*'", ] -for unsafe_snippet in unsafe_snippets: - if unsafe_snippet in source: - raise SystemExit(f"DHL Express XML still writes raw value: {unsafe_snippet}") +for unsafe_pattern in unsafe_patterns: + if re.search(unsafe_pattern, source): + raise SystemExit(f"DHL Express XML still writes raw value pattern: {unsafe_pattern}")