v2.0.0
2 days ago
-
- Grav Version 2.0 stable is released - read all about it here: https://getgrav.org/blog/grav-2-stable-released
-
- [security] Install packages uploaded through Direct Install are now rejected when their contents exceed safe limits on total uncompressed size, file count, or folder nesting depth, so a crafted archive can no longer fill the disk, exhaust inodes, or crash extraction (GHSA-2vcx-h8p2-9pg9).
v2.0.0-rc.10
4 days ago
-
- [security] Image
resize in page content (for example ) now only accepts numeric dimensions, closing a stored CSS injection where a crafted resize value could write extra style declarations, such as a full-page overlay, into the image for a higher-privileged viewer (CWE-79). Media actions in an image URL are now limited to the documented set, so page content can no longer reach other internal methods on a media object, and inline styles are validated again when the image is rendered. Thanks to @DavidCarliez for the report.
- Twig in page content that puts an output tag inside an
if block, such as {% if x %}{{ y }}{% endif %}, no longer fails with an "Unknown endif tag" error when Markdown runs first. Fixes getgrav/grav#4126.
- Twig in the content of a modular page's modules, such as a
{% include %} tag, is now processed the same way it is in a regular page instead of being left as literal text. Fixes getgrav/grav#4142.
- Hyphenized anchors and slugs now keep accented and other Unicode letters such as
ä, ö and ü instead of mangling them, so on-page menu links to modules with those characters point to the right place. Thanks to @Xoriander. getgrav/grav#4143
v2.0.0-rc.9
6 days ago
-
- Added a
GRAV_ENV_PATH environment variable that loads the .env file(s) from a directory or file path outside the web root, so secrets such as API keys no longer have to live in the publicly served document root.
- Added an
onFlexObjectMedia event so a plugin can rewrite a flex object's media links, letting the original files be served through a controlled route while resized or cropped versions still load straight from the image cache.
-
- [security] Inline styles set on an image from page content (for example
) are now limited to safe layout CSS, so an editor can no longer store a full-page overlay or a url() callout that would target an administrator viewing the page (CWE-79). Thanks to @CyberKareem for the report.
- [security] Direct web access to the
user/accounts, user/config, user/data and user/env folders is now blocked in every bundled webserver config, closing a hole where files such as certificates, tokens and databases stored under user/data with an unlisted extension could be downloaded directly.
- [security] A backup deny-all
.htaccess now ships inside user/accounts, user/config and user/data so Apache installs stay protected even when the site root .htaccess has been customised or is out of date.
- [security] The upgrade postflight now patches an existing stock root
.htaccess to add the folder block automatically, so installs that updated from an earlier version are protected without editing the file by hand.
- The new
user/data block now makes an exception for public media uploads, such as Flex Object images, so they keep displaying instead of returning a 403, while data files, databases and keys stay blocked. Fixes getgrav/grav#4129.
- [security] The Twig filesystem helpers such as
read_file and file_exists now reject ../ path traversal and null bytes in their argument, an extra safeguard on top of the sandbox that already keeps these functions out of editor-authored page content.
v1.7.53
6 days ago
-
- [security] Direct web access to the
user/accounts, user/config, user/data and user/env folders is now blocked outright in every bundled webserver config, closing a hole where files such as certificates, tokens and databases stored under user/data with an unlisted extension could be downloaded directly.
- [security] A backup deny-all
.htaccess now ships inside user/accounts, user/config and user/data so Apache installs stay protected even when the site root .htaccess has been customised or is out of date.
- [security] The upgrade postflight now patches an existing stock root
.htaccess to add the folder block automatically, so installs that updated from an earlier version are protected without editing the file by hand.
- [security] URL query image transforms (such as
image.jpg?resize=) are now turned off by default and, when enabled, refuse oversized dimensions above a configurable pixel limit, closing an unauthenticated denial of service where huge resize values could exhaust server memory.
v2.0.0-rc.8
2 weeks ago
-
- Page Authors in a page's Security settings is now picked from a searchable list of the users who can edit pages, instead of typed-in usernames.
-
- [security] URL-based image resizing (e.g.
image.jpg?resize=2000,2000) is now off by default and, when enabled, capped by a configurable total-pixel limit, so an unauthenticated visitor can no longer exhaust server memory by requesting oversized image transforms (CWE-400). Thanks to @iliaal for the report.
- [security] With error display off, an uncaught error no longer leaks the file path, line, and exception message to a JSON or AJAX request, which now receives a generic JSON error instead (CWE-209). Thanks to @iliaal for the report.
- The default theme is now
quark2 to match the theme bundled with Grav 2.0, so reverting the theme setting in the Admin panel no longer leaves the site pointing at the missing quark theme. Fixes getgrav/grav#4108.
- A missing theme no longer takes the Admin panel and API down along with the frontend, so the site stays reachable to fix the theme setting.
- A Twig template that calls a function or filter which isn't registered in the current context, such as a plugin function referenced in a template while that plugin is inactive in the Admin panel, now renders as empty again instead of failing with an "Unknown function" error. This also restores form notification emails whose data template uses an unregistered filter, which were arriving with the raw
{% include %} tag in the body. Calls to real PHP functions still require an explicit safe_functions entry. Fixes getgrav/grav#4110 and getgrav/grav#4115.
- Twig in page content can again read media by filename under the security sandbox in deeply modular and nested layouts, so an expression like
{{ page.media['photo.jpg'].url }} resolves instead of leaking its raw {{ ... }} into the output. Fixes getgrav/grav#4114.
v2.0.0-rc.7
3 weeks ago
-
- Upgrading Grav core from the Admin panel no longer fails with "Failed to upgrade Grav core" because the installer misread the incoming release version and then wrongly flagged every installed plugin as incompatible; command line upgrades were unaffected.
v2.0.0-rc.6
3 weeks ago
-
- Added GitHub-style task lists:
- [ ] and - [x] now render as (disabled) checkboxes. Can be turned off under Configuration > System > Markdown.
- Added
==highlight==, ~subscript~, and ^superscript^ inline syntax, rendering as <mark>, <sub>, and <sup>. Can be turned off under Markdown settings.
- Disallowed raw HTML tags in page content (
script, iframe, style, textarea, and similar) are now escaped in the output to match GitHub Flavored Markdown. Can be turned off under Markdown settings.
- Bare
www. URLs and email addresses in content are now turned into links automatically (GitHub Flavored Markdown autolinks). Can be turned off under Markdown settings.
- Tables gained five optional extensions, all off by default and toggled under Markdown settings: an empty cell can merge into the cell on its left (colspan), a table can start at the divider row with no header row (header-less), a
[Caption] line immediately after a table becomes a caption, a {.class #id} line immediately after a table sets the class and id on the table element (the kramdown {:.class} form is accepted too), and a row ending in a backslash continues onto the next line so a cell can span multiple lines.
- Plugins can now add custom markdown block and inline syntax through a documented extension API with an element builder, instead of injecting closures and hand-building element arrays. The previous approach still works.
- Plugins can now register their own safe Twig functions for use inside sandboxed page content, so a plugin's custom functions work in editor-authored Twig without turning the security sandbox off.
- Grav now reads environment variables from a
.env file in the site root natively, so the separate DotEnv plugin is no longer needed; .env.local and per-environment files such as .env.production are layered on top in that order, and real server-set variables always take precedence. Use it to set things like GRAV_ENVIRONMENT or any GRAV_CONFIG__* override.
-
- [security] The
ZipArchiver extraction helper now refuses any archive entry whose path would escape the destination directory, bringing it in line with the Zip Slip guard already on the GPM installer (CWE-22). Thanks to @XananasX7 for the report.
- [security] Restricted scheduler job queue deserialization to the
Job class as defense in depth, on top of the existing HMAC integrity check on queue entries (CWE-502). Thanks to @XananasX7 for the report.
bin/grav server no longer shows the "PHP webserver requires a router" message and serves the site correctly when the Symfony local server falls back to PHP's built-in web server (for example on systems without php-fpm). Fixes getgrav/grav#4099.
- Markdown Extra no longer triggers an "implicitly marking parameter as nullable is deprecated" notice on PHP 8.4 and newer; the bundled Markdown Extra parser is now a maintained fork with the fix applied.
- Fenced code blocks with a trailing attribute block (for example
```python {#id .class}) now apply the id and classes to the code element instead of corrupting the language class. Requires Markdown Extra to be enabled.
- Twig in modular and editor page content can read page media again while the security sandbox is enabled, so expressions like
{{ page.media['photo.jpg'].url }} work without disabling the sandbox. Fixes getgrav/grav#4105.
- [security] Removed the
undefined_functions and undefined_filters Twig settings, which let any non-blocklisted PHP function be called from a template by default (deprecated since Grav 1.7). The safe_functions and safe_filters allow-lists remain as an explicit opt-in, empty by default, and now refuse command and code-execution functions outright so they can no longer be used to enable system, exec, and similar. Templates that called PHP functions directly will need those functions added to the allow-list, or registered through a plugin.
- Requests sending an empty or malformed
Accept header no longer trigger a "header string should not be empty" error during page content-type negotiation.
v2.0.0-rc.5
3 weeks ago
-
- Enabling Twig in content under Configuration > Security > Twig in Content is now the only setting needed to allow editor-authored Twig site-wide, and the per-page checkbox in the page editor follows the same setting so the admin UI and the live render always agree.
- Flex pages now honor the Twig in content security gate the same way classic pages do, both at render time and when busting cached content after the gate is changed.
- The security log no longer reports modular pages as Twig-blocked when they actually rendered, so the audit trail only lists pages the gate actually stopped.
-
- Loading the page index no longer returns a 500 when a single broken symlink or otherwise unreadable file sits inside a page folder; the offending path is logged to
grav.log and skipped so the rest of the pages still load.
- [security] The
evaluate_twig and evaluate Twig functions are now rendered through the same @Var: sandbox as Twig::processString(). Before this fix, both built a fresh Twig\Environment with no SandboxExtension or SourcePolicy, so a trusted theme that called {{ evaluate_twig(page.content|raw, {page: page}) }} rendered editor-authored Twig with the full unrestricted Twig surface — a complete sandbox bypass for any editor with page-edit access. The internal implementation now delegates to Twig::processString() so the same source policy, allowed-method/property/filter/function lists, and SandboxConfig denied-path facade all apply.
- The second argument to
{{ evaluate_twig(twig, variables) }} and {{ evaluate(expression, variables) }} is now honored. Both functions were registered with needs_context: true but their PHP signature only declared two parameters, so the caller's variables array was silently dropped and the docs example {{ evaluate_twig('{{ foo }}', {foo: 'bar'}) }} rendered nothing for foo. Caller variables now merge over the parent template context with caller winning. Fixes getgrav/grav#4098.
- Per-page content cache now invalidates on configuration changes, matching the pages-index cache. The page-content cache id now mixes in
$config->checksum() alongside the page identity, so any change to system, site, security, or plugin configuration evicts previously rendered output — including the new security.twig_content.* gates, every markdown rendering option, system.pages.twig_first, sandbox allow-lists, the summary delimiter, and the active plugin set. Previously the content cache only watched the page file's mtime, so toggling a security setting or enabling a plugin that subscribes to onPageContent* did nothing until a manual bin/grav cache --clear-all. The pages-index cache has used this strategy since 1.7; the per-page content cache was overlooked. Fixes getgrav/grav#4098.
v2.0.0-rc.4
1 month ago
-
- [security] Twig processing in page content is now off by default and configurable under Configuration > Security > Twig in Content, with separate toggles for enabling it site-wide, letting trusted editors turn it on per page, and exposing the
config variable to sandboxed renders. Pages that used Twig on the source site are detected during migration and the gate is flipped back on automatically.
-
- [security] Synced the bundled Twig fork with upstream 3.x to pick up its 2025-2026 sandbox-bypass and XSS security fixes (CVE-2026-46627, -46628, -46629, -46633, -46634, -46635, -46637, -46638, -46639, -46640, -47730, -47732, and -24425).
- Cleared the Twig 3.21+ deprecation warnings emitted by the bundled Deferred block extension, and added unit coverage for its defer/resolve flow including conditional child overrides.
- Refreshed the
bin/grav clean distribution-trim list for Grav 2.0: stripped stale entries for packages no longer shipped (Gregwar Image/Cache, MaximeBF DebugBar, MatthiasMullie Minify, PHIVE Twig Extensions Deferred, monolithic Symfony Contracts, Swiftmailer, Antimatter theme, Admin Classic vendor bundles) and added coverage for the new ones (Getgrav Image, Multiavatar, Tubalmartin CSSMin, Tedivm JShrink, PHP-DebugBar, Doctrine Deprecations, PSR Event Dispatcher, and the split Symfony Cache / String / Var-Exporter / Polyfill packages), shrinking the release zip and incidentally clearing a Sanesecurity phpcomment.UNOFFICIAL antivirus false positive against a test fixture image (#4088).
-
- Fixed a multi-site issue where saving a theme, plugin, or system config from the admin panel could silently create a stray
user/env/<host>/ folder when the request hostname differed from the page-load hostname (for example a proxy quirk delivering a bare hostname for the POST while page loads arrive as www.). Grav now refuses to materialize a per-host environment folder that does not already exist on disk and falls back to the shared user/config/ instead, matching the long-standing Grav 1.7 admin behavior (#4086).
- Fixed an issue where
CompiledFile (used for account, config, and other YAML reads) cached its parsed contents into the PHP session, so an admin's permission changes to a logged-in user wouldn't take effect until that user's session was destroyed. The file is now re-read fresh on each request.
v2.0.0-rc.3
1 month ago
-
- [security] Hardened client-IP resolution against forwarded-header spoofing: every
X-Forwarded-*, CF-Connecting-IP, and Client-IP header is now opt-in via its own system.http_x_forwarded.* toggle (defaults all false in 2.0), with FILTER_VALIDATE_IP enforced on the result (#4078).
-
- Fixed
bin/gpm commands silently exiting with no error on a fresh Grav 2.0 + Admin install before any user accounts had been created (#4079).
- Fixed JIT stack exhaustion in
Twig3CompatibilityTransformer when processing content with many Unicode characters (e.g. middle-dots), caused by catastrophic backtracking in the negative-lookahead regex of rewriteSameAsTests, rewriteDivisibleByTests, and rewriteNoneTests (#4015).