{"record":{"id":"dcd18f2f3db04db3","repo":"twigphp/Twig","slug":"looks-like-you-try-to-load-a-template-outside-configured","errorCode":null,"errorMessage":"Looks like you try to load a template outside configured directories (%s).","messagePattern":"Looks like you try to load a template outside configured directories \\((.+?)\\)\\.","errorType":"exception","errorClass":"LoaderError","httpStatus":null,"severity":"error","filePath":"src/Loader/FilesystemLoader.php","lineNumber":275,"sourceCode":"\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\n    private function isAbsolutePath(string $file): bool\n    {\n        return strspn($file, '/\\\\', 0, 1)\n            || (\\strlen($file) > 3 && ctype_alpha($file[0])\n                && ':' === $file[1]\n                && strspn($file, '/\\\\', 2, 1)\n            )\n            || null !== parse_url($file, \\PHP_URL_SCHEME)\n        ;\n    }\n}\n","sourceCodeStart":257,"sourceCodeEnd":291,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/Loader/FilesystemLoader.php#L257-L291","documentation":"FilesystemLoader resolves template names relative to configured root directories while forbidding traversal above them (via \"../\" segments tracked as a level counter in validateName). If the normalized path walks out of every root, Twig throws this LoaderError to prevent reading arbitrary files outside the template directories.","triggerScenarios":"Calling findTemplate (via render/load/getSourceContext) with a name whose normalized path escapes the loader roots, e.g. \"../../etc/passwd\" or \"../secrets.html.twig\" — typically when the name comes from user input.","commonSituations":"Path traversal attacks on template-name parameters; dynamic template paths miscomputed with a leading \"..\"; misconfigured loader roots so legitimate relative paths appear to escape.","solutions":["Reject or sanitize template names containing \"..\" segments before passing them to the loader.","Ensure the loader root(s) are set correctly (new FilesystemLoader($rootDir), addPath()) so relative names resolve inside the project.","Whitelist template names (regex or fixed set) instead of accepting raw input.","If a legitimate template lives outside the root, register its directory with addPath() instead of using \"../\" in names.","As defense in depth, use the sandbox extension when templates are user-influenced."],"exampleFix":"// before\n$page = $_GET['page']; // \"../../etc/passwd\"\necho $twig->render($page . '.twig');\n// after\n$page = basename((string) $_GET['page']); // strip traversal\nif (!preg_match('/^[\\w\\-]+$/', $page)) {\n    throw new InvalidArgumentException('Invalid page.');\n}\necho $twig->render($page . '.twig');","handlingStrategy":"validation","validationCode":"function isTemplateInsideRoots(string $name): bool {\n    $name = ltrim(str_replace('\\\\', '/', $name), '/');\n    $level = 0;\n    foreach (explode('/', $name) as $part) {\n        if ($part === '..') { $level--; }\n        elseif ($part !== '.') { $level++; }\n        if ($level < 0) { return false; }\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    $html = $twig->render($name);\n} catch (\\Twig\\Error\\LoaderError $e) {\n    if (str_contains($e->getMessage(), 'outside configured directories')) {\n        // treat as forbidden path; log and return 404\n    }\n    throw $e;\n}","preventionTips":["Normalize and reject \"..\" segments in template names before loading.","Configure loader roots correctly via the constructor/addPath() and never use \"..\" in names.","Whitelist allowed template names for user-facing rendering.","Use the sandbox extension when rendering user-influenced templates."],"tags":["template","loader","security","path-traversal"],"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"}