From f00686021ad3cc3cc6b05f2ebfaedcaaa9cf6c74 Mon Sep 17 00:00:00 2001 From: Angel de la Torre Date: Mon, 27 Apr 2026 13:55:35 -0700 Subject: [PATCH 1/7] fix(model): add Purify mutator to Alert model Alert HTML content was stored without sanitization, enabling stored XSS if an attacker could create alerts (e.g. via privilege escalation). The setHtmlAttribute mutator ensures all HTML is cleaned through HTMLPurifier before persistence, matching the pattern already established on Group, Party, User, and Network models. --- app/Models/Alert.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Models/Alert.php b/app/Models/Alert.php index d4c15b6956..9ad283465a 100644 --- a/app/Models/Alert.php +++ b/app/Models/Alert.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\Log; use OwenIt\Auditing\Contracts\Auditable; +use Stevebauman\Purify\Facades\Purify; class Alert extends Model implements Auditable { @@ -32,4 +33,9 @@ class Alert extends Model implements Auditable 'end', 'variant' ]; + + public function setHtmlAttribute($value) + { + $this->attributes['html'] = $value === null ? null : Purify::clean((string) $value); + } } From 14b57c9a5f8b19e00f45b14ed6dbb6a810222f6b Mon Sep 17 00:00:00 2001 From: Angel de la Torre Date: Mon, 27 Apr 2026 13:56:09 -0700 Subject: [PATCH 2/7] fix(vue): add DOMPurify sanitization to AlertBanner AlertBanner rendered alert HTML via v-html without client-side sanitization. This adds DOMPurify as a defense-in-depth layer alongside the server-side Purify mutator on the Alert model, matching the pattern used in ReadMore.vue. --- resources/js/components/AlertBanner.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/js/components/AlertBanner.vue b/resources/js/components/AlertBanner.vue index 16096db683..6bfebcb9ca 100644 --- a/resources/js/components/AlertBanner.vue +++ b/resources/js/components/AlertBanner.vue @@ -10,7 +10,7 @@ {{ alert.title }} -
+
@@ -24,6 +24,7 @@