{
  "id": 7915608,
  "title": "A stored-XSS report we couldn't quite reproduce, and hardened anyway",
  "url": "https://urgent.news/2026/09/17/a-stored-xss-report-we-couldnt-quite-reproduce-and-hardened-anyway",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-09-17T00:45:34.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/hamzezn/a-stored-xss-report-we-couldnt-quite-reproduce-and-hardened-anyway-5f1i"
  },
  "original_language": "en",
  "account": "A researcher named Dhruv reached out to us with a report about a stored Cross-Site Scripting (XSS) vulnerability. The issue occurred when someone attached a PDF file with embedded JavaScript to a support ticket in our helpdesk product. When a support agent opened the ticket, the malicious script ran in the context of the application.\n\nUpon investigation, we discovered that our download route had been enforcing a Content-Disposition: attachment and application/octet-stream header since December 2025. We confirmed this by examining the git blame information for the relevant code. Our frontend attachment viewer did not contain any iframe, embed, or object elements, and every non-image attachment was downloaded as a Blob, rather than being rendered inline.\n\nOur validation process for uploads utilized python-magic, which analyzed the actual file bytes rather than trusting the browser's claimed content type. Furthermore, we had explicitly excluded image/svg+xml from our allow-list for this specific reason. The XSS path described by Dhruv likely could not be reproduced as written due to these security measures.\n\nHowever, this incident highlighted the importance of ensuring that our code remained secure even if the underlying code paths were to change. Therefore, we implemented an additional layer of security by re-detecting the real MIME type at download time using the same file bytes on disk, rather than relying on the type stored in our database. We determined whether the file was an image using FileStorageService.renders_inline_safely() and returned either the application/octet-stream media type with an inline Content-Type header for images, or the application/octet-stream media type with an attachment Content-Type header for all other file types.\n\nTo prevent any potential bypasses, we made sure that only allow-listed image types were served inline. Every PDF and any other file type that we couldn't explicitly recognize was forced to download, now with the nosniff and a sandboxed Content-Security-Policy header in place. This would prevent a browser from deciding to render the file inline even if some future code path attempted to do so directly.\n\nWe added four tests to cover this new logic and deployed the changes to production on the same day. The actual lesson here is that even a bug report that doesn't reproduce exactly as written can still be valuable. Dhruv's email pointed out a real gap in our security validation process, but not one that was immediately exploitable. Closing that gap took an afternoon of work, but the effort was worthwhile. The investigation, code changes, and security fixes were all our own.",
  "summary": "A researcher named Dhruv emailed us to report a stored XSS path: someone attaches a PDF with embedded JavaScript to a support ticket in our helpdesk product, and when a support agent opens it, the script runs \"in the context of the application.\" What we found when we checked The download route has forced Content-Disposition: attachment and application/octet-stream since December 2025 — confirmed…",
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}