CVE-2026-55791 is a maximum-severity (CVSS 10.0) vulnerability in Craft CMS, the popular PHP content management system. Craft’s /actions/app/resource-js endpoint trusts the incoming Host / X-Forwarded-Host header to build the site’s base URL, then uses it to fetch and re-serve JavaScript. Because Craft ships with trustedHosts set to ['any'] by default, an unauthenticated attacker can poison that header to make the server fetch a malicious script from an attacker-controlled domain and hand it back to the browser as valid JavaScript. Behind a caching layer, this becomes web cache poisoning: the next administrator to log in loads the attacker’s script (stored XSS), which steals the CSRF token and silently installs a plugin — a 1-click path to remote code execution. It’s fixed in Craft CMS 4.18.0 and 5.10.0. Upgrade now.

What the Vulnerability Is

The root cause is an origin-validation error (CWE-346) that chains three weaknesses together:

  • Permissive proxy trust. Craft’s default GeneralConfig::$trustedHosts is ['any']. That tells the underlying Yii2 framework to trust the Host / X-Forwarded-Host header on every request, even if your Nginx or Apache front end validated the real Host. An attacker who injects X-Forwarded-Host: attacker.tld gets Craft to set its global $baseUrl to their domain.
  • A validation check that trusts the poisoned value. In AppController::actionResourceJs(), Craft guards the remote fetch with str_starts_with($url, $baseUrl). But $baseUrl is already attacker-controlled, so the check passes for the attacker’s own URL. Craft then fetches it with a Guzzle HTTP client that, unlike other fetchers in the codebase, allows redirects.
  • Forced JavaScript content type. Whatever comes back from the attacker’s server is returned to the browser via asRaw() with Content-Type: application/javascript. The endpoint has effectively become an open proxy that serves attacker content as trusted first-party script.

The condition is reachable when assetManager.cacheSourcePaths is false. On its own, the flaw is a blind SSRF (useful for probing internal networks). But the devastating impact appears when a caching layer sits in front of Craft: the poisoned /actions/app/resource-js response gets cached, and when an authenticated administrator next opens the Control Panel, their browser executes the cached malicious JavaScript as stored XSS. That script reads window.Craft.csrfTokenValue and POSTs to /admin/actions/plugins/install-plugin, installing a plugin of the attacker’s choosing — remote code execution via session riding.

Why It Matters

  • CVSS 10.0, no authentication to start the chain. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — network vector, low complexity, no privileges, no user interaction, and a scope change from web app to server.
  • Insecure default does the work. trustedHosts => ['any'] is the out-of-the-box setting, so most installs are exposed without any misconfiguration on the operator’s part.
  • Caching layers amplify it. CDNs and reverse-proxy caches — standard on production sites — are exactly what turns a blind SSRF into a stored-XSS-to-RCE weapon that hits the highest-privilege user.
  • RCE means full server control. Installing an arbitrary plugin lets an attacker run PHP on the host, read secrets, and pivot.
  • Craft powers real production sites. It’s a mainstream PHP CMS used for business and agency sites; unpatched instances are attractive targets.

Am I Affected?

You are potentially exposed if all of these apply:

  • You run Craft CMS in the affected range: 4.0.0-RC1 up to (not including) 4.18.0, or 5.0.0-RC1 up to (not including) 5.10.0.
  • Your configuration leaves trustedHosts at the permissive default (['any']) — the shipped default.
  • assetManager.cacheSourcePaths is false.

The risk escalates from blind SSRF to full stored-XSS-to-RCE if a caching layer (CDN or reverse-proxy cache) sits in front of Craft, which is common in production. Check your Craft version in the Control Panel (Utilities → System Report) or via php craft version on the server.

What to Do About It: Step-by-Step

Step 1: Upgrade Craft to a fixed release — this is the real fix

Move to 4.18.0 (4.x branch) or 5.10.0 (5.x branch) or later:

composer require craftcms/cms:^5.10.0 --update-with-dependencies
php craft up

Use ^4.18.0 if you’re on the 4.x line. Run php craft migrate/all / php craft up after updating.

Step 2: Lock down trustedHosts regardless

Don’t rely on the default. In config/general.php, restrict trusted hosts to your real domain(s) instead of ['any']:

return [
    'trustedHosts' => ['^example\.com$', '^www\.example\.com$'],
];

This is defense-in-depth against this and future host-header attacks.

Step 3: Enforce the real Host at the edge

Configure Nginx/Apache to reject requests whose Host doesn’t match your site, and to strip or ignore attacker-supplied X-Forwarded-Host unless it comes from a trusted proxy. Example Nginx guard:

if ($host !~* ^(example\.com|www\.example\.com)$) { return 421; }
proxy_set_header X-Forwarded-Host $host;

Step 4: Purge your cache after patching

If a CDN or reverse-proxy cache fronts the site, purge it fully to evict any already-poisoned /actions/app/resource-js responses.

Step 5: Hunt for exploitation

Search web-server/access logs for requests to the resource endpoint carrying suspicious host headers, and for unexpected plugin installs:

grep -Ei "/actions/app/resource-js" /var/log/nginx/access.log | grep -i "x-forwarded-host"

Review Craft’s installed plugins list and the project.yaml / plugin config for anything you didn’t install.

Step 6: If you find signs of compromise, treat it as RCE

Rotate the Craft security key and all secrets, review admin users, reinstall from known-good code, and restore content from a clean backup taken before the earliest suspicious request.

Quick-Win Checklist

  • Confirmed the running Craft CMS version and branch.
  • Upgraded to Craft 4.18.0 / 5.10.0 (or later).
  • Set trustedHosts to an explicit allow-list instead of ['any'].
  • Enforced Host validation and controlled X-Forwarded-Host at the web-server/proxy edge.
  • Fully purged CDN / reverse-proxy caches after patching.
  • Reviewed logs for poisoned host headers and the installed-plugins list for rogue entries.
  • Rotated the security key and secrets if any compromise is suspected.

Sources