File tree Expand file tree Collapse file tree 3 files changed +32
-1
lines changed
Expand file tree Collapse file tree 3 files changed +32
-1
lines changed Original file line number Diff line number Diff line change @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. For info on
77### Security
88
99- [ CVE-2026 -25500] ( https://github.com/advisories/GHSA-whrj-4476-wvmp ) XSS injection via malicious filename in ` Rack::Directory ` .
10+ - [ CVE-2026 -22860] ( https://github.com/advisories/GHSA-mxw3-3hh2-x2mh ) Directory traversal via root prefix bypass in ` Rack::Directory ` .
1011
1112## [ 2.2.21] - 2025-11-03
1213
Original file line number Diff line number Diff line change @@ -76,6 +76,7 @@ def DIR_FILE_escape(htmls)
7676 # Set the root directory and application for serving files.
7777 def initialize ( root , app = nil )
7878 @root = ::File . expand_path ( root )
79+ @root_with_separator = @root . end_with? ( ::File ::SEPARATOR ) ? @root : "#{ @root } #{ ::File ::SEPARATOR } "
7980 @app = app || Files . new ( @root )
8081 @head = Head . new ( method ( :get ) )
8182 end
@@ -112,7 +113,9 @@ def check_bad_request(path_info)
112113 # Rack response to use for requests with paths outside the root, or nil if path is inside the root.
113114 def check_forbidden ( path_info )
114115 return unless path_info . include? ".."
115- return if ::File . expand_path ( ::File . join ( @root , path_info ) ) . start_with? ( @root )
116+
117+ expanded_path = ::File . expand_path ( ::File . join ( @root , path_info ) )
118+ return if expanded_path == @root || expanded_path . start_with? ( @root_with_separator )
116119
117120 body = "Forbidden\n "
118121 [ 403 , { CONTENT_TYPE => "text/plain" ,
Original file line number Diff line number Diff line change @@ -128,6 +128,33 @@ def setup
128128 res . must_be :forbidden?
129129 end
130130
131+ it "not allow directory traversal via root prefix bypass" do
132+ Dir . mktmpdir do |dir |
133+ root = File . join ( dir , "root" )
134+ outside = "#{ root } _test"
135+ FileUtils . mkdir_p ( root )
136+ FileUtils . mkdir_p ( outside )
137+ FileUtils . touch ( File . join ( outside , "test.txt" ) )
138+
139+ app = Rack ::Directory . new ( root )
140+ res = Rack ::MockRequest . new ( app ) . get ( "/../#{ File . basename ( outside ) } /" )
141+
142+ res . must_be :forbidden?
143+ end
144+ end
145+
146+ it "not allow dir globs" do
147+ Dir . mktmpdir do |dir |
148+ weirds = "uploads/.?/.?"
149+ full_dir = File . join ( dir , weirds )
150+ FileUtils . mkdir_p full_dir
151+ FileUtils . touch File . join ( dir , "secret.txt" )
152+ app = Rack ::Directory . new ( File . join ( dir , "uploads" ) )
153+ res = Rack ::MockRequest . new ( app ) . get ( "/.%3F" )
154+ refute_match "secret.txt" , res . body
155+ end
156+ end
157+
131158 it "404 if it can't find the file" do
132159 res = Rack ::MockRequest . new ( Rack ::Lint . new ( app ) ) .
133160 get ( "/cgi/blubb" )
You can’t perform that action at this time.
0 commit comments