fix: harden recipe content against stored XSS (chips, instructions, asset media) (#7719)

This commit is contained in:
Hayden
2026-05-31 11:14:16 -05:00
committed by GitHub
parent 48752bcd06
commit 2d8b74282a
15 changed files with 362 additions and 49 deletions

View File

@@ -4,12 +4,8 @@
</template>
<script setup lang="ts">
import DOMPurify from "isomorphic-dompurify";
import { marked } from "marked";
enum DOMPurifyHook {
UponSanitizeAttribute = "uponSanitizeAttribute",
}
import { sanitizeMarkdownHtml } from "~/lib/sanitize/markdown";
const props = defineProps({
source: {
@@ -18,48 +14,11 @@ const props = defineProps({
},
});
const ALLOWED_STYLE_TAGS = [
"background-color", "color", "font-style", "font-weight", "text-decoration", "text-align",
];
function sanitizeMarkdown(rawHtml: string | null | undefined): string {
if (!rawHtml) {
return "";
}
DOMPurify.addHook(DOMPurifyHook.UponSanitizeAttribute, (node, data) => {
if (data.attrName === "style") {
const styles = data.attrValue.split(";").filter((style) => {
const [property] = style.split(":");
return ALLOWED_STYLE_TAGS.includes(property.trim().toLowerCase());
});
data.attrValue = styles.join(";");
}
});
const sanitized = DOMPurify.sanitize(rawHtml, {
ALLOWED_TAGS: [
"strong", "em", "b", "i", "u", "p", "code", "pre", "samp", "kbd", "var", "sub", "sup", "dfn", "cite",
"small", "address", "hr", "br", "id", "div", "span", "h1", "h2", "h3", "h4", "h5", "h6",
"ul", "ol", "li", "dl", "dt", "dd", "abbr", "a", "img", "blockquote", "iframe",
"del", "ins", "table", "thead", "tbody", "tfoot", "tr", "th", "td", "colgroup",
],
ALLOWED_ATTR: [
"href", "src", "alt", "height", "width", "class", "allow", "title", "allowfullscreen", "frameborder",
"scrolling", "cite", "datetime", "name", "abbr", "target", "border", "start", "style",
],
});
Object.values(DOMPurifyHook).forEach((hook) => {
DOMPurify.removeHook(hook);
});
return sanitized;
}
const { $appInfo } = useNuxtApp();
const value = computed(() => {
const rawHtml = marked.parse(props.source || "", { async: false, breaks: true });
return sanitizeMarkdown(rawHtml);
return sanitizeMarkdownHtml(rawHtml, $appInfo?.allowedIframeHosts ?? []);
});
</script>