security: fix unsafe html inputs (#1173)

* use DomPurify to sanatize ingredient html

* fix list-item render for markdown

* address volar linter issue
This commit is contained in:
Hayden 2022-04-24 13:00:04 -08:00 committed by GitHub
parent 1c41ce7538
commit 2613420cd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 6 deletions

View File

@ -173,8 +173,8 @@
</v-card-text> </v-card-text>
<v-expand-transition> <v-expand-transition>
<div v-show="!isChecked(index) && !edit" class="m-0 p-0"> <div v-show="!isChecked(index) && !edit" class="m-0 p-0">
<v-card-text> <v-card-text class="markdown">
<VueMarkdown :source="step.text"> </VueMarkdown> <VueMarkdown class="markdown" :source="step.text"> </VueMarkdown>
</v-card-text> </v-card-text>
</div> </div>
</v-expand-transition> </v-expand-transition>
@ -506,8 +506,19 @@ export default defineComponent({
}); });
</script> </script>
<style scoped> <style lang="css" scoped>
.v-card--link:before { .v-card--link:before {
background: none; background: none;
} }
/** Select all li under .markdown class */
.markdown >>> ul > li {
display: list-item;
list-style-type: disc !important;
}
/** Select all li under .markdown class */
.markdown >>> ol > li {
display: list-item;
}
</style> </style>

View File

@ -1,8 +1,16 @@
// @ts-ignore DOMPurify has no types
import DOMPurify from "dompurify";
import { useFraction } from "./use-fraction"; import { useFraction } from "./use-fraction";
import { RecipeIngredient } from "~/types/api-types/recipe"; import { RecipeIngredient } from "~/types/api-types/recipe";
const { frac } = useFraction(); const { frac } = useFraction();
function sanitizeIngredientHTML(rawHtml: string) {
return DOMPurify.sanitize(rawHtml, {
"USE_PROFILES": {html: true},
"ALLOWED_TAGS": ["b", "q", "i", "strong", "sup"]
}) as string
}
export function parseIngredientText(ingredient: RecipeIngredient, disableAmount: boolean, scale = 1): string { export function parseIngredientText(ingredient: RecipeIngredient, disableAmount: boolean, scale = 1): string {
if (disableAmount) { if (disableAmount) {
return ingredient.note || ""; return ingredient.note || "";
@ -26,5 +34,6 @@ export function parseIngredientText(ingredient: RecipeIngredient, disableAmount:
} }
} }
return `${returnQty} ${unit?.name || " "} ${food?.name || " "} ${note || " "}`.replace(/ {2,}/g, " "); const text = `${returnQty} ${unit?.name || " "} ${food?.name || " "} ${note || " "}`.replace(/ {2,}/g, " ");
return sanitizeIngredientHTML(text);
} }

View File

@ -25,6 +25,7 @@
"@vueuse/core": "^6.8.0", "@vueuse/core": "^6.8.0",
"core-js": "^3.15.1", "core-js": "^3.15.1",
"date-fns": "^2.23.0", "date-fns": "^2.23.0",
"dompurify": "^2.3.6",
"fuse.js": "^6.5.3", "fuse.js": "^6.5.3",
"nuxt": "^2.15.8", "nuxt": "^2.15.8",
"v-jsoneditor": "^1.4.5", "v-jsoneditor": "^1.4.5",
@ -55,4 +56,4 @@
"resolutions": { "resolutions": {
"vite": "2.3.8" "vite": "2.3.8"
} }
} }

View File

@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"jsx": "preserve",
"target": "ES2018", "target": "ES2018",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Node",

View File

@ -4898,6 +4898,11 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.0:
dependencies: dependencies:
domelementtype "^2.2.0" domelementtype "^2.2.0"
dompurify@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875"
integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==
domutils@^1.7.0: domutils@^1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"