{"record":{"id":"a62b6bd06fd2ffe8","repo":"getgrav/grav","slug":"twig-map-arrow-is-not-allowed","errorCode":null,"errorMessage":"Twig |map(\"{arrow}\") is not allowed.","messagePattern":"Twig \\|map\\(\"(.+?)\"\\) is not allowed\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Common/Twig/Extension/GravExtension.php","lineNumber":2091,"sourceCode":"\n        if ($array === null) {\n            $array = [];\n        }\n\n        return twig_array_filter($env, $array, $arrow);\n    }\n\n    /**\n     * @param Environment $env\n     * @param array $array\n     * @param callable|string $arrow\n     * @return array|CallbackFilterIterator\n     * @throws RuntimeError\n     */\n    function mapFunc(Environment $env, $array, $arrow)\n    {\n        if (!$arrow instanceof \\Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {\n            throw new RuntimeError('Twig |map(\"' . $arrow . '\") is not allowed.');\n        }\n\n        if ($array === null) {\n            $array = [];\n        }\n\n        return twig_array_map($env, $array, $arrow);\n    }\n\n    /**\n     * @param Environment $env\n     * @param array $array\n     * @param callable|string $arrow\n     * @return array|CallbackFilterIterator\n     * @throws RuntimeError\n     */\n    function reduceFunc(Environment $env, $array, $arrow)\n    {","sourceCodeStart":2073,"sourceCodeEnd":2109,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Common/Twig/Extension/GravExtension.php#L2073-L2109","documentation":"Grav's override of Twig's |map throws this RuntimeError before calling twig_array_map when the arrow is neither a \\Closure nor a string, or when Utils::isDangerousFunction() flags the string (exec, system, passthru, shell_exec, popen, proc_open, pcntl_exec, assert, preg_replace, create_function, include/require, and callback-style functions). The guard exists because outside the sandbox Twig would happily call map('system', $value, $key) per element, turning a template into arbitrary command execution.","triggerScenarios":"{{ items|map('exec') }} or another denylisted function name in any template; a null or array-callable arrow (['Class', 'method']) which fails the Closure-or-string check; legacy Twig templates using PHP function names as the map callback (e.g. |map('ucfirst') works, but any denylisted or non-string callable throws).","commonSituations":"Porting old themes to current Grav; copy-pasted snippets from old cookbook examples; template-injection attempts showing up in error logs; variables used as callbacks that are unexpectedly null.","solutions":["Rewrite the callback as an arrow function: {{ items|map(v => v.title|upper) }}","Wrap class methods instead of array callables: {{ items|map(v => MyClass::label(v)) }}","Investigate any denylisted function name in a template as a possible injection, not just a style problem","Initialize callback variables and never pass null to |map"],"exampleFix":"{# before: string callable #}\n{{ items|map('shell_exec') }}\n\n{# after: arrow function Closure #}\n{{ items|map(v => v.title|upper) }}","handlingStrategy":"validation","validationCode":"// guard the arrow before render (mirrors mapFunc's check)\n$ok = $arrow instanceof \\Closure || (is_string($arrow) && !Utils::isDangerousFunction($arrow));\nif (!$ok) { $arrow = fn($v) => $v; // safe default or fail fast\n}","typeGuard":"function isSafeTwigArrow(mixed $arrow): bool\n{\n    return $arrow instanceof \\Closure || (is_string($arrow) && !\\Grav\\Common\\Utils::isDangerousFunction($arrow));\n}","tryCatchPattern":"use Twig\\Error\\RuntimeError;\ntry { echo $twig->render($template, $data); }\ncatch (RuntimeError $e) { log_template_error($e); // record template name/line for audit\n}","preventionTips":["Always write |map callbacks as arrow functions: |map(v => expr)","Initialize every variable used as a callback; null throws","Ban string callables in templates via code review/CI grep for |map('","Investigate denylisted names in templates as injection attempts"],"tags":["twig","security","filter","callable","rce-guard"],"backgroundTag":"twig-unsafe-callable","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}