From f60d3a8dbc8008ce8afa3517f62a5effa1edc5b1 Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Fri, 25 Mar 2016 14:15:39 +0100 Subject: [PATCH 1/4] Allow symlinks iff they point to stuff inside site.source --- lib/jekyll/entry_filter.rb | 6 +++++- test/test_entry_filter.rb | 10 +++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/jekyll/entry_filter.rb b/lib/jekyll/entry_filter.rb index 48509f9de..cfbbc1f15 100644 --- a/lib/jekyll/entry_filter.rb +++ b/lib/jekyll/entry_filter.rb @@ -52,7 +52,11 @@ module Jekyll end def symlink?(entry) - File.symlink?(entry) && site.safe + site.safe && File.symlink?(entry) && bad_symlink?(entry) + end + + def bad_symlink?(entry) + ! File.realpath(entry).start_with?(File.realpath(@site.source)) end def ensure_leading_slash(path) diff --git a/test/test_entry_filter.rb b/test/test_entry_filter.rb index 546f02527..8887d43b7 100644 --- a/test/test_entry_filter.rb +++ b/test/test_entry_filter.rb @@ -46,11 +46,11 @@ class TestEntryFilter < JekyllUnitTest assert_equal files, @site.reader.filter_entries(files) end - should "filter symlink entries when safe mode enabled" do + should "keep safe symlink entries when safe mode enabled" do site = Site.new(site_configuration('safe' => true)) allow(File).to receive(:symlink?).with('symlink.js').and_return(true) files = %w[symlink.js] - assert_equal [], site.reader.filter_entries(files) + assert_equal files, @site.reader.filter_entries(files) end should "not filter symlink entries when safe mode disabled" do @@ -59,12 +59,12 @@ class TestEntryFilter < JekyllUnitTest assert_equal files, @site.reader.filter_entries(files) end - should "not include symlinks in safe mode" do + should "include only safe symlinks in safe mode" do site = Site.new(site_configuration('safe' => true)) site.reader.read_directories("symlink-test") - assert_equal [], site.pages - assert_equal [], site.static_files + assert_equal %w[main.scss symlinked-file].length, site.pages.length + refute_equal [], site.static_files end should "include symlinks in unsafe mode" do From bbd2a2b7f15e64f0ad3c7ff792d92d3f28523bda Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Sat, 26 Mar 2016 13:12:08 +0100 Subject: [PATCH 2/4] Rename EntryFilter#bad_symlink? to EntryFilter#symlink_outside_site_source? --- lib/jekyll/entry_filter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jekyll/entry_filter.rb b/lib/jekyll/entry_filter.rb index cfbbc1f15..cccd05f91 100644 --- a/lib/jekyll/entry_filter.rb +++ b/lib/jekyll/entry_filter.rb @@ -52,10 +52,10 @@ module Jekyll end def symlink?(entry) - site.safe && File.symlink?(entry) && bad_symlink?(entry) + site.safe && File.symlink?(entry) && symlink_outside_site_source?(entry) end - def bad_symlink?(entry) + def symlink_outside_site_source?(entry) ! File.realpath(entry).start_with?(File.realpath(@site.source)) end From 4aadfe9fd71635b993ce06a3efa4e1809528072c Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Sat, 26 Mar 2016 14:16:43 +0100 Subject: [PATCH 3/4] Add test that filters out symlink pointing outside site source --- test/test_entry_filter.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_entry_filter.rb b/test/test_entry_filter.rb index 8887d43b7..d20c9be2d 100644 --- a/test/test_entry_filter.rb +++ b/test/test_entry_filter.rb @@ -59,6 +59,12 @@ class TestEntryFilter < JekyllUnitTest assert_equal files, @site.reader.filter_entries(files) end + should "filter symlink pointing outside site source" do + ent1 = %w[_includes/tmp] + entries = EntryFilter.new(@site).filter(ent1) + assert_equal %w[], entries + end + should "include only safe symlinks in safe mode" do site = Site.new(site_configuration('safe' => true)) From 9347e90a677353ef9687c2284c5154dbacf84b58 Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Sat, 26 Mar 2016 16:40:19 +0100 Subject: [PATCH 4/4] Use EntryFilter throughout & update tests --- lib/jekyll/collection.rb | 4 ++-- lib/jekyll/readers/data_reader.rb | 5 +++-- test/test_collections.rb | 8 ++++---- test/test_site.rb | 8 ++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb index 11a4f06fe..ff605b870 100644 --- a/lib/jekyll/collection.rb +++ b/lib/jekyll/collection.rb @@ -94,7 +94,7 @@ module Jekyll Dir.chdir(directory) do entry_filter.filter(entries).reject do |f| path = collection_dir(f) - File.directory?(path) || (File.symlink?(f) && site.safe) + File.directory?(path) || entry_filter.symlink?(f) end end end @@ -135,7 +135,7 @@ module Jekyll # Returns false if the directory doesn't exist or if it's a symlink # and we're in safe mode. def exists? - File.directory?(directory) && !(File.symlink?(directory) && site.safe) + File.directory?(directory) && !entry_filter.symlink?(directory) end # The entry filter for this collection. diff --git a/lib/jekyll/readers/data_reader.rb b/lib/jekyll/readers/data_reader.rb index 870fc2dc4..796c0d297 100644 --- a/lib/jekyll/readers/data_reader.rb +++ b/lib/jekyll/readers/data_reader.rb @@ -4,6 +4,7 @@ module Jekyll def initialize(site) @site = site @content = {} + @entry_filter = EntryFilter.new(site) end # Read all the files in //_drafts and create a new Draft @@ -26,7 +27,7 @@ module Jekyll # # Returns nothing def read_data_to(dir, data) - return unless File.directory?(dir) && (!site.safe || !File.symlink?(dir)) + return unless File.directory?(dir) && !@entry_filter.symlink?(dir) entries = Dir.chdir(dir) do Dir['*.{yaml,yml,json,csv}'] + Dir['*'].select { |fn| File.directory?(fn) } @@ -34,7 +35,7 @@ module Jekyll entries.each do |entry| path = @site.in_source_dir(dir, entry) - next if File.symlink?(path) && site.safe + next if @entry_filter.symlink?(path) key = sanitize_filename(File.basename(entry, '.*')) if File.directory?(path) diff --git a/test/test_collections.rb b/test/test_collections.rb index 1faef26e2..82175bc54 100644 --- a/test/test_collections.rb +++ b/test/test_collections.rb @@ -179,13 +179,13 @@ class TestCollections < JekyllUnitTest @collection = @site.collections["methods"] end - should "not allow symlinks" do - refute_includes @collection.filtered_entries, "um_hi.md" + should "include the symlinked file as it resolves to inside site.source" do + assert_includes @collection.filtered_entries, "um_hi.md" refute_includes @collection.filtered_entries, "/um_hi.md" end - should "not include the symlinked file in the list of docs" do - refute_includes @collection.docs.map(&:relative_path), "_methods/um_hi.md" + should "include the symlinked file in the list of docs as it resolves to inside site.source" do + assert_includes @collection.docs.map(&:relative_path), "_methods/um_hi.md" end end diff --git a/test/test_site.rb b/test/test_site.rb index 3bb93c35b..42cedc7ce 100644 --- a/test/test_site.rb +++ b/test/test_site.rb @@ -418,12 +418,12 @@ class TestSite < JekyllUnitTest assert_equal site.site_payload['site']['data']['products'], file_content end - should "not load symlink files in safe mode" do + should "load the symlink files in safe mode, as they resolve to inside site.source" do site = Site.new(site_configuration('safe' => true)) site.process - - assert_nil site.data['products'] - assert_nil site.site_payload['site']['data']['products'] + file_content = SafeYAML.load_file(File.join(source_dir, '_data', 'products.yml')) + assert_equal site.data['products'], file_content + assert_equal site.site_payload['site']['data']['products'], file_content end end