Skip to content

Commit bd93284

Browse files
committed
fix(xss): add DOMPurify to ReadMore.vue as defense-in-depth
Adds client-side HTML sanitization via DOMPurify to the ReadMore component, which renders group and event descriptions via v-html. Server-side Purify::clean() model mutators are the primary defense, but this provides a second layer in case any write path bypasses the model (raw DB queries, migrations, imports).
1 parent ef9ae01 commit bd93284

2 files changed

Lines changed: 11 additions & 3 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"bootstrap4-datetimepicker": "^5.2.3",
6060
"chart.js": "^2.7.2",
6161
"codemirror": "^5.39.2",
62+
"dompurify": "^3.4.0",
6263
"dropzone": "^5.7.2",
6364
"ekko-lightbox": "^5.3.0",
6465
"floatthead": "^2.1.2",

resources/js/components/ReadMore.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
<p v-html="formattedString"></p>
55
</span>
66
<span v-else-if="html">
7-
<span v-if="!needsTruncating" v-html="html" class="w-100" />
7+
<span v-if="!needsTruncating" v-html="sanitizedHtml" class="w-100" />
88
<span v-else>
9-
<span v-if="!isReadMore" v-html="truncatedHTML" class="w-100" />
10-
<span v-else v-html="html" class="w-100" />
9+
<span v-if="!isReadMore" v-html="sanitizedTruncatedHtml" class="w-100" />
10+
<span v-else v-html="sanitizedHtml" class="w-100" />
1111
</span>
1212
</span>
1313
<span v-if="needsTruncating">
@@ -20,6 +20,7 @@
2020
<script>
2121
const htmlToText = require('html-to-text');
2222
import clip from "text-clipper"
23+
import DOMPurify from "dompurify"
2324
// Originally based on https://github.com/orlyyani/read-more, with thanks.
2425
2526
export default {
@@ -70,12 +71,18 @@ export default {
7071
7172
return val_container;
7273
},
74+
sanitizedHtml() {
75+
return this.html ? DOMPurify.sanitize(this.html) : null
76+
},
7377
truncatedHTML() {
7478
// We need to truncate HTML with care to ensure that the result is tag safe; string truncation isn't good
7579
// enough.
7680
const ret = this.html ? clip(this.html, this.maxChars, { html: true, maxLines: this.maxLines }) : null
7781
return ret
7882
},
83+
sanitizedTruncatedHtml() {
84+
return this.truncatedHTML ? DOMPurify.sanitize(this.truncatedHTML) : null
85+
},
7986
needsTruncating() {
8087
if (this.text && (text.length > maxChars)) {
8188
return true

0 commit comments

Comments
 (0)