Improve sanitation of CSS pseudo-classes (#4230)

85f38542b6722307187ce2714b80cd626a5167a2

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +31 -8Ignore whitespace
public/scripts/chats.js+31 -8
@@ -499,14 +499,7 @@ export function decodeStyleTags(text, { prefix } = { prefix: '.mes_text ' }) {
499499 for (let i = 0; i < rule.selectors.length; i++) {
500500 const selector = rule.selectors[i];
501501 if (selector) {
502- const selectors = (selector.split(' ') ?? []).map((v) => {
502+ rule.selectors[i] = prefix + sanitizeSelector(selector);
503- if (v.startsWith('.')) {
504- return '.custom-' + v.substring(1);
505- }
506- return v;
507- }).join(' ');
508-
509- rule.selectors[i] = prefix + selectors;
510503 }
511504 }
512505 }
@@ -515,6 +508,36 @@ export function decodeStyleTags(text, { prefix } = { prefix: '.mes_text ' }) {
515508 }
516509 }
517510
511+ function sanitizeSelector(selector) {
512+ // Handle pseudo-classes that can contain nested selectors
513+ const pseudoClasses = ['has', 'not', 'where', 'is', 'matches', 'any'];
514+ const pseudoRegex = new RegExp(`:(${pseudoClasses.join('|')})\\(([^)]+)\\)`, 'g');
515+
516+ // First, sanitize any nested selectors within pseudo-classes
517+ selector = selector.replace(pseudoRegex, (match, pseudoClass, content) => {
518+ // Recursively sanitize the content within the pseudo-class
519+ const sanitizedContent = sanitizeSimpleSelector(content);
520+ return `:${pseudoClass}(${sanitizedContent})`;
521+ });
522+
523+ // Then sanitize the main selector parts
524+ return sanitizeSimpleSelector(selector);
525+ }
526+
527+ function sanitizeSimpleSelector(selector) {
528+ // Split by spaces but preserve complex selectors
529+ return selector.split(/\s+/).map((part) => {
530+ // Handle class selectors, but preserve pseudo-classes and other complex parts
531+ return part.replace(/\.([\w-]+)/g, (match, className) => {
532+ // Don't modify if it's already prefixed with 'custom-'
533+ if (className.startsWith('custom-')) {
534+ return match;
535+ }
536+ return `.custom-${className}`;
537+ });
538+ }).join(' ');
539+ }
540+
518541 function sanitizeRuleSet(ruleSet) {
519542 if (Array.isArray(ruleSet.selectors) || Array.isArray(ruleSet.declarations)) {
520543 sanitizeRule(ruleSet);