brakeman-lib 4.3.1 → 4.4.0
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/CHANGES.md +24 -1
- data/README.md +35 -6
- data/bin/brakeman +2 -0
- data/lib/brakeman.rb +5 -3
- data/lib/brakeman/app_tree.rb +15 -1
- data/lib/brakeman/call_index.rb +7 -4
- data/lib/brakeman/checks.rb +16 -8
- data/lib/brakeman/checks/base_check.rb +2 -19
- data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +1 -1
- data/lib/brakeman/checks/check_content_tag.rb +4 -4
- data/lib/brakeman/checks/check_create_with.rb +1 -1
- data/lib/brakeman/checks/check_cross_site_scripting.rb +3 -3
- data/lib/brakeman/checks/check_default_routes.rb +3 -3
- data/lib/brakeman/checks/check_deserialize.rb +1 -1
- data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
- data/lib/brakeman/checks/check_digest_dos.rb +4 -4
- data/lib/brakeman/checks/check_escape_function.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +5 -4
- data/lib/brakeman/checks/check_file_access.rb +13 -3
- data/lib/brakeman/checks/check_file_disclosure.rb +1 -1
- data/lib/brakeman/checks/check_filter_skipping.rb +1 -1
- data/lib/brakeman/checks/check_forgery_setting.rb +3 -3
- data/lib/brakeman/checks/check_header_dos.rb +3 -3
- data/lib/brakeman/checks/check_i18n_xss.rb +3 -3
- data/lib/brakeman/checks/check_jruby_xml.rb +1 -1
- data/lib/brakeman/checks/check_json_encoding.rb +3 -3
- data/lib/brakeman/checks/check_json_parsing.rb +8 -11
- data/lib/brakeman/checks/check_link_to.rb +3 -3
- data/lib/brakeman/checks/check_link_to_href.rb +2 -2
- data/lib/brakeman/checks/check_mail_to.rb +3 -3
- data/lib/brakeman/checks/check_mime_type_dos.rb +1 -1
- data/lib/brakeman/checks/check_model_attributes.rb +4 -4
- data/lib/brakeman/checks/check_model_serialize.rb +1 -1
- data/lib/brakeman/checks/check_nested_attributes.rb +3 -3
- data/lib/brakeman/checks/check_nested_attributes_bypass.rb +1 -1
- data/lib/brakeman/checks/check_number_to_currency.rb +4 -4
- data/lib/brakeman/checks/check_quote_table_name.rb +2 -2
- data/lib/brakeman/checks/check_regex_dos.rb +1 -1
- data/lib/brakeman/checks/check_render.rb +2 -2
- data/lib/brakeman/checks/check_render_dos.rb +1 -1
- data/lib/brakeman/checks/check_render_inline.rb +1 -1
- data/lib/brakeman/checks/check_response_splitting.rb +1 -1
- data/lib/brakeman/checks/check_route_dos.rb +1 -1
- data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +1 -1
- data/lib/brakeman/checks/check_sanitize_methods.rb +3 -3
- data/lib/brakeman/checks/check_secrets.rb +1 -1
- data/lib/brakeman/checks/check_select_tag.rb +1 -1
- data/lib/brakeman/checks/check_select_vulnerability.rb +1 -1
- data/lib/brakeman/checks/check_session_manipulation.rb +1 -1
- data/lib/brakeman/checks/check_session_settings.rb +1 -1
- data/lib/brakeman/checks/check_simple_format.rb +2 -2
- data/lib/brakeman/checks/check_single_quotes.rb +14 -10
- data/lib/brakeman/checks/check_skip_before_filter.rb +2 -2
- data/lib/brakeman/checks/check_sprockets_path_traversal.rb +39 -0
- data/lib/brakeman/checks/check_sql.rb +1 -1
- data/lib/brakeman/checks/check_sql_cves.rb +2 -2
- data/lib/brakeman/checks/check_strip_tags.rb +10 -8
- data/lib/brakeman/checks/check_symbol_dos.rb +1 -1
- data/lib/brakeman/checks/check_symbol_dos_cve.rb +1 -1
- data/lib/brakeman/checks/check_translate_bug.rb +7 -7
- data/lib/brakeman/checks/check_unsafe_reflection.rb +1 -1
- data/lib/brakeman/checks/check_unscoped_find.rb +1 -1
- data/lib/brakeman/checks/check_validation_regex.rb +1 -1
- data/lib/brakeman/checks/check_weak_hash.rb +18 -19
- data/lib/brakeman/checks/check_xml_dos.rb +1 -1
- data/lib/brakeman/checks/check_yaml_parsing.rb +1 -1
- data/lib/brakeman/format/style.css +8 -0
- data/lib/brakeman/messages.rb +220 -0
- data/lib/brakeman/options.rb +13 -0
- data/lib/brakeman/parsers/template_parser.rb +2 -2
- data/lib/brakeman/processors/alias_processor.rb +7 -0
- data/lib/brakeman/processors/config_processor.rb +4 -1
- data/lib/brakeman/processors/gem_processor.rb +30 -2
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +2 -1
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +0 -2
- data/lib/brakeman/processors/lib/rails4_config_processor.rb +18 -0
- data/lib/brakeman/processors/lib/render_helper.rb +5 -0
- data/lib/brakeman/processors/lib/render_path.rb +15 -0
- data/lib/brakeman/processors/library_processor.rb +1 -1
- data/lib/brakeman/report/report_base.rb +17 -161
- data/lib/brakeman/report/report_csv.rb +17 -0
- data/lib/brakeman/report/report_html.rb +34 -31
- data/lib/brakeman/report/report_json.rb +21 -0
- data/lib/brakeman/report/report_markdown.rb +13 -6
- data/lib/brakeman/report/report_table.rb +157 -0
- data/lib/brakeman/report/report_tabs.rb +3 -1
- data/lib/brakeman/report/report_text.rb +16 -0
- data/lib/brakeman/scanner.rb +5 -1
- data/lib/brakeman/tracker/config.rb +1 -1
- data/lib/brakeman/util.rb +0 -17
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +9 -4
- data/lib/brakeman/warning_codes.rb +1 -0
- metadata +13 -10
@@ -23,7 +23,7 @@ class Brakeman::CheckXMLDoS < Brakeman::BaseCheck
|
|
23
23
|
|
24
24
|
return if has_workaround?
|
25
25
|
|
26
|
-
message = "
|
26
|
+
message = msg(msg_version(version), " is vulnerable to denial of service via XML parsing ", msg_cve("CVE-2015-3227"), ". Upgrade to ", msg_version(fix_version))
|
27
27
|
|
28
28
|
warn :warning_type => "Denial of Service",
|
29
29
|
:warning_code => :CVE_2015_3227,
|
@@ -22,7 +22,7 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
|
|
22
22
|
"3.2.11"
|
23
23
|
end
|
24
24
|
|
25
|
-
message = "
|
25
|
+
message = msg(msg_version(rails_version), " has a remote code execution vulnerability. Upgrade to ", msg_version(new_version), " or disable XML parsing")
|
26
26
|
|
27
27
|
warn :warning_type => "Remote Code Execution",
|
28
28
|
:warning_code => :CVE_2013_0156,
|
@@ -0,0 +1,220 @@
|
|
1
|
+
module Brakeman
|
2
|
+
module Messages
|
3
|
+
# Create a new message from a list of messages.
|
4
|
+
# Strings are converted to Brakeman::Messages::Plain objects.
|
5
|
+
def msg *args
|
6
|
+
parts = args.map do |a|
|
7
|
+
if a.is_a? String
|
8
|
+
Plain.new(a)
|
9
|
+
else
|
10
|
+
a
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Message.new(*parts)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a new code message fragment
|
18
|
+
def msg_code code
|
19
|
+
Code.new code
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a new message fragment with a CVE identifier
|
23
|
+
def msg_cve cve
|
24
|
+
CVE.new cve
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a new message fragment representing a file name
|
28
|
+
def msg_file str
|
29
|
+
Messages::FileName.new str
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create a new message fragment from a user input type (e.g. `:params`).
|
33
|
+
# The input type will be converted to a friendly version (e.g. "parameter value").
|
34
|
+
def msg_input input
|
35
|
+
Input.new input
|
36
|
+
end
|
37
|
+
|
38
|
+
# Create a new message fragment which will not be modified during output
|
39
|
+
def msg_lit str
|
40
|
+
Literal.new str
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create a new plain string message fragment
|
44
|
+
def msg_plain str
|
45
|
+
Plain.new str
|
46
|
+
end
|
47
|
+
|
48
|
+
# Create a message fragment representing the version of a library
|
49
|
+
def msg_version version, lib = "Rails"
|
50
|
+
Version.new version, lib
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Class to represent a list of message types
|
56
|
+
class Brakeman::Messages::Message
|
57
|
+
def initialize *args
|
58
|
+
@parts = args.map do |a|
|
59
|
+
case a
|
60
|
+
when String, Symbol
|
61
|
+
Brakeman::Messages::Plain.new(a.to_s)
|
62
|
+
else
|
63
|
+
a
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def << msg
|
69
|
+
if msg.is_a? String
|
70
|
+
@parts << Brakeman::Messages::Plain.new(msg)
|
71
|
+
else
|
72
|
+
@parts << msg
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
output = @parts.map(&:to_s).join
|
78
|
+
|
79
|
+
case @parts.first
|
80
|
+
when Brakeman::Messages::Code, Brakeman::Messages::Literal, Brakeman::Messages::Version
|
81
|
+
else
|
82
|
+
output[0] = output[0].capitalize
|
83
|
+
end
|
84
|
+
|
85
|
+
output
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_html
|
89
|
+
require 'cgi'
|
90
|
+
|
91
|
+
output = @parts.map(&:to_html).join
|
92
|
+
|
93
|
+
case @parts.first
|
94
|
+
when Brakeman::Messages::Code, Brakeman::Messages::Literal, Brakeman::Messages::Version
|
95
|
+
else
|
96
|
+
output[0] = output[0].capitalize
|
97
|
+
end
|
98
|
+
|
99
|
+
output
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Brakeman::Messages::Code
|
104
|
+
def initialize code
|
105
|
+
@code = code.to_s
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
"`#{@code}`"
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_html
|
113
|
+
"<span class=\"code\">#{CGI.escapeHTML(@code)}</span>"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Brakeman::Messages::CVE
|
118
|
+
def initialize cve
|
119
|
+
@cve = cve
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_s
|
123
|
+
"(#{@cve})"
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_html
|
127
|
+
"(<a href=\"https://cve.mitre.org/cgi-bin/cvename.cgi?name=#{@cve}\" target=\"_blank\" rel=\"noreferrer\">#{@cve}</a>)"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Brakeman::Messages::FileName
|
132
|
+
def initialize file
|
133
|
+
@file = file
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_s
|
137
|
+
"`#{@file}`"
|
138
|
+
end
|
139
|
+
|
140
|
+
def to_html
|
141
|
+
"<span class=\"filename\">#{CGI.escapeHTML(@file)}</span>"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Brakeman::Messages::Input
|
146
|
+
def initialize input
|
147
|
+
@input = input
|
148
|
+
@value = friendly_type_of(@input)
|
149
|
+
end
|
150
|
+
|
151
|
+
def friendly_type_of input_type
|
152
|
+
if input_type.is_a? Brakeman::BaseCheck::Match
|
153
|
+
input_type = input_type.type
|
154
|
+
end
|
155
|
+
|
156
|
+
case input_type
|
157
|
+
when :params
|
158
|
+
"parameter value"
|
159
|
+
when :cookies
|
160
|
+
"cookie value"
|
161
|
+
when :request
|
162
|
+
"request value"
|
163
|
+
when :model
|
164
|
+
"model attribute"
|
165
|
+
else
|
166
|
+
"user input"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_s
|
171
|
+
@value
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_html
|
175
|
+
self.to_s
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class Brakeman::Messages::Literal
|
180
|
+
def initialize value
|
181
|
+
@value = value.to_s
|
182
|
+
end
|
183
|
+
|
184
|
+
def to_s
|
185
|
+
@value
|
186
|
+
end
|
187
|
+
|
188
|
+
def to_html
|
189
|
+
@value
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class Brakeman::Messages::Plain
|
194
|
+
def initialize string
|
195
|
+
@value = string
|
196
|
+
end
|
197
|
+
|
198
|
+
def to_s
|
199
|
+
@value
|
200
|
+
end
|
201
|
+
|
202
|
+
def to_html
|
203
|
+
CGI.escapeHTML(@value)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class Brakeman::Messages::Version
|
208
|
+
def initialize version, lib
|
209
|
+
@version = version
|
210
|
+
@library = lib
|
211
|
+
end
|
212
|
+
|
213
|
+
def to_s
|
214
|
+
"#{@library} #{@version}"
|
215
|
+
end
|
216
|
+
|
217
|
+
def to_html
|
218
|
+
CGI.escapeHTML(self.to_s)
|
219
|
+
end
|
220
|
+
end
|
data/lib/brakeman/options.rb
CHANGED
@@ -169,6 +169,19 @@ module Brakeman::Options
|
|
169
169
|
options[:engine_paths].merge paths
|
170
170
|
end
|
171
171
|
|
172
|
+
opts.on "-E", "--enable Check1,Check2,etc", Array, "Enable the specified checks" do |checks|
|
173
|
+
checks.map! do |check|
|
174
|
+
if check.start_with? "Check"
|
175
|
+
check
|
176
|
+
else
|
177
|
+
"Check" << check
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
options[:enable_checks] ||= Set.new
|
182
|
+
options[:enable_checks].merge checks
|
183
|
+
end
|
184
|
+
|
172
185
|
opts.on "-t", "--test Check1,Check2,etc", Array, "Only run the specified checks" do |checks|
|
173
186
|
checks.each_with_index do |s, index|
|
174
187
|
if s[0,5] != "Check"
|
@@ -59,9 +59,9 @@ module Brakeman
|
|
59
59
|
else
|
60
60
|
require 'erb'
|
61
61
|
src = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
|
62
|
-
ERB.new(text, trim_mode:
|
62
|
+
ERB.new(text, trim_mode: '-').src
|
63
63
|
else
|
64
|
-
ERB.new(text, nil,
|
64
|
+
ERB.new(text, nil, '-').src
|
65
65
|
end
|
66
66
|
src.sub!(/^#.*\n/, '') if Brakeman::Scanner::RUBY_1_9
|
67
67
|
src
|
@@ -731,6 +731,13 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
731
731
|
exp[2 + i] = process_if_branch branch
|
732
732
|
end
|
733
733
|
else
|
734
|
+
# Translate `if !...` into `unless ...`
|
735
|
+
# Technically they are different but that's only if someone overrides `!`
|
736
|
+
if call? condition and condition.method == :!
|
737
|
+
condition = condition.target
|
738
|
+
exps.reverse!
|
739
|
+
end
|
740
|
+
|
734
741
|
was_inside = @inside_if
|
735
742
|
@inside_if = true
|
736
743
|
|
@@ -1,11 +1,14 @@
|
|
1
1
|
require 'brakeman/processors/base_processor'
|
2
2
|
require 'brakeman/processors/alias_processor'
|
3
|
+
require 'brakeman/processors/lib/rails4_config_processor.rb'
|
3
4
|
require 'brakeman/processors/lib/rails3_config_processor.rb'
|
4
5
|
require 'brakeman/processors/lib/rails2_config_processor.rb'
|
5
6
|
|
6
7
|
class Brakeman::ConfigProcessor
|
7
8
|
def self.new tracker
|
8
|
-
if tracker.options[:
|
9
|
+
if tracker.options[:rails4]
|
10
|
+
Brakeman::Rails4ConfigProcessor.new tracker
|
11
|
+
elsif tracker.options[:rails3]
|
9
12
|
Brakeman::Rails3ConfigProcessor.new tracker
|
10
13
|
else
|
11
14
|
Brakeman::Rails2ConfigProcessor.new tracker
|
@@ -10,8 +10,17 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
|
|
10
10
|
|
11
11
|
def process_gems gem_files
|
12
12
|
@gem_files = gem_files
|
13
|
-
@gemfile = gem_files[:gemfile][:file]
|
14
|
-
|
13
|
+
@gemfile = gem_files[:gemfile] && gem_files[:gemfile][:file]
|
14
|
+
@gemspec = gem_files[:gemspec] && gem_files[:gemspec][:file]
|
15
|
+
|
16
|
+
|
17
|
+
if @gemspec
|
18
|
+
process gem_files[:gemspec][:src]
|
19
|
+
end
|
20
|
+
|
21
|
+
if @gemfile
|
22
|
+
process gem_files[:gemfile][:src]
|
23
|
+
end
|
15
24
|
|
16
25
|
if gem_files[:gemlock]
|
17
26
|
process_gem_lock
|
@@ -41,11 +50,30 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
|
|
41
50
|
@tracker.config.set_ruby_version version.value
|
42
51
|
end
|
43
52
|
end
|
53
|
+
elsif @inside_gemspec and exp.method == :add_dependency
|
54
|
+
if string? exp.first_arg and string? exp.last_arg
|
55
|
+
@tracker.config.add_gem exp.first_arg.value, exp.last_arg.value, @gemspec, exp.line
|
56
|
+
end
|
44
57
|
end
|
45
58
|
|
46
59
|
exp
|
47
60
|
end
|
48
61
|
|
62
|
+
GEM_SPEC = s(:colon2, s(:const, :Gem), :Specification)
|
63
|
+
|
64
|
+
def process_iter exp
|
65
|
+
if exp.block_call.target == GEM_SPEC and exp.block_call.method == :new
|
66
|
+
@inside_gemspec = true
|
67
|
+
process exp.block if sexp? exp.block
|
68
|
+
|
69
|
+
exp
|
70
|
+
else
|
71
|
+
process_default exp
|
72
|
+
end
|
73
|
+
ensure
|
74
|
+
@inside_gemspec = false
|
75
|
+
end
|
76
|
+
|
49
77
|
def process_gem_lock
|
50
78
|
line_num = 1
|
51
79
|
file = @gem_files[:gemlock][:file]
|
@@ -9,7 +9,8 @@ module Brakeman
|
|
9
9
|
# Join two array literals into one.
|
10
10
|
def join_arrays lhs, rhs, original_exp = nil
|
11
11
|
if array? lhs and array? rhs
|
12
|
-
result = Sexp.new(:array)
|
12
|
+
result = Sexp.new(:array)
|
13
|
+
result.line(lhs.line || rhs.line)
|
13
14
|
result.concat lhs[1..-1]
|
14
15
|
result.concat rhs[1..-1]
|
15
16
|
result
|
@@ -183,8 +183,6 @@ class Brakeman::Rails3RoutesProcessor < Brakeman::BasicProcessor
|
|
183
183
|
else
|
184
184
|
add_route route[1], route[0]
|
185
185
|
end
|
186
|
-
elsif in_controller_block? and symbol? first_arg
|
187
|
-
add_route first_arg
|
188
186
|
else hash? first_arg
|
189
187
|
hash_iterate first_arg do |k, v|
|
190
188
|
if string? k
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'brakeman/processors/lib/rails3_config_processor'
|
2
|
+
|
3
|
+
class Brakeman::Rails4ConfigProcessor < Brakeman::Rails3ConfigProcessor
|
4
|
+
APPLICATION_CONFIG = s(:call, s(:call, s(:const, :Rails), :application), :configure)
|
5
|
+
|
6
|
+
# Look for Rails.application.configure do ... end
|
7
|
+
def process_iter exp
|
8
|
+
if exp.block_call == APPLICATION_CONFIG
|
9
|
+
@inside_config = true
|
10
|
+
process exp.block if sexp? exp.block
|
11
|
+
@inside_config = false
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
exp
|
17
|
+
end
|
18
|
+
end
|
@@ -65,6 +65,11 @@ module Brakeman::RenderHelper
|
|
65
65
|
return
|
66
66
|
end
|
67
67
|
|
68
|
+
if called_from
|
69
|
+
# Track actual template that was rendered
|
70
|
+
called_from.last_template = template
|
71
|
+
end
|
72
|
+
|
68
73
|
template_env = only_ivars(:include_request_vars)
|
69
74
|
|
70
75
|
#Hash the environment and the source of the template to avoid
|
@@ -29,6 +29,17 @@ module Brakeman
|
|
29
29
|
self
|
30
30
|
end
|
31
31
|
|
32
|
+
def last_template= template
|
33
|
+
if @path.last
|
34
|
+
@path.last[:rendered] = {
|
35
|
+
name: template.name,
|
36
|
+
file: template.file,
|
37
|
+
}
|
38
|
+
else
|
39
|
+
Brakeman.debug "[Notice] No render path to add template information"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
32
43
|
def include_template? name
|
33
44
|
name = name.to_sym
|
34
45
|
|
@@ -71,6 +82,10 @@ module Brakeman
|
|
71
82
|
@path.length
|
72
83
|
end
|
73
84
|
|
85
|
+
def map &block
|
86
|
+
@path.map &block
|
87
|
+
end
|
88
|
+
|
74
89
|
def to_a
|
75
90
|
@path.map do |loc|
|
76
91
|
case loc[:type]
|