Skip to content

Commit f9bde3b

Browse files
Masamuneeeioquatix
authored andcommitted
Prevent directory traversal via root prefix bypass.
1 parent 93a68f5 commit f9bde3b

File tree

3 files changed

+20
-1
lines changed

3 files changed

+20
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff 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
### Fixed
1213

lib/rack/directory.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def DIR_FILE_escape(htmls)
8282
# Set the root directory and application for serving files.
8383
def initialize(root, app = nil)
8484
@root = ::File.expand_path(root)
85+
@root_with_separator = @root.end_with?(::File::SEPARATOR) ? @root : "#{@root}#{::File::SEPARATOR}"
8586
@app = app || Files.new(@root)
8687
@head = Head.new(method(:get))
8788
end
@@ -118,7 +119,9 @@ def check_bad_request(path_info)
118119
# Rack response to use for requests with paths outside the root, or nil if path is inside the root.
119120
def check_forbidden(path_info)
120121
return unless path_info.include? ".."
121-
return if ::File.expand_path(::File.join(@root, path_info)).start_with?(@root)
122+
123+
expanded_path = ::File.expand_path(::File.join(@root, path_info))
124+
return if expanded_path == @root || expanded_path.start_with?(@root_with_separator)
122125

123126
body = "Forbidden\n"
124127
[403, { CONTENT_TYPE => "text/plain",

test/spec_directory.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,21 @@ def setup
149149
res.must_be :forbidden?
150150
end
151151

152+
it "not allow directory traversal via root prefix bypass" do
153+
Dir.mktmpdir do |dir|
154+
root = File.join(dir, "root")
155+
outside = "#{root}_test"
156+
FileUtils.mkdir_p(root)
157+
FileUtils.mkdir_p(outside)
158+
FileUtils.touch(File.join(outside, "test.txt"))
159+
160+
app = Rack::Directory.new(root)
161+
res = Rack::MockRequest.new(app).get("/../#{File.basename(outside)}/")
162+
163+
res.must_be :forbidden?
164+
end
165+
end
166+
152167
it "not allow dir globs" do
153168
Dir.mktmpdir do |dir|
154169
weirds = "uploads/.?/.?"

0 commit comments

Comments
 (0)