-
-
Notifications
You must be signed in to change notification settings - Fork 10k
/
entry_filter.rb
132 lines (114 loc) · 3.46 KB
/
entry_filter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true
module Jekyll
class EntryFilter
attr_reader :site
SPECIAL_LEADING_CHARACTERS = [
".", "_", "#", "~",
].freeze
def initialize(site, base_directory = nil)
@site = site
@base_directory = derive_base_directory(
@site, base_directory.to_s.dup
)
end
def base_directory
@base_directory.to_s
end
def derive_base_directory(site, base_dir)
base_dir[site.source] = "" if base_dir.start_with?(site.source)
base_dir
end
def relative_to_source(entry)
File.join(
base_directory, entry
)
end
def filter(entries)
entries.reject do |e|
<<<<<<< HEAD
unless included?(e)
special?(e) || backup?(e) || excluded?(e) || symlink?(e)
end
=======
# Reject this entry if it is a symlink.
next true if symlink?(e)
# Do not reject this entry if it is included.
next false if included?(e)
# Reject this entry if it is special, a backup file, or excluded.
special?(e) || backup?(e) || excluded?(e)
>>>>>>> f1c87a91... Security: fix `include` bypass of `EntryFilter#filter` symlink check (#7226)
end
end
def included?(entry)
glob_include?(site.include, entry) ||
glob_include?(site.include, File.basename(entry))
end
def special?(entry)
SPECIAL_LEADING_CHARACTERS.include?(entry[0..0]) ||
SPECIAL_LEADING_CHARACTERS.include?(File.basename(entry)[0..0])
end
def backup?(entry)
entry[-1..-1] == "~"
end
def excluded?(entry)
glob_include?(site.exclude, relative_to_source(entry)).tap do |excluded|
if excluded
Jekyll.logger.debug(
"EntryFilter:",
"excluded #{relative_to_source(entry)}"
)
end
end
end
# --
# Check if a file is a symlink.
# NOTE: This can be converted to allowing even in safe,
# since we use Pathutil#in_path? now.
# --
def symlink?(entry)
site.safe && File.symlink?(entry) && symlink_outside_site_source?(entry)
end
# --
# NOTE: Pathutil#in_path? gets the realpath.
# @param [<Anything>] entry the entry you want to validate.
# Check if a path is outside of our given root.
# --
def symlink_outside_site_source?(entry)
!Pathutil.new(entry).in_path?(
site.in_source_dir
)
end
# --
# Check if an entry matches a specific pattern and return true,false.
# Returns true if path matches against any glob pattern.
# --
def glob_include?(enum, e)
entry = Pathutil.new(site.in_source_dir).join(e)
enum.any? do |exp|
# Users who send a Regexp knows what they want to
# exclude, so let them send a Regexp to exclude files,
# we will not bother caring if it works or not, it's
# on them at this point.
if exp.is_a?(Regexp)
entry =~ exp
else
item = Pathutil.new(site.in_source_dir).join(exp)
# If it's a directory they want to exclude, AKA
# ends with a "/" then we will go on to check and
# see if the entry falls within that path and
# exclude it if that's the case.
if e.end_with?("/")
entry.in_path?(
item
)
else
File.fnmatch?(item, entry) ||
entry.to_path.start_with?(
item
)
end
end
end
end
end
end