jekyll-minifier 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CLAUDE.md +10 -10
- data/COVERAGE_ANALYSIS.md +228 -0
- data/FINAL_TEST_REPORT.md +164 -0
- data/SECURITY.md +155 -0
- data/SECURITY_FIX_SUMMARY.md +141 -0
- data/VALIDATION_FEATURES.md +254 -0
- data/example_config.yml +127 -0
- data/jekyll-minifier.gemspec +1 -1
- data/lib/jekyll-minifier/version.rb +1 -1
- data/lib/jekyll-minifier.rb +1165 -134
- data/spec/caching_performance_spec.rb +238 -0
- data/spec/compressor_cache_spec.rb +326 -0
- data/spec/coverage_enhancement_spec.rb +391 -0
- data/spec/enhanced_css_spec.rb +277 -0
- data/spec/environment_validation_spec.rb +84 -0
- data/spec/fixtures/assets/data.json +25 -0
- data/spec/input_validation_spec.rb +514 -0
- data/spec/jekyll-minifier_enhanced_spec.rb +211 -0
- data/spec/performance_spec.rb +232 -0
- data/spec/security_redos_spec.rb +306 -0
- data/spec/security_validation_spec.rb +253 -0
- metadata +36 -28
- data/spec/fixtures/_site/404.html +0 -1
- data/spec/fixtures/_site/assets/css/style.css +0 -1
- data/spec/fixtures/_site/assets/js/script.js +0 -1
- data/spec/fixtures/_site/atom.xml +0 -1
- data/spec/fixtures/_site/index.html +0 -1
- data/spec/fixtures/_site/random/index.html +0 -1
- data/spec/fixtures/_site/random/random.html +0 -1
- data/spec/fixtures/_site/reviews/index.html +0 -1
- data/spec/fixtures/_site/reviews/test-review-1.html +0 -1
- data/spec/fixtures/_site/reviews/test-review-2.html +0 -1
@@ -0,0 +1,306 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Jekyll Minifier - ReDoS Security Protection" do
|
4
|
+
let(:overrides) { Hash.new }
|
5
|
+
let(:config) do
|
6
|
+
Jekyll.configuration(Jekyll::Utils.deep_merge_hashes({
|
7
|
+
"full_rebuild" => true,
|
8
|
+
"source" => source_dir,
|
9
|
+
"destination" => dest_dir,
|
10
|
+
"show_drafts" => true,
|
11
|
+
"url" => "http://example.org",
|
12
|
+
"name" => "My awesome site"
|
13
|
+
}, overrides))
|
14
|
+
end
|
15
|
+
let(:site) { Jekyll::Site.new(config) }
|
16
|
+
let(:compressor) { Jekyll::Document.new(source_dir("_posts/2014-03-01-test-review-1.md"), site: site, collection: site.collections["posts"]) }
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
allow(ENV).to receive(:[]).and_call_original
|
20
|
+
allow(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('production')
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "ReDoS Attack Prevention" do
|
24
|
+
context "with safe preserve patterns" do
|
25
|
+
let(:overrides) do
|
26
|
+
{
|
27
|
+
"jekyll-minifier" => {
|
28
|
+
"preserve_patterns" => [
|
29
|
+
"<!-- PRESERVE -->.*?<!-- /PRESERVE -->",
|
30
|
+
"<script[^>]*>.*?</script>",
|
31
|
+
"<style[^>]*>.*?</style>"
|
32
|
+
]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
it "processes safe patterns without issues" do
|
38
|
+
expect { site.process }.not_to raise_error
|
39
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "compiles safe patterns successfully" do
|
43
|
+
patterns = compressor.send(:compile_preserve_patterns, [
|
44
|
+
"<!-- PRESERVE -->.*?<!-- /PRESERVE -->",
|
45
|
+
"<script[^>]*>.*?</script>"
|
46
|
+
])
|
47
|
+
|
48
|
+
expect(patterns.length).to eq(2)
|
49
|
+
expect(patterns.all? { |p| p.is_a?(Regexp) }).to be true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with potentially dangerous ReDoS patterns" do
|
54
|
+
let(:dangerous_patterns) do
|
55
|
+
[
|
56
|
+
# Nested quantifiers - classic ReDoS vector
|
57
|
+
"(a+)+b",
|
58
|
+
"(a*)*b",
|
59
|
+
"(a+)*b",
|
60
|
+
"(a*)+b",
|
61
|
+
|
62
|
+
# Alternation with overlapping patterns
|
63
|
+
"(a|a)*b",
|
64
|
+
"(ab|ab)*c",
|
65
|
+
"(.*|.*)*d",
|
66
|
+
|
67
|
+
# Excessively long patterns
|
68
|
+
"a" * 1001,
|
69
|
+
|
70
|
+
# Complex nested structures
|
71
|
+
"(" * 15 + "a" + ")" * 15,
|
72
|
+
|
73
|
+
# Excessive quantifiers
|
74
|
+
"a+" * 25 + "b"
|
75
|
+
]
|
76
|
+
end
|
77
|
+
|
78
|
+
it "rejects dangerous ReDoS patterns gracefully" do
|
79
|
+
# Should not raise errors, but should warn and skip dangerous patterns
|
80
|
+
expect(Jekyll.logger).to receive(:warn).at_least(:once)
|
81
|
+
|
82
|
+
patterns = compressor.send(:compile_preserve_patterns, dangerous_patterns)
|
83
|
+
|
84
|
+
# All dangerous patterns should be filtered out
|
85
|
+
expect(patterns.length).to eq(0)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "continues processing when dangerous patterns are present" do
|
89
|
+
overrides = {
|
90
|
+
"jekyll-minifier" => {
|
91
|
+
"preserve_patterns" => dangerous_patterns
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
test_site = Jekyll::Site.new(Jekyll.configuration({
|
96
|
+
"full_rebuild" => true,
|
97
|
+
"source" => source_dir,
|
98
|
+
"destination" => dest_dir,
|
99
|
+
"show_drafts" => true,
|
100
|
+
"jekyll-minifier" => {
|
101
|
+
"preserve_patterns" => dangerous_patterns
|
102
|
+
}
|
103
|
+
}))
|
104
|
+
|
105
|
+
# Should complete processing despite dangerous patterns
|
106
|
+
expect { test_site.process }.not_to raise_error
|
107
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "with mixed safe and dangerous patterns" do
|
112
|
+
let(:mixed_patterns) do
|
113
|
+
[
|
114
|
+
"<!-- PRESERVE -->.*?<!-- /PRESERVE -->", # Safe
|
115
|
+
"(a+)+b", # Dangerous - nested quantifiers
|
116
|
+
"<script[^>]*>.*?</script>", # Safe
|
117
|
+
"(a|a)*b", # Dangerous - alternation overlap
|
118
|
+
"<style[^>]*>.*?</style>" # Safe
|
119
|
+
]
|
120
|
+
end
|
121
|
+
|
122
|
+
it "processes only the safe patterns" do
|
123
|
+
expect(Jekyll.logger).to receive(:warn).at_least(:twice) # For the two dangerous patterns
|
124
|
+
|
125
|
+
patterns = compressor.send(:compile_preserve_patterns, mixed_patterns)
|
126
|
+
|
127
|
+
# Should compile only the 3 safe patterns
|
128
|
+
expect(patterns.length).to eq(3)
|
129
|
+
expect(patterns.all? { |p| p.is_a?(Regexp) }).to be true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "with invalid regex patterns" do
|
134
|
+
let(:invalid_patterns) do
|
135
|
+
[
|
136
|
+
"[", # Unclosed bracket
|
137
|
+
"(", # Unclosed parenthesis
|
138
|
+
"*", # Invalid quantifier
|
139
|
+
"(?P<test>)", # Invalid named group syntax for Ruby
|
140
|
+
nil, # Nil value
|
141
|
+
123, # Non-string value
|
142
|
+
"", # Empty string
|
143
|
+
]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "handles invalid patterns gracefully" do
|
147
|
+
expect(Jekyll.logger).to receive(:warn).at_least(:once)
|
148
|
+
|
149
|
+
patterns = compressor.send(:compile_preserve_patterns, invalid_patterns)
|
150
|
+
|
151
|
+
# Should filter out all invalid patterns
|
152
|
+
expect(patterns.length).to eq(0)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "Pattern Validation Logic" do
|
158
|
+
it "validates pattern complexity correctly" do
|
159
|
+
# Safe patterns should pass
|
160
|
+
safe_patterns = [
|
161
|
+
"simple text",
|
162
|
+
"<!-- comment -->.*?<!-- /comment -->",
|
163
|
+
"<[^>]+>",
|
164
|
+
"a{1,5}b"
|
165
|
+
]
|
166
|
+
|
167
|
+
safe_patterns.each do |pattern|
|
168
|
+
expect(compressor.send(:valid_regex_pattern?, pattern)).to be(true), "Expected '#{pattern}' to be valid"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it "rejects dangerous patterns correctly" do
|
173
|
+
dangerous_patterns = [
|
174
|
+
"(a+)+", # Nested quantifiers
|
175
|
+
"(a|a)*", # Alternation overlap
|
176
|
+
"(" * 15, # Too much nesting
|
177
|
+
"a" * 1001, # Too long
|
178
|
+
"a+" * 25 # Too many quantifiers
|
179
|
+
]
|
180
|
+
|
181
|
+
dangerous_patterns.each do |pattern|
|
182
|
+
expect(compressor.send(:valid_regex_pattern?, pattern)).to be(false), "Expected '#{pattern}' to be invalid"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it "handles edge cases in validation" do
|
187
|
+
edge_cases = [
|
188
|
+
nil, # Nil
|
189
|
+
123, # Non-string
|
190
|
+
"", # Empty string
|
191
|
+
" ", # Whitespace only
|
192
|
+
]
|
193
|
+
|
194
|
+
edge_cases.each do |pattern|
|
195
|
+
expect(compressor.send(:valid_regex_pattern?, pattern)).to be(false), "Expected #{pattern.inspect} to be invalid"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "Timeout Protection" do
|
201
|
+
it "compiles simple patterns quickly" do
|
202
|
+
start_time = Time.now
|
203
|
+
regex = compressor.send(:compile_regex_with_timeout, "simple.*pattern", 1.0)
|
204
|
+
duration = Time.now - start_time
|
205
|
+
|
206
|
+
expect(regex).to be_a(Regexp)
|
207
|
+
expect(duration).to be < 0.1 # Should be very fast
|
208
|
+
end
|
209
|
+
|
210
|
+
it "handles timeout gracefully for complex patterns" do
|
211
|
+
# This test uses a pattern that should compile quickly
|
212
|
+
# but demonstrates the timeout mechanism is in place
|
213
|
+
start_time = Time.now
|
214
|
+
regex = compressor.send(:compile_regex_with_timeout, "test.*pattern", 0.001) # Very short timeout
|
215
|
+
duration = Time.now - start_time
|
216
|
+
|
217
|
+
# Either compiles successfully (very fast) or times out gracefully
|
218
|
+
expect(duration).to be < 0.1
|
219
|
+
# The regex should compile successfully or timeout gracefully
|
220
|
+
expect(regex.nil? || regex.is_a?(Regexp)).to be true
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "Backward Compatibility" do
|
225
|
+
context "with existing user configurations" do
|
226
|
+
let(:legacy_configs) do
|
227
|
+
[
|
228
|
+
{
|
229
|
+
"preserve_patterns" => ["<!-- PRESERVE -->.*?<!-- /PRESERVE -->"]
|
230
|
+
},
|
231
|
+
{
|
232
|
+
"preserve_patterns" => [
|
233
|
+
"<script[^>]*>.*?</script>",
|
234
|
+
"<style[^>]*>.*?</style>"
|
235
|
+
]
|
236
|
+
},
|
237
|
+
{
|
238
|
+
"preserve_php" => true,
|
239
|
+
"preserve_patterns" => ["<!-- CUSTOM -->.*?<!-- /CUSTOM -->"]
|
240
|
+
}
|
241
|
+
]
|
242
|
+
end
|
243
|
+
|
244
|
+
it "maintains full backward compatibility" do
|
245
|
+
legacy_configs.each do |config|
|
246
|
+
test_site = Jekyll::Site.new(Jekyll.configuration({
|
247
|
+
"full_rebuild" => true,
|
248
|
+
"source" => source_dir,
|
249
|
+
"destination" => dest_dir,
|
250
|
+
"jekyll-minifier" => config
|
251
|
+
}))
|
252
|
+
|
253
|
+
# All legacy configurations should continue working
|
254
|
+
expect { test_site.process }.not_to raise_error
|
255
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
context "with no preserve_patterns configuration" do
|
261
|
+
it "works without preserve_patterns" do
|
262
|
+
expect { site.process }.not_to raise_error
|
263
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context "with empty preserve_patterns" do
|
268
|
+
let(:overrides) do
|
269
|
+
{
|
270
|
+
"jekyll-minifier" => {
|
271
|
+
"preserve_patterns" => []
|
272
|
+
}
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
it "handles empty preserve_patterns array" do
|
277
|
+
expect { site.process }.not_to raise_error
|
278
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe "Security Boundary Testing" do
|
284
|
+
it "prevents ReDoS through compilation timeout" do
|
285
|
+
# This simulates a potential ReDoS attack pattern
|
286
|
+
# The protection should prevent hanging
|
287
|
+
start_time = Time.now
|
288
|
+
|
289
|
+
result = compressor.send(:compile_preserve_patterns, ["(a+)+"])
|
290
|
+
|
291
|
+
duration = Time.now - start_time
|
292
|
+
expect(duration).to be < 2.0 # Should not hang
|
293
|
+
expect(result).to eq([]) # Dangerous pattern should be rejected
|
294
|
+
end
|
295
|
+
|
296
|
+
it "maintains site generation speed with protection enabled" do
|
297
|
+
# Full site processing should remain fast
|
298
|
+
start_time = Time.now
|
299
|
+
site.process
|
300
|
+
duration = Time.now - start_time
|
301
|
+
|
302
|
+
expect(duration).to be < 10.0 # Should complete within reasonable time
|
303
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Jekyll Minifier - End-to-End Security Validation" do
|
4
|
+
let(:config) do
|
5
|
+
Jekyll.configuration({
|
6
|
+
"full_rebuild" => true,
|
7
|
+
"source" => source_dir,
|
8
|
+
"destination" => dest_dir,
|
9
|
+
"show_drafts" => true,
|
10
|
+
"url" => "http://example.org",
|
11
|
+
"name" => "Security Test Site"
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
allow(ENV).to receive(:[]).and_call_original
|
17
|
+
allow(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('production')
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "Complete ReDoS Protection Validation" do
|
21
|
+
context "with real-world attack patterns" do
|
22
|
+
let(:redos_attack_patterns) do
|
23
|
+
[
|
24
|
+
# Catastrophic backtracking patterns
|
25
|
+
"(a+)+$",
|
26
|
+
"(a|a)*$",
|
27
|
+
"(a*)*$",
|
28
|
+
"(a+)*$",
|
29
|
+
"^(a+)+",
|
30
|
+
"^(a|a)*",
|
31
|
+
|
32
|
+
# Evil regex patterns from real attacks
|
33
|
+
"^(([a-z])+.)+[A-Z]([a-z])+$",
|
34
|
+
"([a-zA-Z]+)*$",
|
35
|
+
"(([a-z]*)*)*$",
|
36
|
+
|
37
|
+
# Nested alternation
|
38
|
+
"((a|a)*)*",
|
39
|
+
"((.*)*)*",
|
40
|
+
"((.+)*)+",
|
41
|
+
|
42
|
+
# Long pattern attacks
|
43
|
+
"a" * 2000,
|
44
|
+
|
45
|
+
# Complex nested structures
|
46
|
+
"(" * 20 + "a" + ")" * 20,
|
47
|
+
|
48
|
+
# Excessive quantifiers
|
49
|
+
("a+" * 30) + "b"
|
50
|
+
]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "blocks all ReDoS attack vectors while maintaining site functionality" do
|
54
|
+
# Create site with dangerous patterns
|
55
|
+
malicious_config = config.merge({
|
56
|
+
"jekyll-minifier" => {
|
57
|
+
"preserve_patterns" => redos_attack_patterns,
|
58
|
+
"compress_html" => true,
|
59
|
+
"compress_css" => true,
|
60
|
+
"compress_javascript" => true
|
61
|
+
}
|
62
|
+
})
|
63
|
+
|
64
|
+
malicious_site = Jekyll::Site.new(malicious_config)
|
65
|
+
|
66
|
+
# Site should process successfully despite malicious patterns
|
67
|
+
start_time = Time.now
|
68
|
+
expect { malicious_site.process }.not_to raise_error
|
69
|
+
duration = Time.now - start_time
|
70
|
+
|
71
|
+
# Should complete quickly (not hang due to ReDoS)
|
72
|
+
expect(duration).to be < 10.0
|
73
|
+
|
74
|
+
# Site should be built successfully
|
75
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
76
|
+
expect(File.exist?(dest_dir("assets/css/style.css"))).to be true
|
77
|
+
expect(File.exist?(dest_dir("assets/js/script.js"))).to be true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "production site build with mixed patterns" do
|
82
|
+
let(:mixed_config) do
|
83
|
+
{
|
84
|
+
"jekyll-minifier" => {
|
85
|
+
"preserve_patterns" => [
|
86
|
+
# Safe patterns (should work)
|
87
|
+
"<!-- PRESERVE -->.*?<!-- /PRESERVE -->",
|
88
|
+
"<script type=\"text/template\">.*?</script>",
|
89
|
+
|
90
|
+
# Dangerous patterns (should be filtered)
|
91
|
+
"(a+)+attack",
|
92
|
+
"(malicious|malicious)*",
|
93
|
+
|
94
|
+
# More safe patterns
|
95
|
+
"<%.*?%>",
|
96
|
+
"{{.*?}}"
|
97
|
+
],
|
98
|
+
"compress_html" => true,
|
99
|
+
"compress_css" => true,
|
100
|
+
"compress_javascript" => true,
|
101
|
+
"remove_comments" => true
|
102
|
+
}
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
it "successfully builds production site with security protection active" do
|
107
|
+
test_site = Jekyll::Site.new(config.merge(mixed_config))
|
108
|
+
|
109
|
+
# Capture any warnings
|
110
|
+
warnings = []
|
111
|
+
original_warn = Jekyll.logger.method(:warn)
|
112
|
+
allow(Jekyll.logger).to receive(:warn) do |*args|
|
113
|
+
warnings << args.join(" ")
|
114
|
+
original_warn.call(*args)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Build should succeed
|
118
|
+
expect { test_site.process }.not_to raise_error
|
119
|
+
|
120
|
+
# Verify all expected files are created and minified
|
121
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
122
|
+
expect(File.exist?(dest_dir("assets/css/style.css"))).to be true
|
123
|
+
expect(File.exist?(dest_dir("assets/js/script.js"))).to be true
|
124
|
+
|
125
|
+
# Verify minification occurred (files should be compressed)
|
126
|
+
html_content = File.read(dest_dir("index.html"))
|
127
|
+
css_content = File.read(dest_dir("assets/css/style.css"))
|
128
|
+
js_content = File.read(dest_dir("assets/js/script.js"))
|
129
|
+
|
130
|
+
expect(html_content.lines.count).to be <= 2 # HTML should be minified
|
131
|
+
expect(css_content).not_to include("\n") # CSS should be on one line
|
132
|
+
expect(js_content).not_to include("// ") # JS comments should be removed
|
133
|
+
|
134
|
+
# Security warnings should be present for dangerous patterns
|
135
|
+
security_warnings = warnings.select { |w| w.include?("Jekyll Minifier:") }
|
136
|
+
expect(security_warnings.length).to be >= 2 # At least 2 dangerous patterns warned
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "Performance Security Validation" do
|
142
|
+
it "maintains fast build times even with many patterns" do
|
143
|
+
# Test with 50 safe patterns + 10 dangerous patterns
|
144
|
+
large_pattern_set = []
|
145
|
+
|
146
|
+
# Add safe patterns
|
147
|
+
50.times { |i| large_pattern_set << "<!-- SECTION#{i} -->.*?<!-- /SECTION#{i} -->" }
|
148
|
+
|
149
|
+
# Add dangerous patterns that should be filtered
|
150
|
+
10.times { |i| large_pattern_set << "(attack#{i}+)+" }
|
151
|
+
|
152
|
+
config_with_many_patterns = config.merge({
|
153
|
+
"jekyll-minifier" => {
|
154
|
+
"preserve_patterns" => large_pattern_set,
|
155
|
+
"compress_html" => true
|
156
|
+
}
|
157
|
+
})
|
158
|
+
|
159
|
+
test_site = Jekyll::Site.new(config_with_many_patterns)
|
160
|
+
|
161
|
+
start_time = Time.now
|
162
|
+
expect { test_site.process }.not_to raise_error
|
163
|
+
duration = Time.now - start_time
|
164
|
+
|
165
|
+
# Should still complete in reasonable time
|
166
|
+
expect(duration).to be < 15.0
|
167
|
+
|
168
|
+
# Site should be built
|
169
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "Memory Safety Validation" do
|
174
|
+
it "prevents memory exhaustion from malicious patterns" do
|
175
|
+
# Pattern designed to consume excessive memory during compilation
|
176
|
+
memory_attack_patterns = [
|
177
|
+
# Highly nested patterns
|
178
|
+
"(" * 100 + "a" + ")" * 100,
|
179
|
+
|
180
|
+
# Very long alternation
|
181
|
+
(["attack"] * 1000).join("|"),
|
182
|
+
|
183
|
+
# Complex quantifier combinations
|
184
|
+
("a{1,1000}" * 100)
|
185
|
+
]
|
186
|
+
|
187
|
+
config_memory_test = config.merge({
|
188
|
+
"jekyll-minifier" => {
|
189
|
+
"preserve_patterns" => memory_attack_patterns
|
190
|
+
}
|
191
|
+
})
|
192
|
+
|
193
|
+
test_site = Jekyll::Site.new(config_memory_test)
|
194
|
+
|
195
|
+
# Should not crash or consume excessive memory
|
196
|
+
expect { test_site.process }.not_to raise_error
|
197
|
+
|
198
|
+
# Site should still build
|
199
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "Input Validation Edge Cases" do
|
204
|
+
it "handles malformed pattern arrays gracefully" do
|
205
|
+
malformed_configs = [
|
206
|
+
{ "preserve_patterns" => [nil, "", 123, [], {}] },
|
207
|
+
{ "preserve_patterns" => "not_an_array" },
|
208
|
+
{ "preserve_patterns" => 42 },
|
209
|
+
{ "preserve_patterns" => nil }
|
210
|
+
]
|
211
|
+
|
212
|
+
malformed_configs.each do |malformed_config|
|
213
|
+
test_config = config.merge({
|
214
|
+
"jekyll-minifier" => malformed_config
|
215
|
+
})
|
216
|
+
|
217
|
+
test_site = Jekyll::Site.new(test_config)
|
218
|
+
|
219
|
+
# Should handle gracefully without crashing
|
220
|
+
expect { test_site.process }.not_to raise_error
|
221
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "Legacy Configuration Security" do
|
227
|
+
it "secures legacy preserve_patterns configurations" do
|
228
|
+
# Simulate legacy config that might contain dangerous patterns
|
229
|
+
legacy_config = config.merge({
|
230
|
+
"jekyll-minifier" => {
|
231
|
+
# Old-style configuration with potentially dangerous patterns
|
232
|
+
"preserve_patterns" => [
|
233
|
+
"<!-- preserve -->.*?<!-- /preserve -->", # Safe legacy pattern
|
234
|
+
"(legacy+)+", # Dangerous legacy pattern
|
235
|
+
"<comment>.*?</comment>", # Safe legacy pattern
|
236
|
+
],
|
237
|
+
"preserve_php" => true, # Legacy PHP preservation
|
238
|
+
"compress_html" => true
|
239
|
+
}
|
240
|
+
})
|
241
|
+
|
242
|
+
legacy_site = Jekyll::Site.new(legacy_config)
|
243
|
+
|
244
|
+
# Should work with legacy config but filter dangerous patterns
|
245
|
+
expect { legacy_site.process }.not_to raise_error
|
246
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
247
|
+
|
248
|
+
# PHP pattern should still be added (safe built-in pattern)
|
249
|
+
html_content = File.read(dest_dir("index.html"))
|
250
|
+
expect(html_content.length).to be > 0
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-minifier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- DigitalSparky
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jekyll
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 2.0
|
61
|
+
version: 2.1.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 2.0
|
68
|
+
version: 2.1.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: json-minify
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -163,13 +163,19 @@ files:
|
|
163
163
|
- ".gitignore"
|
164
164
|
- ".travis.yml"
|
165
165
|
- CLAUDE.md
|
166
|
+
- COVERAGE_ANALYSIS.md
|
166
167
|
- Dockerfile
|
168
|
+
- FINAL_TEST_REPORT.md
|
167
169
|
- Gemfile
|
168
170
|
- LICENSE
|
169
171
|
- README.md
|
170
172
|
- Rakefile
|
173
|
+
- SECURITY.md
|
174
|
+
- SECURITY_FIX_SUMMARY.md
|
175
|
+
- VALIDATION_FEATURES.md
|
171
176
|
- cody-mcp.db
|
172
177
|
- docker-compose.yml
|
178
|
+
- example_config.yml
|
173
179
|
- issue48-basic/_config.yml
|
174
180
|
- issue48-basic/_layouts/default.html
|
175
181
|
- issue48-basic/assets/css/style.css
|
@@ -178,6 +184,11 @@ files:
|
|
178
184
|
- jekyll-minifier.gemspec
|
179
185
|
- lib/jekyll-minifier.rb
|
180
186
|
- lib/jekyll-minifier/version.rb
|
187
|
+
- spec/caching_performance_spec.rb
|
188
|
+
- spec/compressor_cache_spec.rb
|
189
|
+
- spec/coverage_enhancement_spec.rb
|
190
|
+
- spec/enhanced_css_spec.rb
|
191
|
+
- spec/environment_validation_spec.rb
|
181
192
|
- spec/fixtures/404.html
|
182
193
|
- spec/fixtures/_config.yml
|
183
194
|
- spec/fixtures/_includes/sidebar.html
|
@@ -189,27 +200,23 @@ files:
|
|
189
200
|
- spec/fixtures/_posts/2012-04-03-test-review-1.markdown
|
190
201
|
- spec/fixtures/_posts/2013-04-03-test-review-2.markdown
|
191
202
|
- spec/fixtures/_posts/2015-01-01-random.markdown
|
192
|
-
- spec/fixtures/_site/404.html
|
193
|
-
- spec/fixtures/_site/assets/css/style.css
|
194
|
-
- spec/fixtures/_site/assets/js/script.js
|
195
|
-
- spec/fixtures/_site/atom.xml
|
196
|
-
- spec/fixtures/_site/index.html
|
197
|
-
- spec/fixtures/_site/random/index.html
|
198
|
-
- spec/fixtures/_site/random/random.html
|
199
|
-
- spec/fixtures/_site/reviews/index.html
|
200
|
-
- spec/fixtures/_site/reviews/test-review-1.html
|
201
|
-
- spec/fixtures/_site/reviews/test-review-2.html
|
202
203
|
- spec/fixtures/assets/css/style.css
|
204
|
+
- spec/fixtures/assets/data.json
|
203
205
|
- spec/fixtures/assets/js/script.js
|
204
206
|
- spec/fixtures/atom.xml
|
205
207
|
- spec/fixtures/index.html
|
208
|
+
- spec/input_validation_spec.rb
|
209
|
+
- spec/jekyll-minifier_enhanced_spec.rb
|
206
210
|
- spec/jekyll-minifier_spec.rb
|
211
|
+
- spec/performance_spec.rb
|
212
|
+
- spec/security_redos_spec.rb
|
213
|
+
- spec/security_validation_spec.rb
|
207
214
|
- spec/spec_helper.rb
|
208
215
|
homepage: http://github.com/digitalsparky/jekyll-minifier
|
209
216
|
licenses:
|
210
217
|
- GPL-3.0-or-later
|
211
218
|
metadata: {}
|
212
|
-
post_install_message:
|
219
|
+
post_install_message:
|
213
220
|
rdoc_options: []
|
214
221
|
require_paths:
|
215
222
|
- lib
|
@@ -224,11 +231,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
231
|
- !ruby/object:Gem::Version
|
225
232
|
version: '0'
|
226
233
|
requirements: []
|
227
|
-
rubygems_version: 3.
|
228
|
-
signing_key:
|
234
|
+
rubygems_version: 3.0.3.1
|
235
|
+
signing_key:
|
229
236
|
specification_version: 2
|
230
237
|
summary: Jekyll Minifier for html, css, and javascript
|
231
238
|
test_files:
|
239
|
+
- spec/caching_performance_spec.rb
|
240
|
+
- spec/compressor_cache_spec.rb
|
241
|
+
- spec/coverage_enhancement_spec.rb
|
242
|
+
- spec/enhanced_css_spec.rb
|
243
|
+
- spec/environment_validation_spec.rb
|
232
244
|
- spec/fixtures/404.html
|
233
245
|
- spec/fixtures/_config.yml
|
234
246
|
- spec/fixtures/_includes/sidebar.html
|
@@ -240,19 +252,15 @@ test_files:
|
|
240
252
|
- spec/fixtures/_posts/2012-04-03-test-review-1.markdown
|
241
253
|
- spec/fixtures/_posts/2013-04-03-test-review-2.markdown
|
242
254
|
- spec/fixtures/_posts/2015-01-01-random.markdown
|
243
|
-
- spec/fixtures/_site/404.html
|
244
|
-
- spec/fixtures/_site/assets/css/style.css
|
245
|
-
- spec/fixtures/_site/assets/js/script.js
|
246
|
-
- spec/fixtures/_site/atom.xml
|
247
|
-
- spec/fixtures/_site/index.html
|
248
|
-
- spec/fixtures/_site/random/index.html
|
249
|
-
- spec/fixtures/_site/random/random.html
|
250
|
-
- spec/fixtures/_site/reviews/index.html
|
251
|
-
- spec/fixtures/_site/reviews/test-review-1.html
|
252
|
-
- spec/fixtures/_site/reviews/test-review-2.html
|
253
255
|
- spec/fixtures/assets/css/style.css
|
256
|
+
- spec/fixtures/assets/data.json
|
254
257
|
- spec/fixtures/assets/js/script.js
|
255
258
|
- spec/fixtures/atom.xml
|
256
259
|
- spec/fixtures/index.html
|
260
|
+
- spec/input_validation_spec.rb
|
261
|
+
- spec/jekyll-minifier_enhanced_spec.rb
|
257
262
|
- spec/jekyll-minifier_spec.rb
|
263
|
+
- spec/performance_spec.rb
|
264
|
+
- spec/security_redos_spec.rb
|
265
|
+
- spec/security_validation_spec.rb
|
258
266
|
- spec/spec_helper.rb
|