Skip to content

Commit 127da70

Browse files
JackMctenderlove
authored andcommitted
Only write relative URIs when their normalized path begins with the normalized cache directory path
[CVE-2020-8159]
1 parent b775ea2 commit 127da70

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

lib/action_controller/caching/pages.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def cache_directory
9393
end
9494
end
9595

96+
def normalized_cache_directory
97+
File.expand_path(cache_directory)
98+
end
99+
96100
def handle_proc_cache_directory
97101
if @controller
98102
@controller.instance_exec(&@cache_directory)
@@ -153,15 +157,22 @@ def cache_file(path, extension)
153157
end
154158

155159
def cache_path(path, extension = nil)
156-
File.join(cache_directory, cache_file(path, extension))
160+
unnormalized_path = File.join(normalized_cache_directory, cache_file(path, extension))
161+
normalized_path = File.expand_path(unnormalized_path)
162+
163+
relative_path if normalized_path.start_with?(normalized_cache_directory)
157164
end
158165

159166
def delete(path)
167+
return unless path
168+
160169
File.delete(path) if File.exist?(path)
161170
File.delete(path + ".gz") if File.exist?(path + ".gz")
162171
end
163172

164173
def write(content, path, gzip)
174+
return unless path
175+
165176
FileUtils.makedirs(File.dirname(path))
166177
File.open(path, "wb+") { |f| f.write(content) }
167178

test/caching_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
require "abstract_unit"
22
require "mocha/setup"
3+
require "find"
34

45
CACHE_DIR = "test_cache"
56
# Don't change "../tmp" cavalierly or you might hose something you don't want hosed
67
TEST_TMP_DIR = File.expand_path("../tmp", __FILE__)
78
FILE_STORE_PATH = File.join(TEST_TMP_DIR, CACHE_DIR)
89

10+
911
module PageCachingTestHelpers
1012
def setup
1113
super
@@ -175,6 +177,25 @@ class PageCachingTest < ActionController::TestCase
175177
include PageCachingTestHelpers
176178
tests PageCachingTestController
177179

180+
def test_cache_does_not_escape
181+
draw do
182+
get "/page_caching_test/ok/:id", to: "page_caching_test#ok"
183+
end
184+
185+
project_root = File.expand_path("../../", __FILE__)
186+
187+
188+
# Make a path that escapes the cache directory
189+
get_to_root = "../../../"
190+
191+
# Make sure this relative path points at the project root
192+
assert_equal project_root, File.expand_path(File.join(FILE_STORE_PATH, get_to_root))
193+
194+
get :ok, params: { id: "#{get_to_root}../pwnd" }
195+
196+
assert_predicate Find.find(File.join(project_root, "test")).grep(/pwnd/), :empty?
197+
end
198+
178199
def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
179200
draw do
180201
get "posts.:format", to: "posts#index", as: :formatted_posts

0 commit comments

Comments
 (0)