Skip to content

Escape DHL rate XML fields#328

Open
haradahinata wants to merge 2 commits into
BAWES-Universe:mainfrom
haradahinata:codex/escape-dhl-rate-xml-fields
Open

Escape DHL rate XML fields#328
haradahinata wants to merge 2 commits into
BAWES-Universe:mainfrom
haradahinata:codex/escape-dhl-rate-xml-fields

Conversation

@haradahinata
Copy link
Copy Markdown

@haradahinata haradahinata commented May 17, 2026

/claim #59

Summary

  • Escape DHL Express XML text nodes before concatenating rate-request values
  • Keep postcode/city helper output safe by passing escaped city and postcode values
  • Add a static regression check for the DHL XML escaping guardrails

Validation

  • python tests/check-dhl-rate-xml-escaping.py
  • python -m py_compile tests/check-dhl-rate-xml-escaping.py
  • git diff --check

Note: PHP is not installed in this environment, so php -l could not be run locally.

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced security for DHL shipping quote requests by properly escaping sensitive data (account credentials, currency, postal codes, and destination information) to protect against XML injection vulnerabilities.
  • Tests

    • Added automated validation to ensure XML escaping is properly implemented throughout shipping quote generation.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Warning

Rate limit exceeded

@haradahinata has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 17 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21535511-ff2a-47b2-9142-e999e861cc76

📥 Commits

Reviewing files that changed from the base of the PR and between 62ed6ae and 5246a91.

📒 Files selected for processing (1)
  • tests/check-dhl-rate-xml-escaping.py
📝 Walkthrough

Walkthrough

Dhlexpress.php now escapes XML-bound values in DHL quote requests using a new escapeXmlValue() helper that applies htmlspecialchars() with ENT_XML1 | ENT_QUOTES. Credentials, postcode/city data, country codes, and other XML fields are escaped during parameter assembly and XML generation. A Python test validates the escaping is correctly implemented.

Changes

DHL XML Escaping for Quote Requests

Layer / File(s) Summary
XML escape helper implementation
common/models/shipping/Dhlexpress.php
New private escapeXmlValue() method uses htmlspecialchars() with ENT_XML1 | ENT_QUOTES and UTF-8 encoding to safely escape values for XML contexts.
Parameter escaping in request assembly
common/models/shipping/Dhlexpress.php
Origin and destination postcode/city data, plus DHL account credentials and declared values, are escaped before being passed to ShippingHelper::get_postcode_city() or used in request parameters.
XML request generation with escaped values
common/models/shipping/Dhlexpress.php
Generated DHL XML request strings embed escaped credentials, country codes, unit values, and the <IsDutiable> element with escaped dutiable flag, while including previously-escaped postcode/city data.
Validation test for XML escaping
tests/check-dhl-rate-xml-escaping.py
Python test script validates that the escapeXmlValue implementation and escaped interpolations are present in Dhlexpress.php, and rejects unsafe raw/unescaped value patterns.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 Hops through the DHL request,
XML values now safe from harm,
Escaping's the charm!
From credentials to codes,
No injection roads.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding XML escaping for DHL rate request fields, which is the central focus of both file modifications.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@haradahinata
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/check-dhl-rate-xml-escaping.py (2)

7-25: ⚡ Quick win

Make guardrails whitespace-tolerant to avoid brittle failures.

These checks are highly formatting-dependent (\t, quote style, spacing), so harmless reformatting can break the test without changing safety. Prefer regex checks over exact string literals for semantic assertions.

Proposed refactor
 from pathlib import Path
+import re
@@
-missing = [snippet for snippet in required_snippets if snippet not in source]
+required_patterns = [
+    r"private function escapeXmlValue\(\$value\)",
+    r"htmlspecialchars\(\(string\)\s*\$value,\s*ENT_XML1\s*\|\s*ENT_QUOTES,\s*'UTF-8'\)",
+    r"<PaymentAccountNumber>\"\s*\.\s*\$this->escapeXmlValue\(\$dhlexpress_account\)\s*\.\s*\"</PaymentAccountNumber>",
+    r"\$this->escapeXmlValue\(\$fromCity\)",
+    r"\$this->escapeXmlValue\(\$fromPostcode\)",
+    r"\$this->escapeXmlValue\(\$to_city\)",
+    r"\$this->escapeXmlValue\(\$postcode\)",
+]
+
+missing = [p for p in required_patterns if not re.search(p, source)]
@@
-for unsafe_snippet in unsafe_snippets:
-    if unsafe_snippet in source:
-        raise SystemExit(f"DHL Express XML still writes raw value: {unsafe_snippet}")
+unsafe_patterns = [
+    r"<PaymentAccountNumber>\"\s*\.\s*\$dhlexpress_account\s*\.\s*\"</PaymentAccountNumber>",
+    r"ShippingHelper::get_postcode_city\(\$fromCountryCode,\s*\$fromCity,\s*\$fromPostcode\)",
+    r"ShippingHelper::get_postcode_city\(\$county_code_to,\s*\$to_city,\s*\$postcode\)",
+]
+
+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}")

Also applies to: 34-46

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/check-dhl-rate-xml-escaping.py` around lines 7 - 25, The test's
required_snippets list is brittle because it matches exact whitespace and
quoting; change the checks for symbols like escapeXmlValue, htmlspecialchars,
PaymentAccountNumber, SiteID, Password, CountryCode, DeclaredCurrency,
DeclaredValue, DimensionUnit, WeightUnit and others to use whitespace-tolerant
regexes (e.g. patterns built with re.compile that allow \s* between tokens and
accept either single/double-quote concatenation forms) and use re.search against
the generated XML instead of direct string inclusion; update the
required_snippets handling in tests/check-dhl-rate-xml-escaping.py to convert
the current literal strings into regex patterns and apply the same fix to the
other block referenced (lines 34-46).

15-16: ⚡ Quick win

Assert escaped declared value/currency assignments directly.

The test currently verifies tag usage but not that $declared_currency and $declared_value are created via escapeXmlValue(). Add direct checks so this cannot regress silently.

Proposed addition
 required_snippets = [
@@
     "<DeclaredCurrency>{$declared_currency}</DeclaredCurrency>",
     "<DeclaredValue>{$declared_value}</DeclaredValue>",
+    "$declared_currency = $this->escapeXmlValue($selected_currency);",
+    "$declared_value = $this->escapeXmlValue($total_value);",
@@
 ]

Also applies to: 27-33

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/check-dhl-rate-xml-escaping.py` around lines 15 - 16, Add direct
assertions that the variables $declared_currency and $declared_value are
produced by escapeXmlValue() instead of only checking tag presence: call
escapeXmlValue(raw_currency_input) and escapeXmlValue(raw_value_input) and
assert $declared_currency === that result and $declared_value === that result
(or assert the generated XML strings for
"<DeclaredCurrency>{$declared_currency}</DeclaredCurrency>" and
"<DeclaredValue>{$declared_value}</DeclaredValue>" contain the
escapeXmlValue(...) outputs). Repeat the same explicit assertions for the other
declared-currency/value occurrences referenced in the 27-33 block so escaping
cannot regress.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/check-dhl-rate-xml-escaping.py`:
- Around line 7-25: The test's required_snippets list is brittle because it
matches exact whitespace and quoting; change the checks for symbols like
escapeXmlValue, htmlspecialchars, PaymentAccountNumber, SiteID, Password,
CountryCode, DeclaredCurrency, DeclaredValue, DimensionUnit, WeightUnit and
others to use whitespace-tolerant regexes (e.g. patterns built with re.compile
that allow \s* between tokens and accept either single/double-quote
concatenation forms) and use re.search against the generated XML instead of
direct string inclusion; update the required_snippets handling in
tests/check-dhl-rate-xml-escaping.py to convert the current literal strings into
regex patterns and apply the same fix to the other block referenced (lines
34-46).
- Around line 15-16: Add direct assertions that the variables $declared_currency
and $declared_value are produced by escapeXmlValue() instead of only checking
tag presence: call escapeXmlValue(raw_currency_input) and
escapeXmlValue(raw_value_input) and assert $declared_currency === that result
and $declared_value === that result (or assert the generated XML strings for
"<DeclaredCurrency>{$declared_currency}</DeclaredCurrency>" and
"<DeclaredValue>{$declared_value}</DeclaredValue>" contain the
escapeXmlValue(...) outputs). Repeat the same explicit assertions for the other
declared-currency/value occurrences referenced in the 27-33 block so escaping
cannot regress.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3469ebca-5a6e-4445-894c-ba1b3e04c748

📥 Commits

Reviewing files that changed from the base of the PR and between bc485b0 and 62ed6ae.

📒 Files selected for processing (2)
  • common/models/shipping/Dhlexpress.php
  • tests/check-dhl-rate-xml-escaping.py

@haradahinata
Copy link
Copy Markdown
Author

Follow-up after CodeRabbit review: I pushed 5246a918 to make the DHL XML escape guard less brittle and add direct checks for declared value/currency escaping.

What changed:

  • converts exact string checks to whitespace-tolerant regex checks
  • verifies $declared_currency and $declared_value are assigned through escapeXmlValue(...)
  • still fails if raw DHL credentials/address/config values are interpolated into XML tags

Validation run locally:

python tests/check-dhl-rate-xml-escaping.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant