fix: guard against parser hang on selectors with unbalanced parentheses#603
Conversation
Skip selectors with unbalanced parentheses before passing them to slick's parser. Mensch incorrectly splits pseudo-class selectors like :is(.a, .b) at the comma, producing fragments like ':is(.a' with unclosed parentheses. These malformed selectors cause catastrophic backtracking in slick, hanging indefinitely with 100% CPU usage. The guard checks parenthesis depth in O(n) and returns an empty parse result for malformed selectors, matching the existing error handling behavior. Fixes #587
This pull request was closed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Prevents juice from hanging indefinitely when CSS contains modern pseudo-class selectors like
:is()with comma-separated arguments.Problem
Mensch (the CSS parser) incorrectly splits
:is(.a, .b)at the comma into two fragments::is(.a(unclosed parenthesis).b)(unmatched close)When slick's parser receives the malformed
:is(.afragment, it enters catastrophic backtracking and hangs indefinitely with 100% CPU usage.Fix
Added a
hasBalancedParentheses()guard inlib/selector.jsthat checks for unbalanced parentheses before passing selectors to slick. Selectors with unbalanced parens return an empty parse result (same as the existing error handling for invalid selectors).Test results
:is()pseudo-class with comma-separated selectors #587 no longer hangs (completes instantly)Fixes #587