Skip to content

fix: preg_replace backreference corruption in preload head injection#7

Merged
SamJUK merged 1 commit into
masterfrom
fix/response-before-preg-replace-backreference
Jul 15, 2026
Merged

fix: preg_replace backreference corruption in preload head injection#7
SamJUK merged 1 commit into
masterfrom
fix/response-before-preg-replace-backreference

Conversation

@SamJUK

@SamJUK SamJUK commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

getPreloadsHTML()'s output was passed straight into preg_replace()'s
replacement argument:

$response->setBody(preg_replace(
    '/<head.*?>/',
    "<head>...{$this->getPreloadsHTML()}...",
    $response->getBody(),
    1
));

PHP's preg_replace() treats $0-$99 (and \0-\99) in the
replacement string as backreferences regardless of where the string
came from. The pattern here has no capture groups, so any preloaded
href containing a literal $ followed by digits (a filename like
image$1.jpg, or a querystring like ?v=$1) gets that fragment
silently stripped from the injected <link> tag — no error, no log,
just a broken preload URL.

Verified directly:

preg_replace('/<head.*?>/', '<head>preloadurl=/img$1.jpg</head>', $body, 1);
// => '<head>preloadurl=/img.jpg</head>'  -- "$1" vanished

Fix: use preg_replace_callback() instead — its return value is used
literally and is never re-scanned for backreferences.

Test plan

  • Added testDoesNotCorruptHrefsContainingDollarDigitSequences
    covering an href of /img$1.jpg.
  • Full Test/Unit/Observer/ResponseBeforeTest.php suite passes (8/8)
    against Magento 2.4.8-p3/PHP 8.3.

getPreloadsHTML() output was passed straight into preg_replace()'s
replacement argument. PHP treats $0-$99 in that argument as
backreferences regardless of source, so any href containing a literal
"$" followed by digits (e.g. a filename like image$1.jpg, or a
querystring like ?v=$1) was silently stripped from the injected <link>
markup with no error. Switched to preg_replace_callback(), whose
return value is used literally.
@SamJUK
SamJUK merged commit caf7999 into master Jul 15, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant