Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,14 @@ private function scan_tokens(array $tokens): void
}
}

private static function encoded_payload_could_decode_to_http_scheme(string $payload): bool
/**
* Return whether a Base64 payload could decode to an HTTP(S) URL.
*
* A Base64 group begins at one of three alignments relative to the scheme,
* so the encoded payload has one of four markers. The caller remains
* responsible for Base64 validation and URL rewriting.
*/
public static function encoded_payload_could_decode_to_http_scheme(string $payload): bool
{
return strpos($payload, 'aHR0') !== false
|| strpos($payload, 'dHA6') !== false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function replace_raw_current_text(string $updated_text): bool
}

/**
* Replace configured URL bases in the current raw text token.
* Replace configured URL bases in the current raw modifiable text.
*
* WP_HTML_Tag_Processor exposes decoded text through get_modifiable_text()
* and HTML-encodes the complete replacement in set_modifiable_text(). The
Expand All @@ -70,7 +70,7 @@ public function replace_raw_current_text(string $updated_text): bool
*/
public function replace_url_bases_in_current_text(array $url_mapping): bool
{
if ('#text' !== $this->get_token_type()) {
if ($this->get_token_type() !== '#text' && $this->get_token_type() !== '#tag') {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,330 @@
{
private const DIGITS = '0123456789';

/**
* Return whether the first $bytes could be the prefix of a serialized
* value accepted by this processor.
*
* The prefix may stop inside a token, a byte-counted string, or an open
* array/object. Bytes after the prefix are deliberately not inspected.
*/
public static function is_valid_prefix(string $serialized, int $bytes = 100): bool
{
$length = min(strlen($serialized), $bytes);
if ($length === 0) {
return true;
}

$pos = 0;
$parse_value = null;

$expect = static function (string $expected) use (&$serialized, &$pos, $length): int {
if ($pos === $length) {
return 0;
}

$available = min(strlen($expected), $length - $pos);
if (substr_compare($serialized, $expected, $pos, $available) !== 0) {
return -1;
}

if ($available < strlen($expected)) {
$pos = $length;
return 0;
}

$pos += $available;
return 1;
};

$parse_count = static function () use (&$serialized, &$pos, $length): array {
if ($pos === $length) {
return [0, 0];
}

$digits = strspn($serialized, self::DIGITS, $pos, $length - $pos);
if ($digits === 0) {
return [-1, 0];
}

$count = 0;
for ($offset = 0; $offset < $digits; ++$offset) {
$digit = ord($serialized[$pos + $offset]) - ord('0');
if ($count > intdiv(PHP_INT_MAX - $digit, 10)) {
return [-1, 0];
}
$count = $count * 10 + $digit;
}

$pos += $digits;
return [1, $count];
};

$parse_string = static function () use (&$serialized, &$pos, $length, $expect, $parse_count): int {

Check failure on line 89 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.

Check failure on line 89 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.
$status = $expect('s:');
if ($status !== 1) {
return $status;
}

[$status, $byte_length] = $parse_count();
if ($status !== 1) {
return $status;
}

$status = $expect(':"');
if ($status !== 1) {
return $status;
}

if ($byte_length > $length - $pos) {
$pos = $length;
return 0;
}

$pos += $byte_length;
return $expect('";');
};

$parse_integer = static function () use (&$serialized, &$pos, $length, $expect): int {
$status = $expect('i:');
if ($status !== 1) {
return $status;
}

if ($pos === $length) {
return 0;
}

if ($serialized[$pos] === '-') {
++$pos;
if ($pos === $length) {
return 0;
}
}

$digits = strspn($serialized, self::DIGITS, $pos, $length - $pos);
if ($digits === 0) {
return -1;
}

$pos += $digits;
return $expect(';');
};

$parse_double = static function () use (&$serialized, &$pos, $length, $expect): int {
$status = $expect('d:');
if ($status !== 1) {
return $status;
}

$span = strcspn($serialized, ';', $pos, $length - $pos);
if ($span === 0) {
return $pos === $length ? 0 : -1;
}

$pos += $span;
return $expect(';');
};

$parse_boolean = static function () use (&$serialized, &$pos, $length, $expect): int {
$status = $expect('b:');
if ($status !== 1) {
return $status;
}

if ($pos === $length) {
return 0;
}
if ($serialized[$pos] !== '0' && $serialized[$pos] !== '1') {
return -1;
}

++$pos;
return $expect(';');
};

$parse_reference = static function () use (&$serialized, &$pos, $length, $expect, $parse_count): int {

Check failure on line 172 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.

Check failure on line 172 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $length.

Check failure on line 172 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.

Check failure on line 172 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $length.
++$pos;
$status = $expect(':');
if ($status !== 1) {
return $status;
}

[$status] = $parse_count();
if ($status !== 1) {
return $status;
}

return $expect(';');
};

$parse_array = null;
$parse_object = null;
$parse_custom = null;

$parse_array = function () use (&$parse_value, &$serialized, &$pos, $length, $expect, $parse_count): int {

Check failure on line 191 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.

Check failure on line 191 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $pos.

Check failure on line 191 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $length.

Check failure on line 191 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.

Check failure on line 191 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $pos.

Check failure on line 191 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $length.
$status = $expect('a:');
if ($status !== 1) {
return $status;
}

[$status, $count] = $parse_count();
if ($status !== 1) {
return $status;
}

$status = $expect(':{');
if ($status !== 1) {
return $status;
}

for ($entry = 0; $entry < $count; ++$entry) {
$status = $parse_value();

Check failure on line 208 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Trying to invoke null but it's not a callable.

Check failure on line 208 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Trying to invoke null but it's not a callable.
if ($status !== 1) {
return $status;
}

$status = $parse_value();

Check failure on line 213 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Trying to invoke null but it's not a callable.

Check failure on line 213 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Trying to invoke null but it's not a callable.
if ($status !== 1) {
return $status;
}
}

return $expect('}');
};

$parse_object = function () use (&$parse_value, &$serialized, &$pos, $length, $expect, $parse_count): int {

Check failure on line 222 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.

Check failure on line 222 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $serialized.
$status = $expect('O:');
if ($status !== 1) {
return $status;
}

[$status, $class_name_length] = $parse_count();
if ($status !== 1) {
return $status;
}

$status = $expect(':"');
if ($status !== 1) {
return $status;
}

if ($class_name_length > $length - $pos) {
$pos = $length;
return 0;
}

$pos += $class_name_length;
$status = $expect('":');
if ($status !== 1) {
return $status;
}

[$status, $property_count] = $parse_count();
if ($status !== 1) {
return $status;
}

$status = $expect(':{');
if ($status !== 1) {
return $status;
}

for ($property = 0; $property < $property_count; ++$property) {
$status = $parse_value();

Check failure on line 260 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Trying to invoke null but it's not a callable.

Check failure on line 260 in packages/reprint-client/src/lib/url-rewrite/class-php-serialization-processor.php

View workflow job for this annotation

GitHub Actions / PHPStan

Trying to invoke null but it's not a callable.
if ($status !== 1) {
return $status;
}

$status = $parse_value();
if ($status !== 1) {
return $status;
}
}

return $expect('}');
};

$parse_custom = static function () use (&$serialized, &$pos, $length, $expect, $parse_count): int {
$status = $expect('C:');
if ($status !== 1) {
return $status;
}

[$status, $class_name_length] = $parse_count();
if ($status !== 1) {
return $status;
}

$status = $expect(':"');
if ($status !== 1) {
return $status;
}

if ($class_name_length > $length - $pos) {
$pos = $length;
return 0;
}

$pos += $class_name_length;
$status = $expect('":');
if ($status !== 1) {
return $status;
}

[$status, $payload_length] = $parse_count();
if ($status !== 1) {
return $status;
}

$status = $expect(':{');
if ($status !== 1) {
return $status;
}

if ($payload_length > $length - $pos) {
$pos = $length;
return 0;
}

$pos += $payload_length;
return $expect('}');
};

$parse_value = function () use (&$serialized, &$pos, $length, $parse_string, $parse_integer, $parse_double, $parse_boolean, $expect, $parse_array, $parse_object, $parse_custom, $parse_reference): int {
if ($pos === $length) {
return 0;
}

switch ($serialized[$pos]) {
case 's':
return $parse_string();
case 'i':
return $parse_integer();
case 'd':
return $parse_double();
case 'b':
return $parse_boolean();
case 'N':
return $expect('N;');
case 'a':
return $parse_array();
case 'O':
return $parse_object();
case 'C':
return $parse_custom();
case 'r':
case 'R':
return $parse_reference();
default:
return -1;
}
};

$status = $parse_value();
return $status !== -1 && ( $status === 0 || $pos === $length );
}

/** @var string The original serialized data. */
private $data;

Expand Down
Loading
Loading