RubySec

Providing security resources for the Ruby community

GHSA-r827-6rm4-59pg (alchemy_cms): Stored XSS via unsanitized SVG attachment replacement

Stored XSS via unsanitized SVG attachment replacement

Published: July 28, 2026

SECURITY IDENTIFIERS

GEM

alchemy_cms

SEVERITY

CVSS v3.x: 8.7 (High)

PATCHED VERSIONS

>= 8.3.6

DESCRIPTION

Summary

AlchemyCMS registers its SVG sanitizer (SanitizeSvgJob, a Loofah-based scrubber) only as an after_create_commit callback on Alchemy::Attachment / Alchemy::Picture. This callback fires when a new attachment record is created, but not when an existing attachment's file is replaced through the admin "update" action. An authenticated user holding the editor role (i.e. manage Alchemy::Attachment permission, a low-privilege, non-admin role) can PATCH an existing attachment to replace its file with a malicious SVG containing <script> / onload= payloads. Because the sanitizer never runs on this path, and because AlchemyCMS explicitly configures SVG as an inline-servable content type on Active Storage, the public, unauthenticated /attachment/:id/show route streams the attacker's raw SVG payload with Content-Disposition: inline. Any visitor (including other admins) who opens the attachment URL executes attacker-controlled JavaScript in the AlchemyCMS application origin.

This is a stored, privilege-crossing Cross-Site Scripting vulnerability, confirmed both by static code review and by live dynamic reproduction against an unmodified AlchemyCMS instance.

RELATED