{"record":{"id":"bb9b1ea5e23f6116","repo":"Shopify/liquid","slug":"illegal-template-name-template-path","errorCode":null,"errorMessage":"Illegal template name '#{template_path}'","messagePattern":"Illegal template name '#(.+?)'","errorType":"exception","errorClass":"Liquid::FileSystemError","httpStatus":null,"severity":"error","filePath":"lib/liquid/file_system.rb","lineNumber":62,"sourceCode":"  #   file_system.full_path(\"index\") # => \"/some/path/index.html\"\n  #\n  class LocalFileSystem\n    attr_accessor :root\n\n    def initialize(root, pattern = \"_%s.liquid\")\n      @root    = root\n      @pattern = pattern\n    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":44,"sourceCodeEnd":76,"githubUrl":"https://github.com/Shopify/liquid/blob/807d45a6b3d4568e64e86b375e3702df2c7c860c/lib/liquid/file_system.rb#L44-L76","documentation":"LocalFileSystem#full_path raises Liquid::FileSystemError when the template name fails the allowed pattern (must start with a non-dot, non-slash character and contain only alphanumerics, underscores, and slashes). This rejects names that could escape the template root or reference odd paths.","triggerScenarios":"Passing a template name containing '.', '..', leading '/', spaces, hyphens, or other disallowed characters to {% include %}/{% render %}, e.g. {% include '../secret' %} or {% include 'my-file' %}.","commonSituations":"Dynamic include names built from user data or URLs (which contain dots/hyphens); attempting directory traversal via '../'; using file names with extensions inside the tag (the extension comes from the pattern instead).","solutions":["Sanitize the template name: strip extensions and disallowed characters before it reaches the include tag.","Use only [a-zA-Z0-9_/] characters (no dots, hyphens, or leading slash) in template names.","Never build include names from raw user input; map user choices to a whitelist of template names.","If you need different naming, subclass LocalFileSystem and override full_path with your own validation."],"exampleFix":"// before\n{% include '{{ user_input }}' %}  # user_input = \"blog/post.html\"\n// after\n{% assign slug = user_input | split: '.' | first %}\n{% include slug %}  # \"blog/post\" — safe characters only","handlingStrategy":"validation","validationCode":"TEMPLATE_NAME_RE = /\\A[a-zA-Z0-9_]+(?:\\/[a-zA-Z0-9_]+)*\\z/\ndef safe_template_name?(name)\n  TEMPLATE_NAME_RE.match?(name.to_s)\nend","typeGuard":"def sanitize_template_name(input)\n  name = input.to_s.sub(/\\.[^.]*\\z/, '').gsub(/[^a-zA-Z0-9_\\/]/, '')\n  TEMPLATE_NAME_RE.match?(name) ? name : nil\nend","tryCatchPattern":"begin\n  tpl.render(ctx)\nrescue Liquid::FileSystemError => e\n  raise unless e.message.start_with?('Illegal template name')\n  render_error_page('Invalid template reference')\nend","preventionTips":["Never derive include names directly from user input or URLs.","Whitelist allowed template names instead of sanitizing arbitrary input.","Strip file extensions from dynamic names before including.","Keep names to alphanumerics, underscores, and slashes only."],"tags":["ruby","liquid","path-traversal","validation","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"}