Improve sanitation of CSS pseudo-classes (#4230)

85f38542b6722307187ce2714b80cd626a5167a2

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

Signed
1 files changed, +31 -8Showing whitespace changes
public/scripts/chats.js+31 -8
@@ -499,14 +499,7 @@ export function decodeStyleTags(text, { prefix } = { prefix: '.mes_text ' }) {
499 for (let i = 0; i < rule.selectors.length; i++) {499 for (let i = 0; i < rule.selectors.length; i++) {
500 const selector = rule.selectors[i];500 const selector = rule.selectors[i];
501 if (selector) {501 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;
510 }503 }
511 }504 }
512 }505 }
@@ -515,6 +508,36 @@ export function decodeStyleTags(text, { prefix } = { prefix: '.mes_text ' }) {
515 }508 }
516 }509 }
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
518 function sanitizeRuleSet(ruleSet) {541 function sanitizeRuleSet(ruleSet) {
519 if (Array.isArray(ruleSet.selectors) || Array.isArray(ruleSet.declarations)) {542 if (Array.isArray(ruleSet.selectors) || Array.isArray(ruleSet.declarations)) {
520 sanitizeRule(ruleSet);543 sanitizeRule(ruleSet);