getgrav/grav · error · RuntimeError
Twig |filter("{arrow}") is not allowed.
Error message
Twig |filter("{arrow}") is not allowed. What it means
Grav overrides Twig core's |filter and rejects the arrow before delegating to twig_array_filter when it is neither a \Closure nor a string, or when Utils::isDangerousFunction() flags the string. That denylist covers command execution (exec, system, passthru, shell_exec, popen, proc_open, pcntl_exec), code execution (assert, preg_replace, create_function, include/require) and callback-style PHP functions. Without the guard, |filter('system') in an unsandboxed template would invoke system($v, $k) per element — remote code execution.
Source
Thrown at system/src/Grav/Common/Twig/Extension/GravExtension.php:2071
'numeric' => is_numeric($var),
'object' => is_object($var),
'scalar' => is_scalar($var),
'string' => is_string($var),
default => false,
};
}
/**
* @param Environment $env
* @param array $array
* @param callable|string $arrow
* @return array|CallbackFilterIterator
* @throws RuntimeError
*/
function filterFunc(Environment $env, $array, $arrow)
{
if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.');
}
if ($array === null) {
$array = [];
}
return twig_array_filter($env, $array, $arrow);
}
/**
* @param Environment $env
* @param array $array
* @param callable|string $arrow
* @return array|CallbackFilterIterator
* @throws RuntimeError
*/
function mapFunc(Environment $env, $array, $arrow)
{View on GitHub (pinned to 6040efed04)
Solutions
- Rewrite the callback as a Twig arrow function: {{ list|filter(v => v.published) }}
- For class-method callables, wrap them: {{ list|filter(v => MyClass::isVisible(v)) }}
- If a denylisted name like system appears in a template, treat it as a security incident: audit the template source and how it was written, do not just patch around it
- Ensure the arrow argument is never null — guard variables used as callbacks
Example fix
{# before: string callable (legacy or injected) #}
{{ items|filter('system') }}
{# after: arrow function Closure #}
{{ items|filter(v => v.published) }} Defensive patterns
Strategy: validation
Validate before calling
// guard the arrow before render (mirrors filterFunc's check)
$ok = $arrow instanceof \Closure || (is_string($arrow) && !Utils::isDangerousFunction($arrow));
if (!$ok) { $arrow = fn($v) => $v; // replace with a safe Closure 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_and_source($e->getSourceContext()); // audit, don't silently retry
} Prevention
- Always write |filter callbacks as arrow functions: |filter(v => cond)
- Never pass PHP function names as strings into template callbacks
- Grep templates for |filter(' in CI and reject the pattern
- Treat a dangerous-name hit as attempted template injection and audit its origin
When it happens
Trigger: A template containing {{ list|filter('system') }} or another denylisted function name (attack payload or leftover debug code); passing a null arrow ({{ list|filter(null-var) }} — null is neither Closure nor string, so it throws); array-style callables like ['MyClass', 'method'] which are neither Closure nor string; old Twig 1.x/2.x-era snippets using string callables.
Common situations: Migrating legacy themes to current Grav/Twig; attempted template injection visible in logs; static analysis flagging string callables in templates; passing uninitialized variables as the arrow.
Related errors
- Twig |map("{arrow}") is not allowed.
- Twig |reduce("{arrow}") is not allowed.
- Twig |find("{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/79e9838297d7b4de.
Report an issue: GitHub.