Skip to content

Harden dashboard welcome message against XSS#106

Open
sdebacker wants to merge 1 commit into
mainfrom
claude/hopeful-faraday-Je8NG
Open

Harden dashboard welcome message against XSS#106
sdebacker wants to merge 1 commit into
mainfrom
claude/hopeful-faraday-Je8NG

Conversation

@sdebacker

Copy link
Copy Markdown
Contributor

Summary

Second-pass security fix following the search SQL-injection fix (#104).

The admin dashboard renders the welcome message unescaped:

<div class="dashboard-body">{!! $welcomeMessage !!}</div>

The message is fetched from a remote URL (WELCOME_MESSAGE_URL) and was only filtered with strip_tags($body, '<a>...'). strip_tags() restricts tags but keeps their attributes, so the allowed tags could still carry:

  • event handlers — <a onmouseover=…>, <p onclick=…>
  • dangerous URIs — <a href="javascript:…">

If the configured upstream is compromised — or served over plain http and man-in-the-middled — this becomes stored/reflected XSS executing in the admin panel (full CMS takeover via an authenticated admin's session). The existing SSRF guard (isAllowedUrl) does not address the content of the response.

Fix

sanitizeHtml() now:

  1. Restricts tags with strip_tags (unchanged allowlist).
  2. Parses the result with DOMDocument and removes every attribute from every element.
  3. Restores only a safe href on <a> (http/https/mailto, or relative/anchor) — javascript:/data:/etc. are dropped.

DOM-based rebuilding (rather than regex) avoids attribute-parsing bypasses. Legitimate formatting and safe links are preserved; UTF-8 content is handled correctly.

Verification

Sanitizer logic exercised against representative inputs:

Input Output
<a href="https://x.com" onclick="alert(1)">link</a> <a href="https://x.com">link</a>
<a href="javascript:alert(1)">x</a> <a>x</a>
<a href="JaVaScRiPt:alert(1)">x</a> <a>x</a>
<a href="mailto:a@b.com">mail</a> <a href="mailto:a@b.com">mail</a>
<p onmouseover="alert(1)">hover</p> <p>hover</p>
Café résumé <em>ok</em> Café résumé <em>ok</em>

php -l passes (PHP 8.4). The package has no vendor/ in this environment (the suite runs against a host app), so the full test suite wasn't run here.

https://claude.ai/code/session_01EUUnydptJJw8Az5AAVQ1r3


Generated by Claude Code

The admin dashboard renders the remotely fetched welcome message unescaped
({!! $welcomeMessage !!}). It was only filtered with strip_tags(), which keeps
the attributes of allowed tags, so a compromised or man-in-the-middled upstream
could smuggle in event handlers (onclick, onerror, …) or javascript: URIs and
execute script in the admin context.

Parse the fetched HTML and strip every attribute, restoring only a safe href
(http/https/mailto, relative or anchor) on links.

https://claude.ai/code/session_01EUUnydptJJw8Az5AAVQ1r3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants