From 623f03bb683dbc18f91645ac79eca80b529e60c3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 13 Sep 2026 19:55:37 +0930 Subject: [PATCH] Escape message in curated register details email to fix reflected XSS --- .../request-details-from-user.blade.php | 2 +- .../CuratedRegisterEmailEscapeTest.php | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/resources/views/emails/curated-register/request-details-from-user.blade.php b/resources/views/emails/curated-register/request-details-from-user.blade.php index 59c26c7cd..718fa2e66 100644 --- a/resources/views/emails/curated-register/request-details-from-user.blade.php +++ b/resources/views/emails/curated-register/request-details-from-user.blade.php @@ -8,7 +8,7 @@ To help us process your registration application, we require more information. Our onboarding team have requested the following details: @component('mail::panel') -

{!! $activity->message !!}

+

{!! nl2br(e($activity->message)) !!}

@endcomponent Reply with your response diff --git a/tests/Feature/CuratedRegisterEmailEscapeTest.php b/tests/Feature/CuratedRegisterEmailEscapeTest.php index ad2f467f2..fd1a2d98e 100644 --- a/tests/Feature/CuratedRegisterEmailEscapeTest.php +++ b/tests/Feature/CuratedRegisterEmailEscapeTest.php @@ -2,6 +2,7 @@ use App\Mail\CuratedRegisterNotifyAdmin; use App\Mail\CuratedRegisterNotifyAdminUserResponse; +use App\Mail\CuratedRegisterRequestDetailsFromUser; use App\Models\CuratedRegister; use App\Models\CuratedRegisterActivity; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; @@ -43,3 +44,27 @@ it('escapes user-provided content in the admin notification emails', function () expect($responseHtml)->not->toContain(''); expect($responseHtml)->toContain('<script>'); }); + +/* +| request-details-from-user is rendered as an HTTP response by the admin +| previewDetailsMessageShow endpoint, which reflects a GET `message` param into +| $activity->message. It must escape the message to prevent reflected XSS, the +| same way its sibling admin templates do. +*/ +it('escapes the message in the request-details-from-user mailable', function () { + $verify = new CuratedRegister; + $verify->username = 'attacker'; + $verify->email = 'attacker@example.com'; + $verify->save(); + + $activity = new CuratedRegisterActivity; + $activity->register_id = $verify->id; + $activity->message = ''; + $activity->save(); + + $detailsHtml = (new CuratedRegisterRequestDetailsFromUser($verify, $activity))->render(); + + // The raw tag and its onerror handler must not survive as live HTML. + expect($detailsHtml)->not->toContain('