getgrav/grav · error · RuntimeError
Twig |find("{arrow}") is not allowed.
Error message
Twig |find("{arrow}") is not allowed. What it means
Grav's hardened |find override checks the arrow up front — it must be a \Closure or a string not flagged by Utils::isDangerousFunction() — and then delegates to CoreExtension::find with the resolved sandbox state. Twig core only rejects a dangerous string callable (e.g. find('system') invoked as system($v, $k)) inside the sandbox; Grav's denylist is deliberately defense-in-depth for renders outside the sandbox too, which is why a bad callable fails here regardless of sandbox state.
Source
Thrown at system/src/Grav/Common/Twig/Extension/GravExtension.php:2144
* a page editor RCE (GHSA-xx48-97m4-h7qm). Apply the same dangerous-arrow guard
* used by filter/map/reduce, regardless of sandbox state; a real arrow closure
* still passes.
*
* The resolved sandbox state is passed through to Twig, so a string callable is
* also refused whenever the render is sandboxed (GHSA-p6qj-p5m7-f62h). The
* denylist above is defense-in-depth, not the only guard.
*
* @param Environment $env
* @param bool $isSandboxed
* @param mixed $array
* @param callable|string $arrow
* @return mixed
* @throws RuntimeError
*/
function findFunc(Environment $env, bool $isSandboxed, $array, $arrow)
{
if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
throw new RuntimeError('Twig |find("' . $arrow . '") is not allowed.');
}
return CoreExtension::find($env, $isSandboxed, $array ?? [], $arrow);
}
/**
* Hardened `sort` filter. Same rationale as findFunc(): a string comparator
* such as `sort('system')` would otherwise be called as `system($a, $b)` when
* rendered outside the sandbox. Plain sorts (no comparator) are unaffected.
*
* The resolved sandbox state is passed through so a string comparator is refused
* in sandbox mode (GHSA-p6qj-p5m7-f62h); hardcoding it off left the denylist as
* the only guard, and the denylist does not list every two-argument callable.
*
* @param Environment $env
* @param bool $isSandboxed
* @param mixed $array
* @param callable|string|null $arrowView on GitHub (pinned to 6040efed04)
Solutions
- Rewrite the predicate as an arrow function: {{ items|find(v => v.slug == 'about') }}
- Wrap class methods instead of array callables: {{ items|find(v => MyClass::matches(v)) }}
- If a denylisted name appears in template source or page content, audit for injection — the guard firing is a security signal, not noise
- Ensure the arrow argument is always provided and never null
Example fix
{# before: string callable #}
{{ items|find('system') }}
{# after: arrow function Closure #}
{{ items|find(v => v.slug == 'about') }} Defensive patterns
Strategy: validation
Validate before calling
// guard the arrow before render (mirrors findFunc's check)
$ok = $arrow instanceof \Closure || (is_string($arrow) && !Utils::isDangerousFunction($arrow));
if (!$ok) { $arrow = fn($v) => false; // safe default or fail fast
} Type guard
function isSafeTwigArrow(mixed $arrow): bool
{
return $arrow instanceof \Closure || (is_string($arrow) && !\Grav\Common\Utils::isDangerousFunction($arrow));
} Try / catch
use Twig\Error\RuntimeError;
try { echo $twig->render($template, $data); }
catch (RuntimeError $e) { log_template_error($e); // treat dangerous-name hits as injection signals
} Prevention
- Write |find predicates as arrow functions: |find(v => cond)
- Never pass PHP function names as the find predicate
- CI-grep for |find(' and reject the pattern
- Keep Grav/Twig current so the hardened overrides stay in place
When it happens
Trigger: {{ items|find('system') }} or any denylisted function name in any template; a null or array-callable arrow (fails the Closure-or-string check); template-injection payloads crafted for Twig's arrow-argument filters (find, sort, filter, map, reduce).
Common situations: Attack payloads in submitted content that reached a template render; legacy snippets using string callables; old tutorials for 'find first matching item' written with PHP function names.
Related errors
- Twig |filter("{arrow}") is not allowed.
- Twig |map("{arrow}") is not allowed.
- Twig |reduce("{arrow}") is not allowed.
- Twig |sort("{arrow}") is not allowed.
- The callable passed to the "array_group_by" filter must be a
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/223492696dea11c6.
Report an issue: GitHub.