{"record":{"id":"6b82093bdda62852","repo":"Shopify/liquid","slug":"illegal-template-path-file-expand-path-full-pat","errorCode":null,"errorMessage":"Illegal template path '#{File.expand_path(full_path)}'","messagePattern":"Illegal template path '#(.+?)'","errorType":"exception","errorClass":"Liquid::FileSystemError","httpStatus":null,"severity":"error","filePath":"lib/liquid/file_system.rb","lineNumber":70,"sourceCode":"    end\n\n    def read_template_file(template_path)\n      full_path = full_path(template_path)\n      raise FileSystemError, \"No such template '#{template_path}'\" unless File.exist?(full_path)\n\n      File.read(full_path)\n    end\n\n    def full_path(template_path)\n      raise FileSystemError, \"Illegal template name '#{template_path}'\" unless %r{\\A[^./][a-zA-Z0-9_/]+\\z}.match?(template_path)\n\n      full_path = if template_path.include?('/')\n        File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))\n      else\n        File.join(root, @pattern % template_path)\n      end\n\n      raise FileSystemError, \"Illegal template path '#{File.expand_path(full_path)}'\" unless File.expand_path(full_path).start_with?(File.expand_path(root))\n\n      full_path\n    end\n  end\nend\n","sourceCodeStart":52,"sourceCodeEnd":76,"githubUrl":"https://github.com/Shopify/liquid/blob/807d45a6b3d4568e64e86b375e3702df2c7c860c/lib/liquid/file_system.rb#L52-L76","documentation":"After expanding the template name against root, full_path re-checks that File.expand_path(full_path) still starts with File.expand_path(root); otherwise it raises Liquid::FileSystemError with the expanded path. This is the final defense against names that resolve outside the template root (e.g. via symlinks or '..' slipping past the regex).","triggerScenarios":"A template name whose resolved absolute path escapes the configured root — typically via '..' segments, a misconfigured root, or a symlink inside the template directory pointing elsewhere.","commonSituations":"Configuring root with a relative or trailing-slash path that expands differently than expected; template directories containing symlinks to other locations; names combining subdirectories with '..'.","solutions":["Fix the template name so it resolves inside the root (remove '..' segments).","Set root to an absolute, canonical directory (File.expand_path) when constructing LocalFileSystem.","Remove or replace symlinks in the template directory that point outside the root.","Log/inspect the expanded path in the message to see where the resolution went wrong."],"exampleFix":"// before\nLiquid::LocalFileSystem.new('templates/../views', '%s.liquid')\n// after\nLiquid::LocalFileSystem.new(File.expand_path('views'), '%s.liquid')","handlingStrategy":"validation","validationCode":"def within_root?(fs, name)\n  File.expand_path(fs.full_path(name)).start_with?(File.expand_path(fs.root))\nrescue Liquid::FileSystemError\n  false\nend","typeGuard":"def safe_resolve(fs, name)\n  full = File.expand_path(fs.full_path(name))\n  full.start_with?(File.expand_path(fs.root)) ? full : nil\nrescue Liquid::FileSystemError\n  nil\nend","tryCatchPattern":"begin\n  tpl.render(ctx)\nrescue Liquid::FileSystemError => e\n  raise unless e.message.start_with?('Illegal template path')\n  security_logger.warn(e.message)\n  render_error_page\nend","preventionTips":["Configure root as an absolute, canonical path (File.expand_path).","Avoid symlinks inside template directories pointing outside root.","Never accept '..' in template names from any external source.","Audit the template root periodically for unexpected entries."],"tags":["ruby","liquid","path-traversal","security","file-system"],"backgroundTag":"path-traversal-blocked","analyzedSha":"807d45a6b3d4568e64e86b375e3702df2c7c860c","analyzedAt":"2026-09-08T11:31:38.917Z","contentChangedAt":"2026-09-08T11:31:38.917Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}