Class: Herb::Engine::Validators::SecurityValidator
Instance Attribute Summary
#diagnostics, #enabled
Instance Method Summary
collapse
#clear_diagnostics, #diagnostic_count, #enabled?, #error, #errors, #errors?, #hint, #info, #initialize, #validate, #warning, #warnings, #warnings?
Methods inherited from Visitor
#visit, #visit_all, #visit_cdata_node, #visit_child_nodes, #visit_document_node, #visit_erb_begin_node, #visit_erb_block_node, #visit_erb_case_match_node, #visit_erb_case_node, #visit_erb_content_node, #visit_erb_else_node, #visit_erb_end_node, #visit_erb_ensure_node, #visit_erb_for_node, #visit_erb_if_node, #visit_erb_in_node, #visit_erb_node, #visit_erb_open_tag_node, #visit_erb_render_node, #visit_erb_rescue_node, #visit_erb_strict_locals_node, #visit_erb_unless_node, #visit_erb_until_node, #visit_erb_when_node, #visit_erb_while_node, #visit_erb_yield_node, #visit_html_attribute_node, #visit_html_attribute_value_node, #visit_html_close_tag_node, #visit_html_comment_node, #visit_html_conditional_element_node, #visit_html_conditional_open_tag_node, #visit_html_doctype_node, #visit_html_element_node, #visit_html_omitted_close_tag_node, #visit_html_text_node, #visit_html_virtual_close_tag_node, #visit_literal_node, #visit_node, #visit_ruby_html_attributes_splat_node, #visit_ruby_literal_node, #visit_ruby_parameter_node, #visit_ruby_render_keywords_node, #visit_ruby_render_local_node, #visit_whitespace_node, #visit_xml_declaration_node
#erb_comment?, #erb_graphql?, #erb_output?, #erb_outputs?, #inline_ruby_comment?
Instance Method Details
#add_diagnostic(message, location, severity, code: nil, source: nil, suggestion: nil) ⇒ Object
96
97
98
99
100
101
102
103
104
105
106
107
|
# File 'lib/herb/engine/validators/security_validator.rb', line 96
def add_diagnostic(message, location, severity, code: nil, source: nil, suggestion: nil)
diagnostic = {
message: message,
location: location,
severity: severity,
code: code,
source: source || self.class.name,
suggestion: suggestion,
}
@diagnostics << diagnostic
end
|
#add_security_error(location, message, suggestion) ⇒ Object
91
92
93
94
|
# File 'lib/herb/engine/validators/security_validator.rb', line 91
def add_security_error(location, message, suggestion)
add_diagnostic(message, location, :error, code: "SecurityViolation", source: "SecurityValidator",
suggestion: suggestion)
end
|
#conditional_tag_attributes_prism_node?(prism_node) ⇒ Boolean
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/herb/engine/validators/security_validator.rb', line 75
def conditional_tag_attributes_prism_node?(prism_node)
if prism_node.is_a?(Prism::IfNode) || prism_node.is_a?(Prism::UnlessNode)
body = prism_node.statements&.body
return false unless body
return false unless body.length == 1
return tag_attributes_prism_node?(body.first)
end
if prism_node.is_a?(Prism::AndNode) || prism_node.is_a?(Prism::OrNode)
return tag_attributes_prism_node?(prism_node.right)
end
false
end
|
#tag_attributes_prism_node?(prism_node) ⇒ Boolean
65
66
67
68
69
70
71
72
73
|
# File 'lib/herb/engine/validators/security_validator.rb', line 65
def tag_attributes_prism_node?(prism_node)
return false unless prism_node.is_a?(Prism::CallNode)
return false unless prism_node.name == :attributes
receiver = prism_node.receiver
return false unless receiver.is_a?(Prism::CallNode)
receiver.name == :tag
end
|
#validate_attribute_name_security(node) ⇒ Object
53
54
55
56
57
58
59
60
61
62
63
|
# File 'lib/herb/engine/validators/security_validator.rb', line 53
def validate_attribute_name_security(node)
node.children.each do |child|
next unless child.is_a?(Herb::AST::ERBContentNode) && erb_outputs?(child)
add_security_error(
child.location,
"ERB output in attribute names is not allowed for security reasons.",
"Use static attribute names with dynamic values instead."
)
end
end
|
#validate_tag_security(node) ⇒ Object
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
|
# File 'lib/herb/engine/validators/security_validator.rb', line 24
def validate_tag_security(node)
node.children.each do |child|
next if child.is_a?(Herb::AST::HTMLAttributeNode)
next if child.is_a?(Herb::AST::WhitespaceNode)
next unless child.is_a?(Herb::AST::ERBContentNode) && erb_outputs?(child)
prism_node = child.prism
next if prism_node && tag_attributes_prism_node?(prism_node)
if prism_node && conditional_tag_attributes_prism_node?(prism_node)
add_security_error(
child.location,
"Avoid using conditional `tag.attributes` in attribute position.",
"Use `<% if ... %><%= tag.attributes(...) %><% end %>` instead."
)
next
end
add_security_error(
child.location,
"ERB output tags (<%= %>) are not allowed in attribute position.",
"Use control flow (<% %>) with static attributes instead."
)
end
end
|
#visit_html_attribute_name_node(node) ⇒ Object
16
17
18
19
20
|
# File 'lib/herb/engine/validators/security_validator.rb', line 16
def visit_html_attribute_name_node(node)
validate_attribute_name_security(node)
super
end
|
#visit_html_open_tag_node(node) ⇒ Object
10
11
12
13
14
|
# File 'lib/herb/engine/validators/security_validator.rb', line 10
def visit_html_open_tag_node(node)
validate_tag_security(node)
super
end
|