Automattic / juice

Juice inlines CSS stylesheets into your HTML source.
MIT License
3.1k stars 220 forks source link

Infinite loop when parsing certain CSS rules #471

Open jimrandomh opened 1 year ago

jimrandomh commented 1 year ago

If the provided CSS contains a rule with a pseudoselector with contents that span multiple lines, juice (via the parsing library slick) will go into an infinite loop, crashing the process. This happens even if the rule is in the stylesheet but the corresponding classes are never used. In my case, I was applying a merged stylesheet that contained the styles from react-datepicker, which includes this rule:

.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range,
.react-datepicker__month-text--in-range,
.react-datepicker__quarter-text--in-range,
.react-datepicker__year-text--in-range),
.react-datepicker__month-text--in-selecting-range:not(.react-datepicker__day--in-range,
.react-datepicker__month-text--in-range,
.react-datepicker__quarter-text--in-range,
.react-datepicker__year-text--in-range),
.react-datepicker__quarter-text--in-selecting-range:not(.react-datepicker__day--in-range,
.react-datepicker__month-text--in-range,
.react-datepicker__quarter-text--in-range,
.react-datepicker__year-text--in-range),
.react-datepicker__year-text--in-selecting-range:not(.react-datepicker__day--in-range,
.react-datepicker__month-text--in-range,
.react-datepicker__quarter-text--in-range,
.react-datepicker__year-text--in-range) {
  background-color: rgba(33, 107, 165, 0.5);
}

handleRule in inline.js assumes that the first line is a selector, and the first line, .react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range, (note the mismatched parenthesis and trailing comma) winds up getting passed to slick.parse, which crashes (by infinite looping) here.

I believe there are two bugs here, one in juice and one in slick. The bug in juice is that the newlines in the selector aren't handled correctly, before passing it to slick. The bug in slick is that it goes into a infinite loop, rather than returning an error or throwing an exception. Unfortunately it looks like slick has been archived and isn't likely to receive future updates, so is likely to be hard to get fixed.