From 616d3c32467f21e0916eff841b73bdfd6dbc4839 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 25 Aug 2025 02:36:17 +0100 Subject: [PATCH] [BUGFIX] Improve selector validation performance Avoid [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html) in selector validation regular expression by using possessive quantifier with mutually exclusive alternations. Also remove outdated description from DocBlock, but add description for extended class summarizing differences. --- CHANGELOG.md | 2 ++ src/Property/KeyframeSelector.php | 44 ++++++++++++++++++++++--------- src/Property/Selector.php | 29 +++++++++++++++----- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ef217d7b..a60c8981e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ Please also have a look at our ### Fixed +- Improve performance of selector validation + (avoiding silent PCRE catastrophic failure) (#1372) - Use typesafe versions of PHP functions (#1368, #1370) ### Documentation diff --git a/src/Property/KeyframeSelector.php b/src/Property/KeyframeSelector.php index 2ab8ca977..47881771d 100644 --- a/src/Property/KeyframeSelector.php +++ b/src/Property/KeyframeSelector.php @@ -7,21 +7,41 @@ class KeyframeSelector extends Selector { /** - * regexp for specificity calculations + * This differs from the parent class: + * - comma is not allowed unless escaped or quoted; + * - percentage value is allowed by itself. * - * @var string + * @var non-empty-string * * @internal since 8.5.2 */ public const SELECTOR_VALIDATION_RX = '/ - ^( - (?: - [a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*="\'~\\[\\]()\\-\\s\\.:#+>]* # any sequence of valid unescaped characters - (?:\\\\.)? # a single escaped character - (?:([\'"]).*?(?]++ + | + # one or more escaped characters + (?:\\\\.)++ + | + # quoted text, like in `[id="example"]` + (?: + # opening quote + ([\'"]) + (?: + # sequence of characters except closing quote or backslash + (?:(?!\\g{-1}|\\\\).)++ + | + # one or more escaped characters + (?:\\\\.)++ + )*+ # zero or more times + # closing quote or end (unmatched quote is currently allowed) + (?:\\g{-1}|$) + ) + )*+ # zero or more times + | + # keyframe animation progress percentage (e.g. 50%), untrimmed + \\s*+(\\d++%)\\s*+ + )$ + /ux'; } diff --git a/src/Property/Selector.php b/src/Property/Selector.php index daeed8502..df8d0e906 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -15,19 +15,34 @@ class Selector implements Renderable { /** - * regexp for specificity calculations - * - * @var string + * @var non-empty-string * * @internal since 8.5.2 */ public const SELECTOR_VALIDATION_RX = '/ ^( (?: - [a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*="\'~\\[\\]()\\-\\s\\.:#+>,]* # any sequence of valid unescaped characters - (?:\\\\.)? # a single escaped character - (?:([\'"]).*?(?,]++ + | + # one or more escaped characters + (?:\\\\.)++ + | + # quoted text, like in `[id="example"]` + (?: + # opening quote + ([\'"]) + (?: + # sequence of characters except closing quote or backslash + (?:(?!\\g{-1}|\\\\).)++ + | + # one or more escaped characters + (?:\\\\.)++ + )*+ # zero or more times + # closing quote or end (unmatched quote is currently allowed) + (?:\\g{-1}|$) + ) + )*+ # zero or more times )$ /ux';