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.
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:
normalize_path/foo/../foo/..(unchanged)..never collapsed../foo../foo(unchanged)..never collapsed../../etc/passwdetc/passwd(relative)../../../etc/passwd../etc/passwd(relative)foo/../../../etc/passwdetc/passwd(relative).\..\..(Windows)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 checkindex($static_file, path($public)) == 0— safe for typical Unix/absolutepublic, 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).auto_pagelookups (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
auto_page/template view lookup.Cwd::realpathis imported but never used; arealpath-based containment check on both requested file and allowed root would close the gap.