forked from rubocop/rubocop-rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoutput_safety.rb
99 lines (90 loc) · 3.3 KB
/
output_safety.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
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# This cop checks for the use of output safety calls like `html_safe`,
# `raw`, and `safe_concat`. These methods do not escape content. They
# simply return a SafeBuffer containing the content as is. Instead,
# use `safe_join` to join content and escape it and concat to
# concatenate content and escape it, ensuring its safety.
#
# @example
# user_content = "<b>hi</b>"
#
# # bad
# "<p>#{user_content}</p>".html_safe
# # => ActiveSupport::SafeBuffer "<p><b>hi</b></p>"
#
# # good
# content_tag(:p, user_content)
# # => ActiveSupport::SafeBuffer "<p><b>hi</b></p>"
#
# # bad
# out = ""
# out << "<li>#{user_content}</li>"
# out << "<li>#{user_content}</li>"
# out.html_safe
# # => ActiveSupport::SafeBuffer "<li><b>hi</b></li><li><b>hi</b></li>"
#
# # good
# out = []
# out << content_tag(:li, user_content)
# out << content_tag(:li, user_content)
# safe_join(out)
# # => ActiveSupport::SafeBuffer
# # "<li><b>hi</b></li><li><b>hi</b></li>"
#
# # bad
# out = "<h1>trusted content</h1>".html_safe
# out.safe_concat(user_content)
# # => ActiveSupport::SafeBuffer "<h1>trusted_content</h1><b>hi</b>"
#
# # good
# out = "<h1>trusted content</h1>".html_safe
# out.concat(user_content)
# # => ActiveSupport::SafeBuffer
# # "<h1>trusted_content</h1><b>hi</b>"
#
# # safe, though maybe not good style
# out = "trusted content"
# result = out.concat(user_content)
# # => String "trusted content<b>hi</b>"
# # because when rendered in ERB the String will be escaped:
# # <%= result %>
# # => trusted content<b>hi</b>
#
# # bad
# (user_content + " " + content_tag(:span, user_content)).html_safe
# # => ActiveSupport::SafeBuffer "<b>hi</b> <span><b>hi</b></span>"
#
# # good
# safe_join([user_content, " ", content_tag(:span, user_content)])
# # => ActiveSupport::SafeBuffer
# # "<b>hi</b> <span><b>hi</b></span>"
class OutputSafety < Cop
MSG = 'Tagging a string as html safe may be a security risk.'
def on_send(node)
return if non_interpolated_string?(node)
return unless looks_like_rails_html_safe?(node) ||
looks_like_rails_raw?(node) ||
looks_like_rails_safe_concat?(node)
add_offense(node, location: :selector)
end
alias on_csend on_send
private
def non_interpolated_string?(node)
node.receiver&.str_type? && !node.receiver.dstr_type?
end
def looks_like_rails_html_safe?(node)
node.receiver && node.method?(:html_safe) && !node.arguments?
end
def looks_like_rails_raw?(node)
node.command?(:raw) && node.arguments.one?
end
def looks_like_rails_safe_concat?(node)
node.method?(:safe_concat) && node.arguments.one?
end
end
end
end
end