Grav versions from 2.0.0 up to but not including 2.2.5 contain an improper access control vulnerability in the FlexDirectory::dynamicFlexField() function. This flaw allows page authors to invoke arbitrary Flex object methods through flex-*@ directives in page frontmatter. When a user with page edit permissions opens such a page, certain directives like setStorageKey() and delete() execute in their session, potentially deleting protected pages that should not be deletable by either account.
Grav versions from 2.0.0 up to but not including 2.2.5 contain an information disclosure vulnerability. This flaw allows page authors to capture visitors' HTTP Basic authentication credentials by embedding them in Twig page content. Specifically, authors can print the request URI object, which includes PHP_AUTH_USER and PHP_AUTH_PW, inside an inline style background image, potentially exfiltrating administrator credentials to an external host.
Grav 1.7.53 before 1.7.53.5 and 2.0.0-rc.8 before 2.2.2 contains a resource exhaustion vulnerability that allows anonymous visitors to bypass the system.images.max_pixels limit when system.images.url_actions is enabled. Attackers can send single-dimension or percentage forceResize, zoomCrop, or chained image actions to make GD allocate multi-gigabyte canvases, exhausting server memory and taking the site down.
Grav from 2.0.0 before 2.2.2 contains an information disclosure vulnerability that allows page editors to read protected configuration by adding config-*@ directives to page frontmatter forms. Attackers with page-edit permission can bypass the Twig sandbox config_denied_paths redaction to expose security.salt, plugin credentials, and Redis passwords to all visitors.
Grav from 2.0.15 before 2.2.2 contains an allowlist bypass vulnerability that allows page editors to call arbitrary public static methods by renaming Flex form fields with replace-name@. Attackers with page-edit permission can smuggle data-*@ directives past the callable check to invoke Folder::delete, Folder::move, or Utils::download, deleting directories, overwriting files, or reading files.
CVE-2026-78851 is a directory traversal vulnerability affecting Grav CMS versions 2.0.3 through 2.0.6. This flaw allows a remote attacker with low privileges to access sensitive information by exploiting the GravExtension.php file via the mediaDirFunc method. The vulnerability has a medium severity rating with a CVSS score of 6.5.
Grav versions before 2.1.7 have an information disclosure vulnerability where nested .git directories are not properly blocked by default web server configurations. This allows unauthenticated attackers to access Git repository data such as config files, refs, and objects, potentially exposing unpublished history or secrets.
Grav versions from 2.0.22 up to but not including 2.1.7 have an authentication bypass vulnerability in the Clockwork profiler component. This flaw allows unauthenticated attackers behind a same-host HTTP proxy to access profiler records, which may expose sensitive system and plugin configuration details, secrets, request metadata, and queries. The vulnerability arises because the profiler trusts loopback REMOTE_ADDR values, enabling unauthorized information disclosure.
Grav versions before 2.1.7 have a denial of service vulnerability where authenticated media authors can upload highly compressed oversized raster images. This causes memory exhaustion when the images are processed without dimension checks, leading to page unavailability for anonymous users.
Grav versions from 2.0.22 up to but not including 2.1.7 have an information disclosure vulnerability. This flaw allows page editors to access the Clockwork debugger token due to an omission in the sandbox redaction list. Attackers can exploit this by rendering the token via editor-authored Twig templates and use it to read profiler records containing sensitive configuration, request metadata, and database queries.
Grav is a flat-file CMS. In versions 2.0.19 through 2.0.24 — and in 2.0.0 through 2.0.18 and 1.7.x only where content Twig has been explicitly enabled — page content authored by a user holding only page-write permission is rendered through a Twig sandbox that allowlists get_cookie(), which returns any cookie sent with the current request, including the visitor's session cookie. Because the read occurs server-side via filter_input(INPUT_COOKIE, ...), the HttpOnly, Secure and SameSite attributes offer no protection. Grav then stores the finished post-Twig output in a page-content cache keyed only on page identity and the configuration checksum, with no session, user or request dimension and no bypass for authenticated visitors. A page published by a page-write user can therefore capture the session identifier of the next administrator who views it, after which the cached output serves that identifier to unauthenticated visitors, who can replay the cookie to authenticate as that administrator. Since 2.0.19, security.twig_content.process_enabled defaults to true and Security::applyTwigContentDefault() derives each page's process.twig flag from that gate, so content Twig runs on every page with no frontmatter or operator action. Fixed in 2.0.25; 1.7.x is outside the backport scope.
Grav CMS 2.0.14 through 2.0.24 contains a privilege escalation vulnerability in the group and account blueprints. The access map is gated by a `security@: admin.super` guard that is resolved by the field's exact path, so a submitted flat dot-notation key such as `access.admin.super` (instead of the nested `access[admin][super]`) matches no blueprint rule, survives BlueprintSchema::filterArray() and flattening, and is written by FlexObject::update() via setNestedProperty(), which splits on `.` and reconstructs the nested value. An authenticated backend operator using the flex accounts backend who holds admin.users but not admin.super can therefore grant admin.super to their own account or to a group they belong to and escalate to full super-admin, gaining control over configuration, plugin and theme installation, the file manager, and all accounts. Fixed in 2.0.25, which drops any dotted key whose ancestor path is disabled or marked validate.ignore.
Grav before 2.0.25 ships web server configuration samples whose access-control deny rules are matched case-sensitively. In webserver-configs/web.config (IIS), every deny rule (user_sensitive_folders, user_accounts, user_data, user_error_redirect, user_pages, system, vendor, ignore_folders) sets ignoreCase="false" on its URL Rewrite <match> element, overriding the IIS default of ignoreCase="true"; because these are rewrite matches rather than <requestFiltering> elements, there is no case-insensitive fallback. On IIS running over case-insensitive NTFS, an unauthenticated remote attacker can vary the case of a folder name or file extension (for example GET /user/CONFIG/system.YAML) so that no deny rule matches and the IIS static file handler resolves and returns the underlying file, disclosing sensitive data such as configuration secrets or account password hashes. Whether a bypassed file is actually returned depends on MIME registration: .json is served by default, while .yaml/.yml return HTTP 404.3 on a stock IIS unless a YAML MIME mapping has been added. The same class of gap exists in the bundled webserver-configs/lighttpd.conf, whose user/(config|env), directory, script-extension, root-file and dotfile rules lack the (?i) modifier, though it is lower risk because lighttpd typically runs on case-sensitive filesystems. Deployments served by Apache (.htaccess), nginx, Caddy, or the PHP built-in server are not affected. The issue is fixed in 2.0.25; because the .htaccess installer heal does not touch web.config or lighttpd.conf, operators must re-copy the corrected sample files after upgrading.
Grav 2.0.0 through 2.0.24 contain a Twig content sandbox escape. The `array` filter (and its identical function form) is on the sandbox allowlist but is registered without the needs_is_sandboxed guard that print_r, vardump, json_encode, yaml_encode and string carry, and its implementation calls toArray() — or falls back to an (array) cast — without consulting the sandbox method allowlist. Because the `grav` Twig global is the raw Pimple-based dependency injection container, a user who can author Twig in page content can evaluate `grav|array` to read the container's private $values array, including the un-redacted Config service; a second array cast returns the entire configuration tree, disclosing plugin credentials, SMTP and OAuth secrets, Redis passwords, proxy URLs and the security.* subtree that the sandbox's redaction is meant to hide. Because the payload is stored in page content, the disclosed configuration is rendered to anonymous visitors. Grav 1.7 is not affected as it has no Twig content sandbox. Fixed in Grav 2.0.25.
Grav is a flat-file CMS. In versions 2.0.0-rc.1 through 2.0.21, the Twig content sandbox fails to restrict the dump and serialize filters (print_r, vardump, json_encode, yaml_encode, string): GravExtension::assertSandboxDumpSafe() determines sandbox state by calling SandboxExtension::isSandboxed() without a Source argument, which reports only the global sandbox flag that Grav never enables, so the guard added in GHSA-mc5q-6hpj-rp7j never executes. As a result, an authenticated user with page-edit rights can render {{ config|print_r }} in page content with Twig processing enabled and dump Grav's entire merged configuration — print_r reflects the real Config object held in a private property of the SandboxConfig facade, bypassing its path redaction — exposing plugin secrets such as SMTP credentials, API tokens, webhook secrets and cache backend passwords. Grav 1.7 is not affected because it ships no Twig content sandbox. The issue is fixed in 2.0.22, where the affected filters are registered with Twig's needs_is_sandboxed flag.
Grav is a flat-file CMS. In Grav 1.7.0 through 1.7.53.2 and 2.0.0 through 2.0.21, when the debugger is enabled (system.debugger.enabled: true, which is not the default), the Clockwork profiler endpoint is exposed without authentication: InitializeProcessor::handleDebuggerRequest() intercepts any path containing /__clockwork/ during bootstrap and passes it to Debugger::debuggerRequest(), which performs no user lookup, IP restriction, or Clockwork authenticator check, and also supports anonymous pagination over the entire stored history. With the shipped censored: false default, each stored record contains raw request cookies (including Grav's session cookie, whose value is the PHP session id, allowing an attacker to resume another user's session, including an authenticated admin's), the full parsed request body (Grav's login form posts data[username]/data[password], so passwords are stored in plaintext because Clockwork's password filter only inspects top-level keys), and the site's entire system and plugin configuration, including operator-saved secrets such as SMTP credentials, third-party API keys, and licence keys. Authorization and X-API-Token headers are stored even when censored: true. On Grav 2.0, setting provider: debugbar does not avoid the issue because Grav forces the Clockwork provider for requests preferring a JSON response. The issue is fixed in 1.7.53.4 and 2.0.22, which restrict /__clockwork/ to server-local requests or requests presenting the new system.debugger.token secret and strip cookies and credential headers from stored records. Workarounds include setting debugger.enabled: false or blocking /__clockwork/ at the web server or CDN.
Grav versions prior to 2.0.16 have a vulnerability in the Twig sandbox configuration where the default denylist is incomplete. This allows attackers with page-edit permissions to access sensitive system configuration secrets via Twig template functions like config.get() or config.toArray() when config_access is enabled.
Grav versions before 2.0.16 have a vulnerability where sandboxed Twig templates can access sensitive User fields without proper filtering. This allows attackers with page-edit permissions to retrieve hashed passwords and 2FA secrets, potentially enabling offline password cracking and authentication bypass.
Grav CMS versions prior to 2.0.16 have an origin validation bypass vulnerability in the Uri::referrer() and Pages::referrerRoute() methods. These methods validate the Referer header using an unanchored string prefix match without a trailing delimiter, allowing an attacker controlling a domain that starts with the victim site's origin to bypass the Referer-based origin check. This vulnerability is critical with a CVSS score of 9.3.
Grav CMS versions before 2.0.16 have a timing vulnerability in the CSRF nonce validation function Utils::verifyNonce(). The vulnerability arises because the function uses a non-constant-time string comparison operator (===) instead of a constant-time function like hash_equals(). This allows attackers to measure response timing differences and recover valid nonce values byte-by-byte, weakening the intended CSRF protection.