From adcc822c9af2201efad4bf37bbf79e3c4d7d8928 Mon Sep 17 00:00:00 2001 From: LanQin_ Date: Wed, 24 Jun 2026 14:19:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(mail):=20=E4=BC=98=E5=8C=96=E9=82=AE?= =?UTF-8?q?=E4=BB=B6=20HTML=20=E4=B8=8E=E6=A0=B7=E5=BC=8F=E4=BF=9D?= =?UTF-8?q?=E7=95=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 支持整页邮件文档的清洗与渲染,保留 ``、``、`` 和 ` +
badhello
-
`) - for _, want := range []string{"class=\"card\"", "max-width: 600px", "margin: 0 auto", "background: linear-gradient", "box-shadow:", "cellpadding=\"0\"", "cellspacing=\"0\"", "align=\"center\"", "text-align: center"} { + `) + for _, want := range []string{"

ok

`) + if strings.Contains(blockedOut, "]*)>(.*?)`) + +func extractSafeEmailStyles(value string) ([]string, string) { + styles := []string{} + withoutStyles := emailStyleTagRe.ReplaceAllStringFunc(value, func(tag string) string { + match := emailStyleTagRe.FindStringSubmatch(tag) + if len(match) != 3 { + return "" + } + attrs, css := match[1], strings.TrimSpace(match[2]) + if !safeEmailStyleAttrs(attrs) || !safeEmailCSSBlock(css) { + return "" + } + styles = append(styles, ``) + return "" + }) + return styles, withoutStyles +} + +func safeEmailStyleAttrs(attrs string) bool { + attrs = strings.ToLower(strings.TrimSpace(attrs)) + if attrs == "" { + return true + } + return regexp.MustCompile(`^\s*type\s*=\s*["']?text/css["']?\s*$`).MatchString(attrs) +} + +func safeEmailCSSBlock(value string) bool { + value = strings.ToLower(strings.TrimSpace(value)) + if value == "" || len(value) > 50000 { + return false + } + unsafe := []string{"expression", "javascript:", "vbscript:", "data:", "behavior", "-moz-binding", "@import", "${escapeHtml(bodyText || "")}` - const sanitizedBody = DOMPurify.sanitize(rawBody, { - ADD_ATTR: ["style", "align", "valign", "bgcolor", "border", "cellpadding", "cellspacing", "width", "height"], - ADD_TAGS: ["center"], + const sanitized = DOMPurify.sanitize(rawBody, { + ADD_ATTR: ["style", "type", "align", "valign", "bgcolor", "border", "cellpadding", "cellspacing", "width", "height"], + ADD_TAGS: ["html", "head", "body", "style", "center", "font"], + WHOLE_DOCUMENT: /]/i.test(rawBody) || /]/i.test(rawBody), }) + if (/]/i.test(sanitized) || /]/i.test(sanitized)) { + const hasHead = /]/i.test(sanitized) + const withBase = hasHead + ? sanitized.replace(/]*)>/i, `${mailFrameBaseStyle()}`) + : sanitized.replace(/]*)>/i, `${mailFrameBaseStyle()}`) + return /${withBase}` + } return ` - - -${sanitizedBody} -` +` } function sanitizeComposerHtml(value: string) { return DOMPurify.sanitize(value || "") diff --git a/apps/web/src/types.d.ts b/apps/web/src/types.d.ts index bad997b..543c9ec 100644 --- a/apps/web/src/types.d.ts +++ b/apps/web/src/types.d.ts @@ -2,6 +2,7 @@ declare module "dompurify" { type SanitizeConfig = { ADD_ATTR?: string[] ADD_TAGS?: string[] + WHOLE_DOCUMENT?: boolean } const DOMPurify: { sanitize: (source: string, config?: SanitizeConfig) => string } export default DOMPurify