brakeman-min 4.3.1 → 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +9 -6
@@ -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]
|