Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Editor-level authentication required (PR:L), victim must load page (UI:R), stored payload persists across sessions with scope change (S:C), no availability impact.
Primary rating from Vendor (Fluid Attacks).
CVSS VectorVendor: Fluid Attacks
Lifecycle Timeline
2DescriptionCVE.org
Grav 2.0.0-rc.9 with Admin2 2.0.0-rc.14 contains a stored cross-site scripting (XSS) vulnerability in the Admin2 Pages API save flow.
AnalysisAI
Stored XSS in Grav CMS's grav-plugin-api (Admin2) allows a low-privileged editor to persist malicious JavaScript in page content via the PATCH /pages API endpoint, which then executes in any administrator's or visitor's browser when the affected page is loaded. The root cause is that the API's partial-field validation path (validateChangedFields()) ran only type and required-field checks but omitted the XSS safety gate (Validation::checkSafety()), a check the classic Admin interface correctly enforced - creating a trust-boundary bypass exclusive to the REST API path. Publicly available exploit code exists via the Fluid Attacks advisory and the vendor's own regression test suite; no active exploitation (CISA KEV) has been confirmed.
Technical ContextAI
Grav CMS is a flat-file PHP CMS; the affected component is grav-plugin-api (Admin2), identified by CPE cpe:2.3:a:grav:grav-plugin-api:*:*:*:*:*:*:*:*. The vulnerability resides in AbstractApiController::validateChangedFields() in classes/Api/Controllers/AbstractApiController.php, which handles partial-field delta submissions for HTTP PATCH requests to the /pages and /accounts endpoints. The classic Admin interface runs the full blueprint validator, which calls Validation::checkSafety() per field - invoking Security::detectXss() - before persisting content. The API path called only Validation::validate() for type and required checks, leaving the XSS safety gate entirely absent. CWE-79 (Improper Neutralization of Input During Web Page Generation) is the root cause class: unsanitized HTML event handlers such as onerror= attributes embedded in Markdown content fields (type: markdown, validated as textarea) were stored verbatim and later rendered in victim browsers. The fix adds Validation::checkSafety() calls within validateChangedFields() and honours the existing security.xss_whitelist (admin.super) and per-field xss_check: false blueprint settings, maintaining behavioural parity with the classic admin.
RemediationAI
Upstream fix available (PR/commit); released patched version not independently confirmed. Apply patch commit b8ca62eddb7dbea92075a78b1c0a507f03d66d4a from https://github.com/getgrav/grav-plugin-api/commit/b8ca62eddb7dbea92075a78b1c0a507f03d66d4a, which adds Validation::checkSafety() to AbstractApiController::validateChangedFields() and closes the trust-boundary gap. Monitor https://github.com/getgrav/grav-plugin-api and the security advisory at https://github.com/getgrav/grav/security/advisories/GHSA-5wc5-7v9g-f7v6 for a tagged release incorporating this fix. If immediate patching is not feasible, restrict API access to the PATCH /pages and PATCH /accounts endpoints to superadmin-level users only via network ACL or application-layer authentication controls, eliminating the attack surface for non-superadmin editors; the trade-off is that any automation or integrations relying on editor-level API writes will break. Alternatively, disable the grav-plugin-api plugin entirely if REST API access is not operationally required, accepting that all Admin2 API integrations will cease to function. Do not rely solely on a WAF-based XSS filter as a compensating control, as Markdown-context bypass payloads are well-documented and evasion is straightforward.
More in Grav Plugin Api
View allServer-Side Request Forgery in the Grav API plugin (grav-plugin-api) for Grav CMS before 1.0.6 lets a caller holding the
Privilege escalation in Grav API Plugin (all versions before 1.0.6) allows any authenticated user holding the api.users.
Host header injection in the Grav API plugin's forgot-password endpoint allows unauthenticated remote attackers to redir
Privilege escalation in grav-plugin-api before 1.0.20 allows a low-privileged API manager to self-create a super-admin a
Privilege escalation in grav-plugin-api before 1.0.20 allows authenticated API users holding api.access and api.users.wr
Permissive CORS misconfiguration in the Grav API Plugin (all versions prior to 1.0.0-rc.16) allows cross-origin JavaScri
Scope enforcement is entirely absent in the Grav API Plugin prior to 1.0.6, meaning any scoped API key silently exercise
JWT bearer token leakage in the Grav API Plugin for Grav CMS (prior to 1.0.0-rc.16) exposes valid access tokens to any p
Stored cross-site scripting in the Grav API Plugin (all versions prior to 1.0.2) allows an authenticated attacker holdin
Same weakness CWE-79 – Cross-site Scripting (XSS)
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-37916
GHSA-pfvx-ccc8-f3hr