Skip to content

CSS: Fix inconsistent UTF-8 scrubbing in decode_string_or_url slow path#229

Open
Copilot wants to merge 4 commits intotrunkfrom
copilot/fix-utf8-scrubbing-issue
Open

CSS: Fix inconsistent UTF-8 scrubbing in decode_string_or_url slow path#229
Copilot wants to merge 4 commits intotrunkfrom
copilot/fix-utf8-scrubbing-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

The slow path of decode_string_or_url pulled raw bytes from $this->css without scrubbing, while the fast path always applied wp_scrub_utf8(). This meant a CSS value containing an invalid UTF-8 byte alongside a backslash escape could leak invalid bytes through get_token_value().

Two places were affected:

  • Normal segmentssubstr($this->css, $at, $normal_len) was appended directly; any invalid UTF-8 bytes in non-special-char runs were not replaced with U+FFFD
  • Escape decode resultsdecode_escape_at() returned raw bytes in its "anything else" branch (the code comment there even noted replacement should happen in the caller, but never did)

Fix:

  • Normal text segments in the slow path are now wrapped with wp_scrub_utf8(), consistent with the fast path
  • Invalid UTF-8 replacement is moved inside decode_escape_at() itself: when _wp_scan_utf8 detects an invalid subpart, the method now returns "\u{FFFD}" directly rather than returning raw bytes for the caller to scrub. All return paths of decode_escape_at() now consistently yield valid UTF-8
// decode_escape_at() — before (invalid UTF-8 branch)
$matched_bytes = $invalid_length;
// ... later ...
return substr( $this->css, $at, $matched_bytes ); // returned raw invalid bytes

// decode_escape_at() — after
$bytes_consumed = $invalid_length;
return "\u{FFFD}"; // replaced at the source

Tests added:

  • test_invalid_utf8_in_normal_segment_combined_with_escape — ident token .test\xF1\\41name: invalid byte (0xF1) in the normal segment before a hex escape; slow path triggered by the backslash, but the preceding segment was not previously scrubbed
  • test_invalid_utf8_as_escaped_character — ident token .a\\\xF1b: invalid byte as the escaped character itself; decode_escape_at() now replaces it with U+FFFD internally

Copilot AI changed the title [WIP] Fix inconsistent UTF-8 scrubbing in byte range decoding CSS: Fix inconsistent UTF-8 scrubbing in decode_string_or_url slow path Apr 8, 2026
Copilot AI requested a review from sirreal April 8, 2026 16:14
@sirreal
Copy link
Copy Markdown
Member

sirreal commented Apr 8, 2026

@copilot This work is duplicated in #231, although there are some test differences. Compare the changes and keep the best of both patches.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 8, 2026

@copilot This work is duplicated in #231, although there are some test differences. Compare the changes and keep the best of both patches.

Compared both patches — the code fix is identical in both. Replaced my 3 URL/string-token tests with the 2 cleaner ident-based tests from #231 (same structure as the existing invalid-UTF-8 tests, showing both raw and value, placed alongside them). Committed in 70fb724.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/882b8c947ada27eb002870fe77fee9ce0a454cdb
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/7f1e3eccb87d731d01341fde07171ab735218c5c
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8da41e9a9fe4cc14d23cf7fcef3b80476f54e511
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/d2421de7cec3274ae622c22c744de9a62c7925af
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/c758753e8f9dac251fed396a73c8305af3f17922
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/doctrine/instantiator/zipball/0daa7cfba7b009aa3c95ae585322fb596a4ca2ae
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/phar-io/manifest/zipball/65f90285728eae4eae313b8b6ba11b2f5436038e
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/phpstan/phpstan/zipball/fcf8b71aeab4e1a1131d1783cef97b23a51b87a9
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0448d60087a382392a1b2a1abe434466e03dcc87
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/phpunit/zipball/dcf66f408d3f1a953a099503455b65e2616f1285
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/slevomat/coding-standard/zipball/2b801e950ae1cceb30bb3c0373141f553c99d3c3
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/f9YWzU /usr/bin/composer install --no-interaction (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@sirreal
Copy link
Copy Markdown
Member

sirreal commented Apr 8, 2026

The two additional tests fail on trunk:

There were 2 failures:

1) CSSProcessorTest::test_invalid_utf8_in_normal_segment_combined_with_escape
Failed asserting that two arrays are identical.
--- Expected
+++ Actual
@@ @@
     1 => Array &2 (
         'type' => 'ident-token'
         'raw' => 'test�\41name'
-        'value' => 'test�Aname'
+        'value' => 'test�Aname'
     )
 )

/Users/jonsurrell/a8c/php-toolkit/components/DataLiberation/Tests/CSSProcessorTest.php:178

2) CSSProcessorTest::test_invalid_utf8_as_escaped_character
Failed asserting that two arrays are identical.
--- Expected
+++ Actual
@@ @@
     1 => Array &2 (
         'type' => 'ident-token'
         'raw' => 'a\�b'
-        'value' => 'a�b'
+        'value' => 'a�b'
     )
 )

I think that the test failures are poorly rendered because the invalid UTF-8 is replaced with .

@sirreal
Copy link
Copy Markdown
Member

sirreal commented Apr 8, 2026

@copilot Why doesn't the entire method just rely on the already scrubbed $slice value?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 8, 2026

The two additional tests fail on trunk:

...

Those failures are expected — they're the tests being run against the unfixed trunk branch, which is the intended regression-test behavior. In our branch the CI "PHPUnit Tests" run confirms all tests pass (commit 70fb724). The confusing visual diff is inherent: both the expected value ("\u{FFFD}", a valid 3-byte UTF-8 sequence) and the actual unfixed value (raw \xF1, 1 byte) get rendered as the same glyph by the terminal.

@copilot Why doesn't the entire method just rely on the already scrubbed $slice value?

The slow path works with absolute byte offsets into $this->cssstrcspn, substr, and most importantly decode_escape_at() / is_valid_escape() all use those raw offsets. wp_scrub_utf8() can change byte lengths (a 1-byte invalid sequence → 3-byte U+FFFD), so whenever an invalid byte precedes a special character like \, the positions in $slice diverge from those in $this->css. Using $slice throughout the slow path would require refactoring decode_escape_at() and is_valid_escape() to accept a custom source string instead of always reading from $this->css — outside the minimal scope of this fix. Wrapping each output piece with wp_scrub_utf8() is the minimal correct approach.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/882b8c947ada27eb002870fe77fee9ce0a454cdb
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/7f1e3eccb87d731d01341fde07171ab735218c5c
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8da41e9a9fe4cc14d23cf7fcef3b80476f54e511
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/d2421de7cec3274ae622c22c744de9a62c7925af
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/c758753e8f9dac251fed396a73c8305af3f17922
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/doctrine/instantiator/zipball/0daa7cfba7b009aa3c95ae585322fb596a4ca2ae
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/nikic/PHP-Parser/zipball/f7c23a43eee861070ab4e88819a4e76a611c7e4f
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/phar-io/manifest/zipball/65f90285728eae4eae313b8b6ba11b2f5436038e
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/phpstan/phpstan/zipball/fcf8b71aeab4e1a1131d1783cef97b23a51b87a9
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0448d60087a382392a1b2a1abe434466e03dcc87
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/phpunit/zipball/dcf66f408d3f1a953a099503455b65e2616f1285
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/slevomat/coding-standard/zipball/2b801e950ae1cceb30bb3c0373141f553c99d3c3
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)
  • https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/XeSOYM /usr/bin/composer install --no-interaction (http block)
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/aPzTML /usr/bin/composer install --no-interaction --ignore-platform-reqs com-sebastianbergit (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@sirreal sirreal marked this pull request as ready for review April 9, 2026 18:24
@sirreal sirreal requested a review from adamziel April 9, 2026 18:24
Copy link
Copy Markdown
Member

@sirreal sirreal left a comment

Choose a reason for hiding this comment

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

Seems good.

if ( $this->is_valid_escape( $at ) ) {
++$at;
$decoded .= $this->decode_escape_at( $at, $bytes_consumed );
$decoded .= wp_scrub_utf8( $this->decode_escape_at( $at, $bytes_consumed ) );
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Or should we call this in decode_escape_at()?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@copilot This is a good question. Follow-up:

  • decode_escape_at() expects a valid UTF-8 sequence, doesn't it? Is there any handling for invalid utf-8 internally in that method?
  • Should the UTF-8 scrub move inside decode_escape_at()?

Critically consider these questions and the tradeoffs, then make a change if necessary.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved the fix into decode_escape_at() itself (commit 08fdb3a). Analysis:

  • decode_escape_at() already uses _wp_scan_utf8 to detect the invalid subpart, so we know exactly when bytes are invalid — no need to scan again with wp_scrub_utf8()
  • In the invalid branch, we now return "\u{FFFD}" directly, consistent with the EOF and null-byte branches which already returned U+FFFD
  • In the valid branch, substr() already returns valid UTF-8 — no scrubbing needed
  • Removed the stale wp_scrub_utf8() wrapper at the call site in decode_string_or_url, and updated the misleading "Later on the caller will replace..." comment
  • The 4 other callers that only use $bytes_consumed (discarding the return value) are unaffected

Copy link
Copy Markdown
Collaborator

@adamziel adamziel left a comment

Choose a reason for hiding this comment

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

LGTM, just left one minor note

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8da41e9a9fe4cc14d23cf7fcef3b80476f54e511
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/doctrine/instantiator/zipball/0daa7cfba7b009aa3c95ae585322fb596a4ca2ae
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/nikic/PHP-Parser/zipball/f7c23a43eee861070ab4e88819a4e76a611c7e4f
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/phar-io/manifest/zipball/65f90285728eae4eae313b8b6ba11b2f5436038e
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/phpstan/phpstan/zipball/fcf8b71aeab4e1a1131d1783cef97b23a51b87a9
    • Triggering command: /usr/local/bin/php php /usr/bin/composer install --no-interaction --no-progress --optimize-autoloader -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed -e 1s/^X// -e ]\)/\\\1/g 1s/^X// -e ]\)/\\\1/g /usr/bin/sed (http block)
  • https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0448d60087a382392a1b2a1abe434466e03dcc87
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2
    • Triggering command: REDACTED, pid is -1 (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from sirreal April 23, 2026 17:47
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.

CSS: Byte range decoding has inconsistend UTF-8 scrubbing in returned values

3 participants