Feb 11, 2026·6 min read·64 visits
Low-privileged editors can name an article with a malicious XSS payload. When a Super Admin opens the Command Palette (Cmd+K) and searches for it, the script runs. This grants the attacker full admin access.
A critical Stored Cross-Site Scripting (XSS) vulnerability in Statamic CMS allows authenticated content editors to inject malicious JavaScript into the global Command Palette. When a Super Admin searches for content, the payload executes, leading to potential account takeover and Remote Code Execution (RCE).
Statamic is the darling of the Laravel world. It's a flat-file CMS that runs on Git, meaning your content is version-controlled, your database is nonexistent, and your developer experience is usually buttery smooth. One of the sleekest features in the Statamic Control Panel is the Command Palette. You know the drill: hit Cmd+K (or Ctrl+K), type a few letters, and navigate anywhere instantly. It’s the productivity hack we all love.
But here’s the thing about centralized search tools: they aggregate data from everywhere. In a CMS, that means they pull in page titles, user names, collection entries, and navigation labels. If the mechanism that displays those search results gets a little too trusting with the data it retrieves, you have a problem.
In CVE-2026-25759, that trust was misplaced. The Command Palette didn't just display titles; it executed them. This turns a helpful navigation tool into a loaded gun pointing directly at your Super Admin's session cookies. It’s a classic case of "Cool Feature, Bro, Did You Secure It?"
The vulnerability lies in how the Command Palette handles "highlighting." When you search for "Blog", a good UI highlights the word "Blog" in the results so you know why it matched. To do this, Statamic uses a library called fuzzysort.
fuzzysort is a great library. It takes a string, finds the match, and wraps the matching characters in HTML tags, usually <span> or <b>. For example, searching "vuln" in "vulnerability" might produce <span>vuln</span>erability.
Here is the catch-22: To display that highlighting in the browser, the application must render the result as raw HTML. If it rendered it as plain text, you’d see the literal <span> tags on screen, which looks broken.
Statamic's developers fell into the trap. They took the raw content title from the database and fed it into fuzzysort. fuzzysort added the highlight tags, and Vue.js rendered the whole string as HTML. They forgot one crucial step: Escaping the original string first.
Because the input wasn't sanitized before highlighting, if an attacker names a blog post <h1>Hello</h1>, the Command Palette renders a giant header. If they name it <script>..., well, you know what happens next.
Let's look at the crime scene in resources/js/components/command-palette/CommandPalette.vue. The vulnerability existed in a mapping function that processed search results before display.
The Vulnerable Code:
// In Statamic < 6.2.3
results.map(result => {
return {
// ...
// result[0] is the raw, unsanitized title from the content file.
// It gets passed directly to highlight().
html: result[0].highlight(`<span class="${highlightClasses}">`, '</span>'),
// ...
};
});Use your hacker eyes. result[0] comes from user input. highlight adds some HTML classes. The result is assigned to a property called html. Later in the template, this is likely rendered via v-html.
The Fix (Commit 6ed4f65):
The fix is simple but critical. The developer introduced a sanitization step using escapeHtml before asking fuzzysort to do its magic.
// In Statamic >= 6.2.3
import { escapeHtml } from '@/bootstrap/globals.js';
function highlightResult(text) {
// STEP 1: Neutralize the threat.
const safeText = escapeHtml(text);
// STEP 2: Now it's safe to perform highlighting.
const result = fuzzysort.single(query.value, safeText);
// STEP 3: Return the safe string with added highlight tags.
return result?.highlight(`<span class="${classes}">`, '</span>') || safeText;
}By escaping the HTML first, an input like <script> becomes <script>. fuzzysort can still highlight the word "script", but the browser will render it as text, not code.
To exploit this, we don't need fancy tools. We just need a lower-privileged account, like an Editor who can create blog posts but can't change system settings or add users.
Step 1: The Setup
Log in as the Editor. Create a new Entry (page, article, whatever). For the Title, enter the following payload:
User Policy <img src=x onerror="fetch('/cp/users', {method:'POST', body: new FormData().append('email', 'hacker@evil.com')...})">(Note: In a real attack, you'd likely use a cleaner payload that loads an external JS file to keep the title looking somewhat normal, or obfuscate it.)
Step 2: The Trap
The payload is now stored in the content files (or database). It sits there, waiting. It's a landmine.
Step 3: The Trigger
The Super Admin logs in. Maybe they are looking for that "User Policy" page to edit it. They hit Cmd+K. As they type "User", the Command Palette fetches our malicious entry.
Because the palette renders results as you type, the Admin doesn't even need to click the link. The moment the result appears in the dropdown list, the <img> tag is rendered, the onerror event fires, and the JavaScript executes in the context of the Super Admin.
Step 4: The Prestige
The script silently sends a POST request to the Statamic API creating a new Super Admin account named "SystemUpdater" with the password "Password123!". The victim notices nothing, perhaps a slight UI flicker. You now own the CMS.
In the world of CMS security, XSS in the Control Panel is often treated as a "Severity: High" issue, but in reality, it is usually "Severity: Critical/Game Over."
Statamic allows Super Admins to edit Antlers templates (the view layer) and often manage files directly. If I can execute JavaScript as a Super Admin, I can:
.env file via the file manager to get AWS keys, database credentials, and API secrets.This isn't just a pop-up alert box; it's a full compromise of the web server.
The remediation is straightforward: Update to Statamic v6.2.3 immediately.
If you cannot update (perhaps you are locked on a legacy version for compatibility reasons), you are in a tough spot. You could theoretically attempt to patch the CommandPalette.vue file manually and recompile the assets, but that breaks the upgrade path and checksums.
Defensive Advice for Developers:
v-html (or dangerouslySetInnerHTML): If you must use it, ensure the data is sanitized immediately before use. Do not assume data from the database is safe.<script> tags). The correct place to fix this is strictly in the display logic of the Command Palette.unsafe-inline scripts, the attacker's payload might fail to execute. However, getting a CMS control panel to run with a strict CSP is often a project in itself.CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Statamic CMS Statamic | >= 6.0.0, < 6.2.3 | 6.2.3 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-25759 |
| CVSS v3.1 | 8.7 (High) |
| CWE | CWE-79 (Cross-site Scripting) |
| Attack Vector | Network (Stored) |
| Privileges Required | Low (Editor) |
| User Interaction | Required (Search Trigger) |
| Patch Status | Fixed in v6.2.3 |
The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVE-2026-116676 (GHSA-rjh2-4xgw-p2hj) is an OS command injection vulnerability (CWE-78) in Atlantis, an open-source Go application that automates Terraform pull request workflows via webhooks. From version 0.4.0 prior to version 0.47.1, Atlantis constructed Terraform and Conftest commands by joining argument slices into a single space-separated string and passing them to a shell interpreter (`sh -c`). Because user-controlled workspace names from `atlantis.yaml`, pull request comments, HCL configurations, and API requests were insufficiently validated against shell metacharacters, an authenticated contributor eligible to trigger a Terraform plan can execute arbitrary commands under the Atlantis service account.
CVE-2026-116679 (GHSA-5cp7-fp4j-wwcv) is an incorrect authorization vulnerability (CWE-863) in m1k1o/neko, a self-hosted virtual browser running inside Docker via WebRTC. In versions from 3.0.0 prior to 3.1.5, a boolean logic inversion in the LegacyIsHost() method within server/internal/session/session.go causes the server to unconditionally grant host input privileges over the legacy WebRTC data channel when implicit hosting is enabled. Authenticated non-privileged room members can exploit this flaw to bypass room control locks and profile restrictions, allowing unauthorized injection of mouse and keyboard events into the shared virtual browser session.
CVE-2026-116688 (GHSA-rh54-vffm-5fvp) is a high-severity Cross-Site Scripting (XSS) vulnerability in the CKEditor 5 engine package (`@ckeditor/ckeditor5-engine` and the umbrella `ckeditor5` package). The flaw originates from an upstream prototype pollution vulnerability in the `es-toolkit` dependency (versions prior to 1.52.0) used to parse and process inline HTML `style` attributes. When processing crafted CSS style declarations, the engine permits modification of `Object.prototype`, which alters downstream DOM conversion and sanitization checks to achieve arbitrary JavaScript execution in the browser context.
CVE-2026-116687 is a Cross-Site Scripting (XSS) vulnerability in the @ckeditor/ckeditor5-engine package of CKEditor 5. When the General HTML Support (GHS) plugin is configured to permit embedded container elements such as <object>, <embed>, or <iframe>, insufficient validation of data: URIs and incomplete stripping of ASCII C0 control characters allow attackers to execute arbitrary JavaScript within an isolated null-origin browser context.
CVE-2026-116686 is a Server-Side Request Forgery (SSRF) and Denial of Service (DoS) vulnerability in the official Model Context Protocol (MCP) Python SDK (mcp package on PyPI). When an MCP client validates structured tool execution results against a server-provided outputSchema, the SDK invokes the Python jsonschema library with its default URI resolution configuration. An untrusted or compromised MCP server can supply an outputSchema containing external $ref directives (using http://, https://, or file:// schemes), forcing the client process to synchronously fetch internal network resources, read local files, or block the main event loop indefinitely.
CVE-2026-116685 (GHSA-5h93-6whr-6q8j) is a sensitive information disclosure vulnerability in the official Model Context Protocol (MCP) Python SDK (`mcp` on PyPI). In versions 1.8.0 through 1.29.1 and 2.0.0 through 2.1.1, the SDK's HTTP-based client transports—including Streamable HTTP, Server-Sent Events (SSE), and OAuth client flows—unconditionally enable automatic HTTP redirect following (`follow_redirects=True`) across origin boundaries. When a configured endpoint returns a redirect to an external origin, the HTTP client forwards caller-supplied custom authentication headers (such as `X-API-Key`) and, in the case of HTTP 307 or 308 responses, replays the full POST request body containing JSON-RPC payloads or OAuth parameters to the untrusted destination.