{"record":{"id":"fe0becd5b521776c","repo":"twigphp/Twig","slug":"a-template-name-cannot-contain-nul-bytes","errorCode":null,"errorMessage":"A template name cannot contain NUL bytes.","messagePattern":"A template name cannot contain NUL bytes\\.","errorType":"exception","errorClass":"LoaderError","httpStatus":null,"severity":"error","filePath":"src/Loader/FilesystemLoader.php","lineNumber":261,"sourceCode":"    {\n        if (isset($name[0]) && '@' == $name[0]) {\n            if (false === $pos = strpos($name, '/')) {\n                throw new LoaderError(\\sprintf('Malformed namespaced template name \"%s\" (expecting \"@namespace/template_name\").', $name));\n            }\n\n            $namespace = substr($name, 1, $pos - 1);\n            $shortname = substr($name, $pos + 1);\n\n            return [$namespace, $shortname];\n        }\n\n        return [$default, $name];\n    }\n\n    private function validateName(string $name): void\n    {\n        if (str_contains($name, \"\\0\")) {\n            throw new LoaderError('A template name cannot contain NUL bytes.');\n        }\n\n        $name = ltrim($name, '/');\n        $parts = explode('/', $name);\n        $level = 0;\n        foreach ($parts as $part) {\n            if ('..' === $part) {\n                --$level;\n            } elseif ('.' !== $part) {\n                ++$level;\n            }\n\n            if ($level < 0) {\n                throw new LoaderError(\\sprintf('Looks like you try to load a template outside configured directories (%s).', $name));\n            }\n        }\n    }\n","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/Loader/FilesystemLoader.php#L243-L279","documentation":"Twig's FilesystemLoader rejects template names containing NUL (\\0) bytes before resolving them against configured directories. NUL bytes in paths can truncate the path in underlying filesystem calls and are a classic path-injection vector, so the loader throws a LoaderError immediately in validateName.","triggerScenarios":"Calling $loader->getSourceContext($name) / loadTemplate / findTemplate with a name containing a literal \"\\0\", e.g. user-supplied template identifiers concatenated with binary data or unsanitized request input.","commonSituations":"Template names taken from HTTP query/body parameters or database values that contain binary junk; encoding bugs where \"\\u0000\" from JSON or legacy PHP code (old null-byte trim behavior) leaks into the template name.","solutions":["Trim or reject NUL bytes from the template name before passing it to the loader: $name = str_replace(\"\\0\", '', $name);","Validate template names against an allowlist regex (e.g. /^[A-Za-z0-9_.\\-]+$/) before loading.","Never pass raw user input as a template name; map user choices to known template names.","Log the offending name to find where the NUL byte enters (JSON decode, binary file read, etc.)."],"exampleFix":"// before\n$template = $_GET['page'] . '.html.twig';\necho $twig->render($template);\n// after\n$name = $_GET['page'] . '.html.twig';\nif (!preg_match('/^[A-Za-z0-9_.\\-]+\\.html\\.twig$/', $name)) {\n    throw new InvalidArgumentException('Invalid template name.');\n}\necho $twig->render($name);","handlingStrategy":"validation","validationCode":"if (str_contains($name, \"\\0\")) {\n    throw new InvalidArgumentException('Template name must not contain NUL bytes.');\n}","typeGuard":"function isSafeTemplateName(string $name): bool {\n    return $name !== '' && !str_contains($name, \"\\0\")\n        && preg_match('/^[A-Za-z0-9_.\\-]+$/', $name) === 1;\n}","tryCatchPattern":"try {\n    $html = $twig->render($name);\n} catch (\\Twig\\Error\\LoaderError $e) {\n    if (str_contains($e->getMessage(), 'NUL bytes')) {\n        // sanitize or reject the template name\n    }\n    throw $e;\n}","preventionTips":["Never pass raw user input as a template name; map to an allowlist of known templates.","Sanitize names with str_replace(\"\\0\", '', $name) at the boundary.","Use strict validation (regex) on any dynamic template identifier.","Audit sources of binary data (JSON decode, file reads) that feed template names."],"tags":["template","loader","security","input-validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}