diff --git a/lib/jekyll.rb b/lib/jekyll.rb index b978ec7a..8e007ac3 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -175,22 +175,7 @@ module Jekyll return base_directory if base_directory.eql?(questionable_path) return base_directory if questionable_path.nil? - clean_path = questionable_path.dup - clean_path.insert(0, "/") if clean_path.start_with?("~") - clean_path = File.expand_path(clean_path, "/") - - return clean_path if clean_path.eql?(base_directory) - - # remove any remaining extra leading slashes not stripped away by calling - # `File.expand_path` above. - clean_path.squeeze!("/") - - if clean_path.start_with?(base_directory.sub(%r!\z!, "/")) - clean_path - else - clean_path.sub!(%r!\A\w:/!, "/") - File.join(base_directory, clean_path) - end + +Jekyll::PathManager.sanitized_path(base_directory, questionable_path) end # Conditional optimizations diff --git a/lib/jekyll/path_manager.rb b/lib/jekyll/path_manager.rb index eeed1f95..f00e11a8 100644 --- a/lib/jekyll/path_manager.rb +++ b/lib/jekyll/path_manager.rb @@ -27,5 +27,36 @@ module Jekyll @join[base] ||= {} @join[base][item] ||= File.join(base, item).freeze end + + # Ensures the questionable path is prefixed with the base directory + # and prepends the questionable path with the base directory if false. + # + # Returns a frozen string. + def self.sanitized_path(base_directory, questionable_path) + @sanitized_path ||= {} + @sanitized_path[base_directory] ||= {} + @sanitized_path[base_directory][questionable_path] ||= begin + return base_directory.freeze if questionable_path.nil? + + clean_path = if questionable_path.start_with?("~") + questionable_path.dup.insert(0, "/") + else + questionable_path + end + clean_path = File.expand_path(clean_path, "/") + return clean_path.freeze if clean_path.eql?(base_directory) + + # remove any remaining extra leading slashes not stripped away by calling + # `File.expand_path` above. + clean_path.squeeze!("/") + + if clean_path.start_with?(base_directory.sub(%r!\z!, "/")) + clean_path.freeze + else + clean_path.sub!(%r!\A\w:/!, "/") + join(base_directory, clean_path) + end + end + end end end diff --git a/test/test_path_manager.rb b/test/test_path_manager.rb new file mode 100644 index 00000000..e17175c3 --- /dev/null +++ b/test/test_path_manager.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "helper" + +class TestPathManager < JekyllUnitTest + context "PathManager" do + setup do + @source = Dir.pwd + end + + should "return frozen copy of base if questionable path is nil" do + assert_equal @source, Jekyll::PathManager.sanitized_path(@source, nil) + assert Jekyll::PathManager.sanitized_path(@source, nil).frozen? + end + + should "return a frozen copy of base if questionable path expands into the base" do + assert_equal @source, Jekyll::PathManager.sanitized_path(@source, File.join(@source, "/")) + assert Jekyll::PathManager.sanitized_path(@source, File.join(@source, "/")).frozen? + end + + should "return a frozen string result" do + if Jekyll::Utils::Platforms.really_windows? + assert_equal( + "#{@source}/_config.yml", + Jekyll::PathManager.sanitized_path(@source, "E:\\_config.yml") + ) + end + assert_equal( + "#{@source}/_config.yml", + Jekyll::PathManager.sanitized_path(@source, "//_config.yml") + ) + assert Jekyll::PathManager.sanitized_path(@source, "//_config.yml").frozen? + end + end +end