Skip to content

Dancer::FileUtils::normalize_path produces paths that defeat containment checks #1247

Description

@bigpresh

Summary

Dancer::FileUtils::normalize_path (lib/Dancer/FileUtils.pm:86-100) does not fully normalise paths, and in several cases produces results that do not mean what the framework's string-based containment checks assume. Found during an AI-powered sweep of the codebase for quality & security.

Details

Empirically verified behaviours:

input output of normalize_path consequence
/foo/.. /foo/.. (unchanged) trailing .. never collapsed
../foo ../foo (unchanged) leading .. never collapsed
../../etc/passwd etc/passwd (relative) over-shooting produces a relative path resolved against the process CWD
../../../etc/passwd ../etc/passwd (relative) ditto
foo/../../../etc/passwd etc/passwd (relative) ditto
.\..\.. (Windows) untouched backslash separators are not handled

The helper collapses interior seg/../ sequences via a regex loop but leaves leading/trailing .., over-shooting .. (which become relative paths), and Windows separators in place.

Impact

  • Dancer::Renderer::get_file_response (lib/Dancer/Renderer.pm:214-238) guards static serving with a string-prefix check index($static_file, path($public)) == 0 — safe for typical Unix/absolute public, but the semantic gap between the normalised string and the real filesystem resolution is exactly where traversal/precedent bugs hide (e.g. relative-view-lookup cases, symlinks, Windows).
  • Template/auto_page lookups (Dancer::Template::Abstract::view, Dancer::Renderer::render_autopage) have no prefix check at all, so the relative-path outputs feed directly into -f/open.

Notes

  • Dancer 1 is in maintenance mode (all new dev on Dancer 2); this may not be fixed, and any fix should be evaluated for backwards-incompatibility.
  • Related but reported separately as a security advisory: path traversal in auto_page/template view lookup.
  • Cwd::realpath is imported but never used; a realpath-based containment check on both requested file and allowed root would close the gap.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions