Hardening: ContentRewriter mangles //<configured-host> path segments; rewriter drops non-default ports #14

Closed
opened 2026-10-15 22:38:57 -07:00 by eric · 0 comments
Owner

Type: hardening / correctness. Found by security audit run 3 (commit 01195c8) during URL-parser fuzzing (no redirect to an unconfigured host was found in 21k cases).

What happens

  • The ContentRewriter pattern (src/Rewrite/ContentRewriter.php:46) has no left boundary before the optional scheme and //host. A same-site link whose path contains //<configured-host>/, e.g. http://example.com//example.com/x, is rewritten to https://www.example.comhttps://www.example.com/x (a broken, non-resolvable host).
  • UrlRewriter::buildTail (src/Rewrite/UrlRewriter.php:82-87) drops the port and userinfo, so https://example.com:8443/x becomes https://<current>/x. It stays on a configured host, but configured hosts served on non-default ports break.

Suggested fix

Anchor the content match with a lookbehind such as (?<![\w.:/-]) before the optional scheme group (or require a preceding quote, =, whitespace or start of string). Carry $parts['port'] through only when the configured host entry declares a port. Regression case: rewrite('<a href="http://example.com//example.com/x">') keeps the path segment intact.

**Type:** hardening / correctness. Found by security audit run 3 (commit `01195c8`) during URL-parser fuzzing (no redirect to an unconfigured host was found in 21k cases). ## What happens - The `ContentRewriter` pattern (`src/Rewrite/ContentRewriter.php:46`) has no left boundary before the optional scheme and `//host`. A same-site link whose path contains `//<configured-host>/`, e.g. `http://example.com//example.com/x`, is rewritten to `https://www.example.comhttps://www.example.com/x` (a broken, non-resolvable host). - `UrlRewriter::buildTail` (`src/Rewrite/UrlRewriter.php:82-87`) drops the port and userinfo, so `https://example.com:8443/x` becomes `https://<current>/x`. It stays on a configured host, but configured hosts served on non-default ports break. ## Suggested fix Anchor the content match with a lookbehind such as `(?<![\w.:/-])` before the optional scheme group (or require a preceding quote, `=`, whitespace or start of string). Carry `$parts['port']` through only when the configured host entry declares a port. Regression case: `rewrite('<a href="http://example.com//example.com/x">')` keeps the path segment intact.
eric closed this issue 2026-10-16 13:00:48 -07:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eric/multi-domain-redux#14
No description provided.