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 $arrow

View on GitHub (pinned to 6040efed04)

Solutions

  1. Rewrite the predicate as an arrow function: {{ items|find(v => v.slug == 'about') }}
  2. Wrap class methods instead of array callables: {{ items|find(v => MyClass::matches(v)) }}
  3. If a denylisted name appears in template source or page content, audit for injection — the guard firing is a security signal, not noise
  4. 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

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


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/223492696dea11c6. Report an issue: GitHub.