Skip to content

Commit 3af982b

Browse files
rahearnrei-moo
authored andcommitted
Refactor rake task methods into module for better testing
Fix rake task file count output message
1 parent fa4298e commit 3af982b

File tree

3 files changed

+192
-53
lines changed

3 files changed

+192
-53
lines changed

lib/secure_headers/task_helper.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# frozen_string_literal: true
2+
3+
module SecureHeaders
4+
module TaskHelper
5+
include SecureHeaders::HashHelper
6+
7+
INLINE_SCRIPT_REGEX = /(<script(\s*(?!src)([\w\-])+=([\"\'])[^\"\']+\4)*\s*>)(.*?)<\/script>/mx
8+
INLINE_STYLE_REGEX = /(<style[^>]*>)(.*?)<\/style>/mx
9+
INLINE_HASH_SCRIPT_HELPER_REGEX = /<%=\s?hashed_javascript_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx
10+
INLINE_HASH_STYLE_HELPER_REGEX = /<%=\s?hashed_style_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx
11+
12+
def generate_inline_script_hashes(filename)
13+
hashes = []
14+
15+
hashes.concat find_inline_content(filename, INLINE_SCRIPT_REGEX, false)
16+
hashes.concat find_inline_content(filename, INLINE_HASH_SCRIPT_HELPER_REGEX, true)
17+
18+
hashes
19+
end
20+
21+
def generate_inline_style_hashes(filename)
22+
hashes = []
23+
24+
hashes.concat find_inline_content(filename, INLINE_STYLE_REGEX, false)
25+
hashes.concat find_inline_content(filename, INLINE_HASH_STYLE_HELPER_REGEX, true)
26+
27+
hashes
28+
end
29+
30+
def dynamic_content?(filename, inline_script)
31+
!!(
32+
(is_mustache?(filename) && inline_script =~ /\{\{.*\}\}/) ||
33+
(is_erb?(filename) && inline_script =~ /<%.*%>/)
34+
)
35+
end
36+
37+
private
38+
39+
def find_inline_content(filename, regex, strip_trailing_whitespace)
40+
hashes = []
41+
file = File.read(filename)
42+
file.scan(regex) do # TODO don't use gsub
43+
inline_script = Regexp.last_match.captures.last
44+
inline_script.gsub!(/(\r?\n)[\t ]+\z/, '\1') if strip_trailing_whitespace
45+
if dynamic_content?(filename, inline_script)
46+
puts "Looks like there's some dynamic content inside of a tag :-/"
47+
puts "That pretty much means the hash value will never match."
48+
puts "Code: " + inline_script
49+
puts "=" * 20
50+
end
51+
52+
hashes << hash_source(inline_script)
53+
end
54+
hashes
55+
end
56+
57+
def is_erb?(filename)
58+
filename =~ /\.erb\Z/
59+
end
60+
61+
def is_mustache?(filename)
62+
filename =~ /\.mustache\Z/
63+
end
64+
end
65+
end

lib/tasks/tasks.rake

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,8 @@
11
# frozen_string_literal: true
2-
INLINE_SCRIPT_REGEX = /(<script(\s*(?!src)([\w\-])+=([\"\'])[^\"\']+\4)*\s*>)(.*?)<\/script>/mx unless defined? INLINE_SCRIPT_REGEX
3-
INLINE_STYLE_REGEX = /(<style[^>]*>)(.*?)<\/style>/mx unless defined? INLINE_STYLE_REGEX
4-
INLINE_HASH_SCRIPT_HELPER_REGEX = /<%=\s?hashed_javascript_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx unless defined? INLINE_HASH_SCRIPT_HELPER_REGEX
5-
INLINE_HASH_STYLE_HELPER_REGEX = /<%=\s?hashed_style_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx unless defined? INLINE_HASH_STYLE_HELPER_REGEX
2+
require "secure_headers/task_helper"
63

74
namespace :secure_headers do
8-
include SecureHeaders::HashHelper
9-
10-
def is_erb?(filename)
11-
filename =~ /\.erb\Z/
12-
end
13-
14-
def is_mustache?(filename)
15-
filename =~ /\.mustache\Z/
16-
end
17-
18-
def dynamic_content?(filename, inline_script)
19-
(is_mustache?(filename) && inline_script =~ /\{\{.*\}\}/) ||
20-
(is_erb?(filename) && inline_script =~ /<%.*%>/)
21-
end
22-
23-
def find_inline_content(filename, regex, hashes, strip_trailing_whitespace)
24-
file = File.read(filename)
25-
file.scan(regex) do # TODO don't use gsub
26-
inline_script = Regexp.last_match.captures.last
27-
inline_script.gsub!(/(\r?\n)[\t ]+\z/, '\1') if strip_trailing_whitespace
28-
if dynamic_content?(filename, inline_script)
29-
puts "Looks like there's some dynamic content inside of a tag :-/"
30-
puts "That pretty much means the hash value will never match."
31-
puts "Code: " + inline_script
32-
puts "=" * 20
33-
end
34-
35-
hashes << hash_source(inline_script)
36-
end
37-
end
38-
39-
def generate_inline_script_hashes(filename)
40-
hashes = []
41-
42-
find_inline_content(filename, INLINE_SCRIPT_REGEX, hashes, false)
43-
find_inline_content(filename, INLINE_HASH_SCRIPT_HELPER_REGEX, hashes, true)
44-
45-
hashes
46-
end
47-
48-
def generate_inline_style_hashes(filename)
49-
hashes = []
50-
51-
find_inline_content(filename, INLINE_STYLE_REGEX, hashes, false)
52-
find_inline_content(filename, INLINE_HASH_STYLE_HELPER_REGEX, hashes, true)
53-
54-
hashes
55-
end
5+
include SecureHeaders::TaskHelper
566

577
desc "Generate #{SecureHeaders::Configuration::HASH_CONFIG_FILE}"
588
task :generate_hashes do |t, args|
@@ -77,6 +27,7 @@ namespace :secure_headers do
7727
file.write(script_hashes.to_yaml)
7828
end
7929

80-
puts "Script hashes from " + script_hashes.keys.size.to_s + " files added to #{SecureHeaders::Configuration::HASH_CONFIG_FILE}"
30+
file_count = (script_hashes["scripts"].keys + script_hashes["styles"].keys).uniq.count
31+
puts "Script and style hashes from #{file_count} files added to #{SecureHeaders::Configuration::HASH_CONFIG_FILE}"
8132
end
8233
end
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# frozen_string_literal: true
2+
require "spec_helper"
3+
require "secure_headers/task_helper"
4+
5+
class TestHelper
6+
include SecureHeaders::TaskHelper
7+
end
8+
9+
module SecureHeaders
10+
describe TaskHelper do
11+
subject { TestHelper.new }
12+
13+
let(:template) do
14+
<<EOT
15+
<html>
16+
<head>
17+
<script>alert("Hello World!")</script>
18+
<style>p { color: red; }</style>
19+
<%= hashed_javascript_tag do %>
20+
alert("Using the helper tag!")
21+
<% end %>
22+
<%= hashed_style_tag do %>
23+
p { text-decoration: underline; }
24+
<% end %>
25+
</head>
26+
<body>
27+
<p>Testing</p>
28+
</body>
29+
</html>
30+
EOT
31+
end
32+
33+
let(:template_unindented) do
34+
<<EOT
35+
<html>
36+
<head>
37+
<script>alert("Hello World!")</script>
38+
<style>p { color: red; }</style>
39+
<%= hashed_javascript_tag do %>
40+
alert("Using the helper tag!")
41+
<% end %>
42+
<%= hashed_style_tag do %>
43+
p { text-decoration: underline; }
44+
<% end %>
45+
</head>
46+
<body>
47+
<p>Testing</p>
48+
</body>
49+
</html>
50+
EOT
51+
end
52+
53+
describe "#generate_inline_script_hashes" do
54+
let(:expected_hashes) do
55+
[
56+
"'sha256-EE/znQZ7BcfM3LbsqxUc5JlCtE760Pc2RV18tW90DCo='",
57+
"'sha256-64ro9ciexeO5JqSZcAnhmJL4wbzCrpsZJLWl5H6mrkA='"
58+
]
59+
end
60+
61+
it "returns an array of found script hashes" do
62+
Tempfile.create("script") do |f|
63+
f.write template
64+
f.flush
65+
expect(subject.generate_inline_script_hashes(f.path)).to eq expected_hashes
66+
end
67+
end
68+
it "returns the same array no matter the indentation of helper end tags" do
69+
Tempfile.create("script") do |f|
70+
f.write template_unindented
71+
f.flush
72+
expect(subject.generate_inline_script_hashes(f.path)).to eq expected_hashes
73+
end
74+
end
75+
end
76+
77+
describe "#generate_inline_style_hashes" do
78+
let(:expected_hashes) do
79+
[
80+
"'sha256-pckGv9YvNcB5xy+Y4fbqhyo+ib850wyiuWeNbZvLi00='",
81+
"'sha256-d374zYt40cLTr8J7Cvm/l4oDY4P9UJ8TWhYG0iEglU4='"
82+
]
83+
end
84+
85+
it "returns an array of found style hashes" do
86+
Tempfile.create("style") do |f|
87+
f.write template
88+
f.flush
89+
expect(subject.generate_inline_style_hashes(f.path)).to eq expected_hashes
90+
end
91+
end
92+
it "returns the same array no matter the indentation of helper end tags" do
93+
Tempfile.create("style") do |f|
94+
f.write template_unindented
95+
f.flush
96+
expect(subject.generate_inline_style_hashes(f.path)).to eq expected_hashes
97+
end
98+
end
99+
end
100+
101+
describe "#dynamic_content?" do
102+
context "mustache file" do
103+
it "finds mustache templating tokens" do
104+
expect(subject.dynamic_content?("file.mustache", "var test = {{ dynamic_value }};")).to be true
105+
end
106+
107+
it "returns false when not finding any templating tokens" do
108+
expect(subject.dynamic_content?("file.mustache", "var test = 'static value';")).to be false
109+
end
110+
end
111+
112+
context "erb file" do
113+
it "finds erb templating tokens" do
114+
expect(subject.dynamic_content?("file.erb", "var test = <%= dynamic_value %>;")).to be true
115+
end
116+
117+
it "returns false when not finding any templating tokens" do
118+
expect(subject.dynamic_content?("file.erb", "var test = 'static value';")).to be false
119+
end
120+
end
121+
end
122+
end
123+
end

0 commit comments

Comments
 (0)