psf-grav-core-check(8)
Grav CMS core allowlist bypass through Flex form overrides: read-only checker (v1)
CVE-2026-116019
If this software is helpful, consider a donation to our 501c3 nonprofit:
https://privacysafe.foundation
Grav 2.0.15 before 2.2.13 lets a user who may edit pages run arbitrary public static
methods. Grav marks the dynamic directives in a blueprint (the data-*@ fields that name a
PHP method to call) as trusted only where the blueprint file itself defines them, and
checks the ones that arrive from page frontmatter against an allowlist. The merge that
combines the two could relocate a directive away from the path it was checked at: the
vendor deepMerge() honors the unset-name@ merge action as well as replace-name@, and it
renumbers an integer key the base does not have. A relocated directive was then trusted as
if the blueprint file had defined it, so the allowlist was skipped. A page editor can embed
crafted fields in the Flex form overrides of a page's frontmatter and call methods such as
Folder::delete or Utils::download, which deletes directories, overwrites files, or reads
files outside the page tree. CVSS 8.8 (v3.1), 8.7 (v4.0). No exploitation was reported at
the time of the sources used. Found through a GitHub security advisory
(GHSA-47gg-mh38-9rr7), published 24 October 2026.
Affected: Grav 2.0.15 up to and including 2.2.12
Fixed: 2.2.13 (24 October 2026)
How this script reads a site. The version is the GRAV_VERSION constant in
system/defines.php. The script also reads system/src/Grav/Common/Data/Blueprint.php for
the two things the fix adds: the dynamicOf() helper that compares the directives before and
after the merge, and the extension of the stripping from replace-name@ to unset-name@. Both
present means the fix is there, whatever the version string says; neither present on a
version in the affected range means exposed; one of the two is a partial patch.
This script is read-only. "hunt" also looks in the site's pages for what an attack needs:
a relocation merge action (unset-name@, replace-name@) or a static method call in page
frontmatter. Those are indicators to investigate: a relocation directive together with a static
method call in the same page is a strong lead, either one alone is only listed for review.
A clean hunt means none of the published indicators were found.
Usage:
./grav-core-check.sh [command] [DIR ...]
check (default) scan DIR (default /var/www /srv /opt /home) for Grav sites. Exit 0 =
nothing exposed, 1 = an exposed or unverifiable site.
hunt check, then look in the pages for the indicators above. Exit 0 =
nothing found, 1 = an exposed site or a strong lead.
version print the script version.
Containers: with no directory argument the check also copies the files it reads from running
docker and podman containers (SCAN_CONTAINERS=1, the default; 0 skips them) and judges them like
host installs. The copies are temporary: a path shown as .../docker_NAME_PATH is the PATH inside
the container NAME.The Security Patches software and this documentation are dedicated to the public domain under CC0 1.0 Universal. Other pages on privacysafe.app/docs are CC BY-SA 4.0 unless the page says otherwise.