Use recursive stylesheet sanitation

62a1919402faa555c3d670d517fa8872be60a898

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

1 files changed, +42 -32Ignore whitespace
public/scripts/chats.js+42 -32
@@ -465,42 +465,52 @@ export function decodeStyleTags(text) {
465 const styleDecodeRegex = /<custom-style>(.+?)<\/custom-style>/gms;465 const styleDecodeRegex = /<custom-style>(.+?)<\/custom-style>/gms;
466 const mediaAllowed = isExternalMediaAllowed();466 const mediaAllowed = isExternalMediaAllowed();
467467
468 function sanitizeRule(rule) {
469 if (rule.selectors) {
470 for (let i = 0; i < rule.selectors.length; i++) {
471 let selector = rule.selectors[i];
472 if (selector) {
473 let selectors = (selector.split(' ') ?? []).map((v) => {
474 if (v.startsWith('.')) {
475 return '.custom-' + v.substring(1);
476 }
477 return v;
478 }).join(' ');
479
480 rule.selectors[i] = '.mes_text ' + selectors;
481 }
482 }
483 }
484 if (!mediaAllowed && Array.isArray(rule.declarations) && rule.declarations.length > 0) {
485 for (const declaration of rule.declarations) {
486 if (declaration.value.includes('://')) {
487 rule.declarations.splice(rule.declarations.indexOf(declaration), 1);
488 }
489 }
490 }
491 }
492
493 function sanitizeRuleSet(ruleSet) {
494 if (ruleSet.type === 'rule') {
495 sanitizeRule(ruleSet);
496 }
497
498 if (Array.isArray(ruleSet.rules)) {
499 ruleSet.rules = ruleSet.rules.filter(rule => rule.type !== 'import');
500
501 for (const mediaRule of ruleSet.rules) {
502 sanitizeRuleSet(mediaRule);
503 }
504 }
505 }
506
468 return text.replaceAll(styleDecodeRegex, (_, style) => {507 return text.replaceAll(styleDecodeRegex, (_, style) => {
469 try {508 try {
470 let styleCleaned = unescape(style).replaceAll(/<br\/>/g, '');509 let styleCleaned = unescape(style).replaceAll(/<br\/>/g, '');
471 const ast = css.parse(styleCleaned);510 const ast = css.parse(styleCleaned);
472 const rules = ast?.stylesheet?.rules;511 const sheet = ast?.stylesheet;
473 if (rules) {512 if (sheet) {
474 for (const rule of rules) {513 sanitizeRuleSet(ast.stylesheet);
475 if (rule.type === 'import') {
476 rules.splice(rules.indexOf(rule), 1);
477 }
478
479 if (rule.type === 'rule') {
480 if (rule.selectors) {
481 for (let i = 0; i < rule.selectors.length; i++) {
482 let selector = rule.selectors[i];
483 if (selector) {
484 let selectors = (selector.split(' ') ?? []).map((v) => {
485 if (v.startsWith('.')) {
486 return '.custom-' + v.substring(1);
487 }
488 return v;
489 }).join(' ');
490
491 rule.selectors[i] = '.mes_text ' + selectors;
492 }
493 }
494 }
495 if (!mediaAllowed && Array.isArray(rule.declarations) && rule.declarations.length > 0) {
496 for (const declaration of rule.declarations) {
497 if (declaration.value.includes('://')) {
498 rule.declarations.splice(rule.declarations.indexOf(declaration), 1);
499 }
500 }
501 }
502 }
503 }
504 }514 }
505 return `<style>${css.stringify(ast)}</style>`;515 return `<style>${css.stringify(ast)}</style>`;
506 } catch (error) {516 } catch (error) {