Skip to content

← Public intelligence

Packagist package

getgrav/grav

Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.

Open advisories

93 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.

  1. High, CVSS 8.7PackagistAmendedPublished

    Grav CMS vulnerable to remote code execution via .zip file upload

    CVE-2026-72819 · GHSA-r94f-hx44-8jqf

    Affected
    • Packagist getgrav/grav < 2.0.13
    Fixed in
    2.0.13· Vendor fix available
    Issue
    A logged-in user can run any command on the server. A settings field can fill itself by calling one of Grav's built-in routines, and a safety check is supposed to allow only harmless ones. The check only recognises a routine when its name is written as one piece of text; named as a pair of values instead, it is not examined at all and is passed as safe. Pointing such a field at the routine that unpacks ZIP archives writes a PHP file from an uploaded archive into the site's public folder, which the server then runs. Details The check rejects known-dangerous routines and, for those belonging to a component, allows only a short approved list. Both of those cases only apply when the name arrives as a single string. The same routine can be named as a pair (the component and the routine inside it), and in that form the check matches neither case, skips both lists and answers "safe". Grav then calls it, with arguments the attacker supplies in the same field. Any routine shipped with…

    Recommended action: Upgrade to 2.0.13 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  2. High, CVSS 7.7PackagistAmendedPublished

    Grav: Missing admin.super guard on core group blueprint access field allows admin.users operator to escalate to super-admin

    CVE-2026-75837 · GHSA-xhfv-7758-r9hx

    Affected
    • Packagist getgrav/grav < 2.0.14
    Fixed in
    2.0.14· Vendor fix available
    Issue
    The core Flex group blueprint system/blueprints/user/group.yaml (access field, lines 48-55) omits the security@: admin.super field guard that its sibling account blueprint carries (account.yaml:131/138/150, added by the CVE-2026-42613 fix). A delegated non-super operator holding admin.users.update can therefore save a group whose access map contains admin.super: true, which UserGroupObject::authorize then grants to every member of that group, a full privilege escalation to super-admin (scheduler/cron RCE, Twig eval). This is a distinct file, sink, and fix from all four related advisories. Root Cause The CVE-2026-42613 fix protected the account access/groups fields with a blueprint-level security@: admin.super gate, which Blueprint::dynamicSecurity() (system/src/Grav/Common/Data/Blueprint.php:644-662) uses to mark a field validate.ignore=true for non-super users so BlueprintSchema::filterArray() (BlueprintSchema.php:263-311) drops it. The functionally-identical group access…

    Recommended action: Upgrade to 2.0.14 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  3. Medium, CVSS 5.1PackagistAmendedPublished

    Grav: Single invalid UTF-8 byte disables every rule in Security::detectXss(), bypassing the page-content XSS safety gate

    CVE-2026-75834 · GHSA-q2j8-x8hf-63ch

    Affected
    • Packagist getgrav/grav < 2.0.14
    Fixed in
    2.0.14· Vendor fix available
    Issue
    Vulnerability Details Component: getgrav/grav core File: system/src/Grav/Common/Security.php Function: detectXss() (all six entries in the $patterns array use the PCRE u modifier), invoked from Grav\Common\Data\Validation::checkSafety() (the save-time XSS gate for any non-security.xsswhitelist account's blueprint field, including the page content field) and detectXssInEditorContent() (the render-time backstop for GHSA-2c4f-86xc-cr74) CWE: CWE-79 (Stored XSS), root-caused by CWE-20 (Improper Input Validation — fails open on malformed input) Severity: High CVSS: 8.0 — CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N Relationship to prior advisories This project's detectXss()/checkSafety() stack has been patched at least three times for the "page editor without super-admin rights stores an event handler that runs for site visitors" bug class: GHSA-9695-8fr9-hw5q / GHSA-c2q3-p4jr-c55f / GHSA-w8cg-7jcj-4vv2 (unquoted-attribute bypasses), GHSA-269c-h76q-8cxw (quoted-attribute-boundary bypass),…

    Recommended action: Upgrade to 2.0.14 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  4. Critical, CVSS 9.3PackagistAmendedPublished

    Grav: Blueprint dynamic-data bare-function branch is denylist-gated and omits error_log, giving arbitrary file write

    CVE-2026-75827 · GHSA-f8wv-xp27-6gq7

    Affected
    • Packagist getgrav/grav <= 2.0.14
    Fixed in
    2.0.15· Vendor fix available
    Issue
    Affected versions and vulnerable location - Confirmed on grav core at 78ebfc1 (tag 2.0.13). - Sinks: - system/src/Grav/Common/Data/Blueprint.php:455-458 calluserfuncarray($o, $params) (bare-function dynamic-data provider). - Twin: system/src/Grav/Framework/Flex/FlexDirectory.php:936-938 calluserfuncarray($function, $params). - Validation gate: Blueprint::isSafeDynamicCall() at Blueprint.php:514-536. - Class::method branch (:514-527) uses a strict positive allowlist self::$allowedDynamicCallables. - Bare-function branch (:530-534) uses only a denylist: if (isstring($function) && Utils::isDangerousFunction($function)) return false; return !self::paramsContainDangerousCallable($params);. - Denylist: Utils::isDangerousFunction() (system/src/Grav/Common/Utils.php, list around :2020-2270). Root cause GHSA-7pgq/CVE-2026-64850 hardened the Class::method half of the dynamic-callable validation to a positive allowlist because a page-edit account could otherwise name any static method as a…

    Recommended action: Upgrade to 2.0.15 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  5. Critical, CVSS 9.3PackagistAmendedPublished

    Grav: detectXss() misses an event-handler attribute after an unpaired quote in an unquoted attribute value, giving stored XSS

    CVE-2026-75828 · GHSA-vfmf-q6x9-cw96

    Affected
    • Packagist getgrav/grav <= 2.0.14
    Fixed in
    2.0.15· Vendor fix available
    Issue
    Affected versions and vulnerable location - Confirmed on grav core at 78ebfc1 (tag 2.0.13). - Detector: system/src/Grav/Common/Security.php:290, the onevents regex, run via patternMatches() (:315-330). - The onevents pattern at HEAD: #<(?:"[^"]"|'[^']'|[^"'])?(?:[\s\x00-\x20"'/]|"[^"]"|'[^']')on\s[a-z]+\s=#iu - Sole save-time guard for non-super content: Validation::checkSafety() (system/src/Grav/Common/Data/Validation.php:160 scalars, :165 arrays), invoked per field from BlueprintSchema::validate - Validation::checkSafety (system/src/Grav/Common/Data/BlueprintSchema.php:248). security.xsswhitelist: [admin.super] exempts only super-admins (Validation.php:148). Root cause (distinct from GHSA-269c) GHSA-269c hardened the tag-body scan to be quote-aware so a inside a paired quoted attribute value is treated as data, not a tag close. That same quote-awareness opened a new gap: the regex treats ANY " or ' as a string delimiter, but HTML only enters a quoted-value state when a quote…

    Recommended action: Upgrade to 2.0.15 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  6. High, CVSS 8.2PackagistAmendedPublished

    Grav: Unauthenticated Path Traversal via Missing Directory-Boundary Check in plugin-asset-map.php Static Asset Server (index.php)

    CVE-2026-74907 · GHSA-4v9q-p283-qc2m

    Affected
    • Packagist getgrav/grav <= 2.0.14
    Fixed in
    2.0.15· Vendor fix available
    Issue
    Verified against: getgrav/grav devel branch, GRAVVERSION = "2.0.15", file index.php Title Unauthenticated Path Traversal via Missing Directory-Boundary Check in plugin-asset-map.php Static Asset Server (index.php) Product / Affected Versions - Product: getgrav/grav - File: index.php (top-level front controller, runs before Grav itself boots) - Confirmed present in: devel branch, 2.0.15 - Precondition: requires user/config/plugin-asset-map.php to exist and contain at least one route-prefix mapping ,this is an opt-in mechanism (per the code comment: "Fast static asset serving for plugins that bundle SPA apps"). No core mechanism generates this file automatically; it's created by a plugin that opts into this fast-path. Not reachable on a stock Grav install with no such plugin. Where reachable, it requires zero authentication. CWE CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') , specific mechanism: a path-prefix containment check performed with…

    Recommended action: Upgrade to 2.0.15 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  7. High, CVSS 7.1PackagistAmendedPublished

    Grav: Path Traversal in MediaUploadTrait::deleteFile() Allows Arbitrary File Deletion

    CVE-2026-72695 · GHSA-jq29-c7v8-rg55

    Affected
    • Packagist getgrav/grav <= 2.0.15
    Fixed in
    2.0.16· Vendor fix available
    Issue
    Path Traversal in MediaUploadTrait::deleteFile() Allows Arbitrary File Deletion Summary A path traversal vulnerability in MediaUploadTrait::deleteFile() allows an authenticated user with media management permissions to delete arbitrary files on the server. The method validates only the basename portion of the filename using Utils::checkFilename(), while the directory path (which may contain ../ sequences) is preserved and passed unvalidated to unlink(). This enables directory escape from the intended media storage path. Severity High (8.1) - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H CWE CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') Details In system/src/Grav/Common/Media/Traits/MediaUploadTrait.php, the deleteFile() method (lines 332-365) performs filename validation only on the basename, not the full path: doRemove() (line 521-582) then calls: Utils::checkFilename() (lines 1022-1044) properly checks for /, \, and .., but it is applied to…

    Recommended action: Upgrade to 2.0.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  8. High, CVSS 7.1PackagistAmendedPublished

    Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content

    CVE-2026-72697 · GHSA-47ch-6w46-6xm7

    Affected
    • Packagist getgrav/grav <= 2.0.15
    Fixed in
    2.0.16· Vendor fix available
    Issue
    The mediadirectory() Twig function is allow-listed for use in sandboxed, editor-authored page content (system/config/security.yaml). Its implementation, GravExtension::mediaDirFunc(), only treats the input as unsafe when it looks like a Grav stream (user://, theme://, etc). If the input is instead a plain filesystem path, absolute or relative, the stream check is skipped entirely and the raw string is handed straight to new Media($mediadir), which lists every file in that directory whose extension matches a configured media type (which by default includes txt, json, xml, pdf, doc, docx, and more, not just images) and builds Medium objects for them. Separately, the sandbox's own allow-list for the Medium class includes the filepath accessor. A code comment directly above that allow-list entry states the developers' intent was for filepath to be part of the "dangerous surface" that "stays blocked", but it is listed as an allowed method on the very same line, contradicting that…

    Recommended action: Upgrade to 2.0.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  9. High, CVSS 8.7PackagistAmendedPublished

    Grav: UserInterface offsetget/offsetexists allow-listed in Twig sandbox let editor-authored content leak hashed_password and 2FA secrets via offsetGet()

    CVE-2026-76839 · GHSA-3jhr-mxmx-38cx

    Affected
    • Packagist getgrav/grav <= 2.0.15
    Fixed in
    2.0.16· Vendor fix available
    Issue
    system/config/security.yaml's Twig sandbox policy allow-lists offsetget and offsetexists for Grav\Common\User\Interfaces\UserInterface. The concrete Grav\Common\User\DataUser\User class does not filter which fields offsetGet() returns, so any sandboxed template with access to a User object can read hashedpassword, secret (2FA seed), and twofasecret directly, bypassing the redaction Grav's own code applies everywhere else. The core evidence, from Grav's own code system/src/Grav/Common/User/DataUser/User.php: system/config/security.yaml: This is the same vulnerability shape as two already-fixed issues in this file (GHSA-j274-39qw-32c9 and GHSA-mc5q-6hpj-rp7j -- both a raw, unfiltered data-access path bypassing an intended redaction) recurring on a third class neither fix covered. Live, end-to-end verification Built a real Twig\Environment wired with the real Twig\Extension\SandboxExtension, policed by Grav's own GravSecurityPolicy class, constructed directly from values parsed…

    Recommended action: Upgrade to 2.0.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  10. High, CVSS 8.7PackagistAmendedPublished

    Grav: config_denied_paths default list omits system, exposing real secrets (e.g. system.cache.redis.password) via the Twig sandbox when config_access is enabled

    CVE-2026-76846 · GHSA-xjw5-q542-3vmr

    Affected
    • Packagist getgrav/grav <= 2.0.15
    Fixed in
    2.0.16· Vendor fix available
    Issue
    system/config/security.yaml's default twigsandbox.configdeniedpaths list (plugins, streams, security, backups, scheduler) omits the system prefix. When an operator enables the documented, non-default twigcontent.configaccess: true setting (intended to safely expose low-sensitivity values like site.title to editor-authored Twig content), any real secret stored under system. , for example system.cache.redis.password , is also exposed, both via config.get(...) and via config.toArray(), to any user with page-edit permission. This is a follow-up gap in the fix for GHSA-j274-39qw-32c9 (config.toArray() secret exfiltration): that fix correctly introduced a SandboxConfig facade with a denylist, but the shipped default denylist is incomplete. Environment used to verify - Grav commit at HEAD of the default branch, GRAVVERSION 2.0.15 - PHP 8.3.6 with curl, zip, dom, gd extensions installed - Full composer install --no-dev run against the real repository (no mocked dependencies) so the…

    Recommended action: Upgrade to 2.0.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  11. High, CVSS 7.1PackagistAmendedPublished

    Grav: The system, site, and theme Twig variables bypass the content sandbox entirely and are never covered by config_denied_paths

    CVE-2026-72698 · GHSA-p597-crqc-m349

    Affected
    • Packagist getgrav/grav < 2.0.16
    Fixed in
    2.0.16· Vendor fix available
    Issue
    Grav\Common\Twig\Twig::init() unconditionally puts the raw system, site, and theme config arrays into $this-twigvars. Twig::processPage() builds the variables for the sandboxed, editor-authored page-content render by copying that same base array ($sandboxvars = $twigvars;) and replacing only the config key with a filtered SandboxConfig facade. The system, site, and theme keys are carried into the sandboxed render completely untouched. Because these are plain PHP arrays, not objects, Twig's sandbox SecurityPolicy (the allowedclasses/allowedmethods/allowedproperties lists in system/config/security.yaml) has no jurisdiction over them at all. The sandbox only gates method calls and property access on objects. Dot notation or subscript access on an array is always allowed by Twig regardless of any sandbox policy. So {{ system.cache.redis.password }} in page content renders the value directly, with the sandbox doing nothing to stop it, and with…

    Recommended action: Upgrade to 2.0.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  12. Low, CVSS 3.7PackagistAmendedPublished

    Grav: Non constant time nonce comparison in Utils::verifyNonce() used for CSRF protection

    CVE-2026-72701 · GHSA-38p6-h87p-r4cg

    Affected
    • Packagist getgrav/grav < 2.0.16
    Fixed in
    2.0.16· Vendor fix available
    Issue
    `Grav\Common\Utils::verifyNonce()`, the core function Grav and its plugins use to validate CSRF nonces, compares the submitted nonce to the expected value with PHP's `===` operator instead of `hash_equals()`. `===` on strings short circuits at the first differing byte, so the comparison time leaks how many leading bytes of a guess are correct. This is CWE-208, Observable Timing Discrepancy. The codebase already knows to avoid this pattern. `hash_equals()` is used for the equivalent purpose in four other places I found: `system/src/Grav/Common/Session.php`, `system/src/Grav/Framework/Cache/Adapter/FileCache.php`, `system/src/Grav/Common/Scheduler/Scheduler.php` (the webhook token check), and `system/src/Grav/Common/Scheduler/JobQueue.php`. `Utils::verifyNonce()` is the one place I found that still uses a plain equality check for a secret comparison. Affected product and version Product: Grav CMS, getgrav/grav Confirmed present in: 2.0.15, commit…

    Recommended action: Upgrade to 2.0.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  13. UnratedPackagistAmendedPublished

    Grav: Origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match

    CVE-2026-72702 · GHSA-9ccq-2jfg-qw33

    Affected
    • Packagist getgrav/grav <= 2.0.15
    Fixed in
    2.0.16· Vendor fix available
    Issue
    Grav\Common\Uri::referrer() and Grav\Common\Page\Pages::referrerRoute() both check whether an incoming request's Referer header "came from our site" using strstartswith($referrer, $base), where $base is the site's own absolute root URL (for example https://example.com, no trailing slash). Because the comparison has no boundary character after the prefix, any Referer value that merely starts with that string is accepted, including a Referer from a completely different host such as https://example.com.attacker.tld. This is the same class of bug already fixed once in 2.0.15 for the fast static asset server (GHSA-4v9q-p283-qc2m, "also allowing any neighbouring directory whose name starts with the same letters"). The identical pattern is still present in both places that trust the Referer header, and neither is covered by that fix. Affected product and version Product: Grav CMS, getgrav/grav Confirmed present in: 2.0.15, commit c2b46866857a93a0aa7048e7ed707ed3ed45dbc3 The pattern…

    Recommended action: Upgrade to 2.0.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  14. Medium, CVSS 5.1PackagistAmendedPublished

    Grav: Stored XSS via Markdown audio/video media URL

    CVE-2026-75831 · GHSA-6qw9-4vv5-jr97

    Affected
    • Packagist getgrav/grav <= 2.0.14
    Fixed in
    2.0.15· Vendor fix available
    Issue
    Target: github.com/getgrav/grav Affected resource: Grav\Common\Media\Traits\AudioMediaTrait / VideoMediaTrait sourceParsedownElement() — verified on 2.0.13 (latest stable) and develop HEAD 5a7070f Severity: Medium (~6.9 CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:L/A:N — anchored to the sibling script-XSS advisory CVE-2026-42841, same PR:H / S:C / C:H / I:L) Weakness: CWE-79 (Improper Neutralization of Input During Web Page Generation) Summary A Markdown audio or video embed renders its <source element as raw HTML with the media URL concatenated unescaped. The URL fragment is reflected without any encoding, so ) breaks out of <source src="…" and injects arbitrary HTML — including a script-executing <svg onload — into the rendered page. Any user who views the page runs the attacker's JavaScript in their session; a logged-in administrator who views it exposes their same-origin Grav Admin session to the attacker's script. This is the next sink in the media-parameter injection class the…

    Recommended action: Upgrade to 2.0.15 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  15. Medium, CVSS 5.4PackagistAmendedPublished

    Grav: Stored XSS via quoted-attribute bypass in detectXss

    CVE-2026-72832 · GHSA-269c-h76q-8cxw

    Affected
    • Packagist getgrav/grav >= 1.5.2, <= 2.0.12
    Fixed in
    2.0.13· Vendor fix available
    Issue
    A page editor without admin.super can place an event handler after a inside a quoted attribute. Grav accepts and stores the page, then executes the handler in the application origin when a visitor opens it. Details Security::detectXss() (system/src/Grav/Common/Security.php:253) anchors the onevents scan at < and uses [^]?, which cannot cross the first literal . When that character is inside a quoted value, the browser keeps the tag open and parses the later onerror attribute, so the detector and browser disagree. AdminController::savePage() relies on this detector when saving content from page editors outside the admin.super whitelist. PoC I reproduced this with getgrav/grav 2.0.11 (ad9709f865b09b68798fb1ac375b484a8cc1d892), Admin 1.10.52, and Quark 2 1.1.4. 1. Sign in as a user with admin.login and admin.pages, but without admin.super. 2. Create or edit /xsstest and save this page body: 3. Open /xsstest in a private browser window. The save succeeds and the visitor sees an…

    Recommended action: Upgrade to 2.0.13 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  16. High, CVSS 8.7PackagistAmendedPublished

    Grav: Path Traversal in ImageMedium::watermark() — arbitrary file disclosure via publicly-cached images

    CVE-2026-69089 · GHSA-w3f4-8pj2-599w

    Affected
    • Packagist getgrav/grav >= 2.0.10, <= 2.0.10
    Fixed in
    2.0.11· Vendor fix available
    Issue
    Reported by: Nihad Huseynli (@nihaddhuseynli (https://github.com/nihaddhuseynli)) — nihadd.huseynli@gmail.com ▎ Note: I attempted to report this via security@getgrav.org first, per SECURITY.md, but the email bounced with 550 5.1.1 Address does not exist. Filing directly here instead. Path Traversal in ImageMedium::watermark() leading to arbitrary file disclosure via publicly-served images Summary The watermark media action, documented and allow-listed for use in editor-authored Markdown image syntax, passes its $image argument unsanitized into UniformResourceLocator::findResource(). That resolver only lexically collapses .. segments (no realpath()/containment check) and, for the default file:// scheme, resolves straight to fileexists() with no re-validation against the registered stream root. A relative-path traversal string therefore resolves to an arbitrary absolute path on disk. If that path is a valid image, its pixel content is composited into the carrier image and the result is…

    Recommended action: Upgrade to 2.0.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  17. High, CVSS 8.1PackagistAmendedPublished

    Grav: Incomplete callable validation in blueprint dynamic fields allows arbitrary static method invocation and file disclosure

    CVE-2026-69088 · GHSA-7pgq-cr25-xvc8

    Affected
    • Packagist getgrav/grav >= 2.0.7, < 2.0.11
    Fixed in
    2.0.11· Vendor fix available
    Issue
    Grav CMS's blueprint dynamic-field callable guard can be bypassed with a fully-qualified `Class::method` string, letting an account with only page-editing rights (`admin.pages`, not super-admin) plant a directive in a page's form-field frontmatter that invokes an arbitrary public static PHP method with attacker-controlled arguments. Using built-in gadget methods this yields, at minimum, arbitrary reading of any server-readable file (disclosed to anonymous visitors of the crafted page) and arbitrary creation/copying of files and directories under the web-server account. Details `Blueprint::isSafeDynamicCall()` (`system/src/Grav/Common/Data/Blueprint.php`, method around line 488) is meant to block dangerous callables named in a blueprint's dynamic-field directives (`data-*@`). It only consults its dangerous-name denylist when the callable string does not contain `::`: ```php if (is_string($function) && !str_contains($function, '::') &&…

    Recommended action: Upgrade to 2.0.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  18. High, CVSS 8.7PackagistAmendedPublished

    Grav: FlexDirectory::dynamicDataField() executes arbitrary callables from blueprint data with no validation

    CVE-2026-65608 · GHSA-c4wf-2xxc-68qm

    Affected
    • Packagist getgrav/grav >= 1.7.0, < 2.0.9
    Fixed in
    2.0.9· Vendor fix available
    Issue
    A missing validation check in Grav's Flex framework lets an account holding nothing but an ordinary object-create permission on a single Flex directory execute arbitrary shell commands on the server. Any authenticated user with create or update rights on a Flex-based directory (Flex Users, Flex Pages, Flex Objects, or any custom Flex type) can trigger it the moment a blueprint field anywhere in that directory carries a data-@: directive, since the code that resolves those directives calls calluserfuncarray() on attacker-influenced input with no restriction at all. This is a bypass of GHSA-fj2p-qj2f-74v5, already patched in 2.0.7. That fix added real validation to Blueprint::dynamicData(), but Grav's Flex system routes the same directive through a separate, unprotected method, FlexDirectory::dynamicDataField(), which never received the same fix. Details Grav blueprints support action-property@: directives, YAML keys that tell the blueprint engine to compute a field's value…

    Recommended action: Upgrade to 2.0.9 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  19. Medium, CVSS 6.5PackagistAmendedPublished

    Grav: Decompression-bomb size cap bypassed by forged ZIP size in ZipArchiver/Installer

    CVE-2026-61449 · GHSA-8h9x-89f2-m7x3

    Affected
    • Packagist getgrav/grav >= 2.0.1, <= 2.0.1
    Fixed in
    2.0.2· Vendor fix available
    Issue
    The decompression-bomb bound added in 2.0.1 (commit 1c1003c) sums ZipArchive::statIndex($i)['size'] and rejects an archive whose declared uncompressed total exceeds system.gpm.archive.maxuncompressedsize (default 1 GiB) before extracting (ZipArchiver.php:77-86; same logic in GPM\Installer::unZip at Installer.php:228-238). statIndex()['size'] is the uncompressed size declared in the ZIP central directory, which is attacker-forgeable and is not checked against the actual inflated stream. An archive declaring 1 byte per entry passes the cap while extractTo() writes the real (large) content. The entry-count and nesting-depth caps count real structure and still hold; only the size dimension is defeated, so the disk-fill / inode-exhaustion case the bound targets is not prevented. Incomplete fix for GHSA-928x-9mpw-8h56. Details extract()/unZip() validate every entry up front, then call Folder::create + extractTo. The size check is: $stat['size'] is read from the central directory,…

    Recommended action: Upgrade to 2.0.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  20. Medium, CVSS 4.8PackagistAmendedPublished

    Grav: Stored CSS injection via Markdown image resize() bypasses prior media style sanitizers in Grav

    CVE-2026-58657 · GHSA-ffmg-hfvg-jhg9

    Affected
    • Packagist getgrav/grav >= 2.0.0-rc.9, <= 2.0.0-rc.9
    Fixed in
    2.0.0· Vendor fix available
    Issue
    Grav 2.0.0-rc.9 and the current 2.0 branch still allow stored CSS injection through Markdown image media actions. The prior media hardening rejects direct ?style= payloads and unsafe attribute() fallbacks, but the adjacent resize() action still writes caller-controlled values directly into styleAttributes. A publisher who can edit page Markdown can store a crafted image URL that renders additional CSS declarations in the final <img style=... attribute. This crosses the same lower-privileged publisher to higher-privileged reviewer/admin rendered-content boundary as the earlier media style and attribute advisories. Impact A lower-privileged content editor can persist CSS declarations that are rendered when a higher-privileged user views the page or admin preview. The demonstrated payload creates a full-viewport fixed overlay by injecting position:fixed, viewport dimensions, background color, and z-index declarations. This does not require JavaScript execution. The impact is…

    Recommended action: Upgrade to 2.0.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  21. Medium, CVSS 5.1PackagistAmendedPublished

    Grav: XSS Blueprint Validation Bypass via Twig String Concatenation

    CVE-2026-61453 · GHSA-2c4f-86xc-cr74

    Affected
    • Packagist getgrav/grav >= 2.0.0, <= 2.0.0
    Fixed in
    2.0.1· Vendor fix available
    Issue
    The XSS blueprint validator (Security::detectXss()) runs on the raw page content before Twig processing. An attacker can use Twig's string concatenation operator (~) to dynamically construct an event handler name at render time. The validator sees {{ "on" ~ "error" }} - a harmless Twig expression - and allows the content. After Twig processes the template, the output contains <img src=1 onerror=alert(1) which is rendered via {{ content|raw }} and executes in the victim's browser. --- Details The two-stage attack exploits the separation between validation and rendering: Stage 1 - what the XSS validator sees (raw page content): The detectXss() function scans this string. The onevents regex looks for <[^]?\s\x00-\x20\"\'\/\s= inside HTML tags. In {{ x }}, the { character is not in the boundary set [\s\x00-\x20\"\'\/], and x is not on. No match - passes validation. Stage 2 - what Twig produces (after rendering): The validator never re-inspects Twig output. The theme template…

    Recommended action: Upgrade to 2.0.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  22. Medium, CVSS 6.9PackagistAmendedPublished

    Grav CMS — Improper Handling of Highly Compressed Data in Installer::unZip()

    CVE-2026-59193 · GHSA-2vcx-h8p2-9pg9

    Affected
    • Packagist getgrav/grav >= 1.0.0, < 2.0.0
    Fixed in
    2.0.0· Vendor fix available
    Issue
    An authenticated admin.super user can crash Grav or fill the disk by uploading a specially crafted ZIP archive through the Direct Install tool. The method Installer::unZip() calls ZipArchive::extractTo() without any limit on uncompressed size, entry count, or directory depth, enabling Zip Bomb (CWE-409), stack overflow (CWE-674), and disk/inode exhaustion. Details The vulnerability is in system/src/Grav/Common/GPM/Installer.php:176-208 (Installer::unZip()). The ZipArchive::extractTo() call at line 184 is not preceded by any validation of the archive contents. Missing validation: - ❌ No total uncompressed size check (decompression bomb — CWE-409) - ❌ No entry count check (inode exhaustion) - ❌ No directory nesting depth check (stack overflow in Folder::doDelete() — CWE-674) The subsequent cleanup call Folder::delete($destination) at line 189 recursively deletes every subdirectory without depth limit (Folder.php:531-547). A ZIP with thousands of nested directories will cause…

    Recommended action: Upgrade to 2.0.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  23. High, CVSS 7.4PackagistAmendedPublished

    Grav: 2FA Bypass via 'login.regenerate2FASecret' - Secret Rotation During Pending Challenge

    CVE-2026-62232 · GHSA-7mgc-c7pq-3rr3

    Affected
    • Packagist getgrav/grav < 2.0.4
    Fixed in
    2.0.4· Vendor fix available
    Issue
    When 2FA is enabled on an account, submitting correct credentials authenticates the user but leaves them unauthorized pending TOTP verification. During this pending-challenge window, the login.regenerate2FASecret task which requires only $user-exists(), not $user-authorized can be called without a CSRF nonce. It overwrites the victim's twofasecret on disk with an attacker-chosen value, returns the new secret in the JSON response, and the attacker computes a valid TOTP code to complete the 2FA flow. The second factor is reduced to password-only. The exploit was confirmed live after enabling 2FA to a user. Details Four code locations in login plugin v3.8.10 enable the chain: 1. Session user set even with 2FA pending user/plugins/login/login.php - userLogin() assigns $session-user = $user before TOTP verification completes. This makes $this-grav['user'] point to the victim in the pending-challenge window. 2. taskRegenerate2FASecret - no authorization check…

    Recommended action: Upgrade to 2.0.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  24. Medium, CVSS 6.5PackagistAmendedPublished

    Grav: Twig sandbox config exfiltration via grav.offsetGet + dump filter (CVE-2026-44738 bypass)

    CVE-2026-61450 · GHSA-mc5q-6hpj-rp7j

    Affected
    • Packagist getgrav/grav < 2.0.2
    Fixed in
    2.0.2· Vendor fix available
    Issue
    The Twig content sandbox replaces config with the redacted SandboxConfig facade and strips Config::get/toArray from the method allowlist (GHSA-j274-39qw-32c9), so editor content can't read config secrets via config. That's bypassable: grav is the raw container, offsetget is allow-listed on it, so grav.offsetGet('config') returns the real Config. The allow-listed filters jsonencode/printr/yamlencode then serialize it at the PHP level, never hitting the sandbox method gate, dumping the whole config tree including every plugins. secret (SMTP creds, API keys, plugin DB creds). Incomplete fix for GHSA-j274-39qw-32c9. security.salt does not leak (it lives outside config). Details The documented path is blocked: config is the SandboxConfig facade (Twig.php:660) and the raw Config/Data method entries are stripped when configaccess is false, so {{ config.get(...) }} returns the default and {{ grav.offsetGet('config').get(...) }} raises SecurityNotAllowedMethodError. The bypass uses…

    Recommended action: Upgrade to 2.0.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  25. Medium, CVSS 6.5PackagistAmendedPublished

    Grav: Decompression Bomb via ZipArchiver - Missing Extraction Limits

    CVE-2026-61455 · GHSA-928x-9mpw-8h56

    Affected
    • Packagist getgrav/grav < 2.0.1
    Fixed in
    2.0.1· Vendor fix available
    Issue
    ZipArchiver::extract() lacks limits on uncompressed size, file count, and nesting depth, creating a distinct, unpatched variant of the GHSA-2vcx-h8p2-9pg9 zip bomb vulnerability. While the parallel method Installer::unZip() received comprehensive limits, ZipArchiver::extract() remains unprotected, leaving a separate code path vulnerable to the same attack vector. The vulnerability is a distinct, unpatched variant of the bug described in GHSA-2vcx-h8p2-9pg9, as it affects a separate code path in the same codebase, implementing the same abstract class. --- Details Vulnerable code - system/src/Grav/Common/Filesystem/ZipArchiver.php:29-58: What's missing vs Installer::unZip(): | Protection | Installer::unZip() | ZipArchiver::extract() | |-----------|---------------------|------------------------| | Zip Slip guard | ✅ | ✅ | | Max uncompressed size | ✅ (1 GiB) | ❌ | | Max file count | ✅ (50000) | ❌ | | Max nesting depth | ✅ (48) | ❌ | | Pre-extraction validation | ✅ All entries…

    Recommended action: Upgrade to 2.0.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →