Search by

ceymox / module-style-smuggler-shield

Ceymox

Magento 2 / Adobe Commerce security module that blocks the StyleSmuggler GraphQL template-injection RCE.

Package info

github.com/Ceymox/StyleSmugglerShield

Type:magento2-module

pkg:composer/ceymox/module-style-smuggler-shield

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.3.0 2026-09-15 20:20 UTC

This package is auto-updated.

Last update: 2026-09-15 20:20:36 UTC


README

Ceymox_StyleSmugglerShield is a free Magento 2 / Adobe Commerce security module. It started as a fix for StyleSmuggler, a GraphQL template-injection vulnerability disclosed by Sansec and actively exploited in the wild since September 4, 2026, and has grown to cover a few other unpatched or partially-patched Magento vulnerabilities using the same plugin-based approach.

Adobe released an official fix on September 7, 2026 — CVE-2026-75650 / APSB26-146, CVSS 10.0, patch identifier VULN-39341. Apply that patch first — it's the real fix. This module remains useful as defense-in-depth and for stores that haven't applied VULN-39341 yet.

The vulnerability

Attackers send a GraphQL request whose styles input value smuggles Magento template directive syntax ({{block ...}}, {{template ...}}, {{layout ...}}) past existing input validation. The payload sits dormant until Magento renders the Payment Transaction Failed Reminder transactional email — a notification the malware deliberately triggers — at which point the directive executes with full PHP code execution on the server.

Adobe's fix is tracked as CVE-2026-75650 (APSB26-146), patch VULN-39341, released September 7, 2026.

What this module does

Two independent, non-breaking protection layers, kept because neither is made redundant by VULN-39341:

  1. GraphQL request guard — inspects every GraphQL query and its variables (including HTML-entity and URL-encoded obfuscation) for Magento directive syntax and rejects the request with an HTTP 400 before it reaches a resolver or gets persisted.
  2. DI compiler scanner CLI guard — blocks Magento's setup:di:compile scanner classes (ArrayScanner, ClassesScanner, XmlInterceptorScanner, and the rest of ScannerInterface) from running outside a CLI process. These classes include/ require_once arbitrary file paths with no execution-context check of their own, and have no legitimate reason to run during a web request — closing this off is independent, root-cause hardening regardless of how a payload reaches this point.

Both layers are logged to var/log/style_smuggler_shield.log (payload + IP where applicable) and are individually toggleable from the admin panel.

Plus two standalone GraphQL access-control features, unrelated to StyleSmuggler specifically:

  • GraphQL kill switch — an independent toggle that rejects every request to /graphql with HTTP 403, regardless of content. For stores where nothing storefront-side depends on GraphQL (no PWA Studio, no headless frontend, no mobile app), this removes the entire attack surface outright. Off by default, and separate from the master switch above since it's a blunt access-control decision, not a threat-detection layer.
  • Per-operation GraphQL blocklist — a finer-grained alternative to the kill switch. List specific top-level query/mutation field names (e.g. createCustomer, generateCustomerToken) and only those get rejected with HTTP 403; everything else on the endpoint keeps working. Parses the query with the same webonyx/graphql-php AST Magento itself uses, not regex, so it isn't fooled by aliases, comments, or formatting.

Other vulnerabilities this module hardens

Separate from StyleSmuggler, using the same plugin-only approach:

  • PolyShell (sansec.io/research/magento-polyshell) — a polyglot-file RCE via product custom-option image uploads. Magento\Framework\Api\ImageContentValidator checks that the declared MIME type matches the decoded image content, but never checks the filename's own extension — a file named shell.php with a valid GIF header and image/gif MIME type passes validation unchanged and is saved to disk under that exact name. This module adds an extension allowlist (jpg, jpeg, png, gif, bmp) on top of the existing check. No official patch exists for 2.4.7/2.4.8 — the fix only landed in 2.4.9.
  • SessionReaper / CVE-2025-54236 (sansec.io/research/sessionreaper) — partial coverage only. The actual RCE is a PHP object deserialization bug combined with file-based session storage; Adobe's official patch (APSB25-88) fixed that. What Adobe's patch explicitly did not fix is that Magento\Customer\Controller\Address\File\Upload performs no authentication check of its own, so any anonymous visitor can use it as a file-drop — Sansec's own write-up confirms this stays open post-patch. This module requires an active customer session before that controller runs. It does not address the deserialization bug itself; apply APSB25-88 for that.

Installation

Composer (recommended)

Published on Packagist:

composer require ceymox/module-style-smuggler-shield
php bin/magento module:enable Ceymox_StyleSmugglerShield
php bin/magento setup:upgrade
php bin/magento cache:flush

Manual

cd <magento-root>
mkdir -p app/code/Ceymox
git clone git@github.com:Ceymox/StyleSmugglerShield.git app/code/Ceymox/StyleSmugglerShield
php bin/magento module:enable Ceymox_StyleSmugglerShield
php bin/magento setup:upgrade
php bin/magento cache:flush

Configuration

Admin Panel → Stores → Configuration → General → Security → StyleSmuggler Protection

Setting Default
Enable Protection Yes
Reject GraphQL Requests Containing Template Directive Syntax Yes
Restrict DI Compiler Scanners to CLI Only Yes
Disable GraphQL Endpoint No
Blocked GraphQL Operations (empty)

Admin Panel → Stores → Configuration → General → Security → Magento Security Hardening (Ceymox)

Setting Default
Block Non-Image File Extensions in Image Uploads (PolyShell) Yes
Require Customer Login for Address File Upload (SessionReaper) Yes

In production deployment mode, run bin/magento setup:di:compile after installing or updating this module so the new plugins are baked into the generated interceptor classes.

Compatibility

  • Magento Open Source / Adobe Commerce 2.4.7 – 2.4.9
  • PHP 8.1 – 8.3

Why keep this module after applying VULN-39341

Apply Adobe's patch first — it fixes the actual source. But there's still a reason to leave this module running afterward:

  • Layer 1 is broader than the patch. VULN-39341 closes the specific vectors Adobe found (setTemplateStyles, BlockFactory, etc.). The GraphQL guard blocks any directive syntax reaching GraphQL, regardless of which field carries it — coverage for a variant nobody's found yet.
  • Layer 2 isn't about this CVE at all. Restricting DI compiler classes to CLI-only is sound hardening on its own merits, independent of whether this specific bug is patched.
  • Zero measurable cost. Negligible overhead, no conflicts with the official patch (verified running together), fully toggleable per-layer if you disagree.

An earlier third layer (an email template variable sanitizer) was removed once VULN-39341 shipped — setTemplateStyles/setTemplateText now do the equivalent job more precisely at the source, so keeping a duplicate, cruder version around added no protection, only surface area.

Disclaimer

This is a virtual patch, not a substitute for Adobe's official fix (VULN-39341 / CVE-2026-75650). Apply that patch — it corrects the vulnerable source directly, which this module does not do.

Applying the patch does not clean a store that was already compromised during the exploitation window (September 4–7, 2026). Given active exploitation in the wild, also scan for existing backdoors (e.g. with Sansec's eComscan) and review admin users and cron jobs for signs of prior compromise — this module does not do either of those.

Credits

Vulnerability research and disclosure: Sansec (StyleSmuggler, PolyShell, SessionReaper)

License

Copyright © Ceymox. All rights reserved.