diff --git a/Gemfile b/Gemfile index 492212d8..ce0f1246 100644 --- a/Gemfile +++ b/Gemfile @@ -27,6 +27,7 @@ group :test do gem "rubocop", "~> 0.61.0" gem "test-dependency-theme", :path => File.expand_path("test/fixtures/test-dependency-theme", __dir__) gem "test-theme", :path => File.expand_path("test/fixtures/test-theme", __dir__) + gem "test-theme-symlink", :path => File.expand_path("test/fixtures/test-theme-symlink", __dir__) gem "jruby-openssl" if RUBY_ENGINE == "jruby" end diff --git a/lib/jekyll/theme.rb b/lib/jekyll/theme.rb index 134a86d8..3eb44157 100644 --- a/lib/jekyll/theme.rb +++ b/lib/jekyll/theme.rb @@ -57,7 +57,11 @@ module Jekyll end def realpath_for(folder) - File.realpath(Jekyll.sanitized_path(root, folder.to_s)) + # This resolves all symlinks for the theme subfolder and then ensures that the directory + # remains inside the theme root. This prevents the use of symlinks for theme subfolders to + # escape the theme root. + # However, symlinks are allowed to point to other directories within the theme. + Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s))) rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP Jekyll.logger.warn "Invalid theme folder:", folder nil diff --git a/test/fixtures/test-theme-symlink/test-theme-symlink.gemspec b/test/fixtures/test-theme-symlink/test-theme-symlink.gemspec new file mode 100644 index 00000000..b0a915e3 --- /dev/null +++ b/test/fixtures/test-theme-symlink/test-theme-symlink.gemspec @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +Gem::Specification.new do |s| + s.name = "test-theme-symlink" + s.version = "0.1.0" + s.licenses = ["MIT"] + s.summary = "This is a theme with a symlink used to test Jekyll" + s.authors = ["Jekyll"] + s.files = ["lib/example.rb"] + s.homepage = "https://github.com/jekyll/jekyll" +end diff --git a/test/test_theme_assets_reader.rb b/test/test_theme_assets_reader.rb index 358789f5..a78bc7f7 100644 --- a/test/test_theme_assets_reader.rb +++ b/test/test_theme_assets_reader.rb @@ -75,4 +75,27 @@ class TestThemeAssetsReader < JekyllUnitTest refute_file_with_relative_path site.pages, "assets/style.scss" end end + + context "symlinked theme" do + should "not read assets from symlinked theme" do + begin + tmp_dir = Dir.mktmpdir("jekyll-theme-test") + File.open(File.join(tmp_dir, "test.txt"), "wb") { |f| f.write "content" } + + theme_dir = File.join(__dir__, "fixtures", "test-theme-symlink") + File.symlink(tmp_dir, File.join(theme_dir, "assets")) + + site = fixture_site( + "theme" => "test-theme-symlink", + "theme-color" => "black" + ) + ThemeAssetsReader.new(site).read + + assert_empty site.static_files, "static file should not have been picked up" + ensure + FileUtils.rm_rf(tmp_dir) + FileUtils.rm_rf(File.join(theme_dir, "assets")) + end + end + end end