brakeman-lib 4.5.1 → 4.7.2
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 +158 -109
- data/README.md +1 -2
- data/lib/brakeman/call_index.rb +54 -15
- data/lib/brakeman/checks/base_check.rb +50 -47
- data/lib/brakeman/checks/check_cookie_serialization.rb +22 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +4 -4
- data/lib/brakeman/checks/check_deserialize.rb +3 -6
- data/lib/brakeman/checks/check_execute.rb +26 -1
- data/lib/brakeman/checks/check_file_access.rb +7 -1
- data/lib/brakeman/checks/check_header_dos.rb +2 -2
- data/lib/brakeman/checks/check_i18n_xss.rb +2 -2
- data/lib/brakeman/checks/check_jruby_xml.rb +2 -2
- data/lib/brakeman/checks/check_json_parsing.rb +2 -2
- data/lib/brakeman/checks/check_mass_assignment.rb +1 -1
- data/lib/brakeman/checks/check_mime_type_dos.rb +2 -2
- data/lib/brakeman/checks/check_nested_attributes_bypass.rb +1 -1
- data/lib/brakeman/checks/check_reverse_tabnabbing.rb +58 -0
- data/lib/brakeman/checks/check_sanitize_methods.rb +2 -2
- data/lib/brakeman/checks/check_session_settings.rb +5 -2
- data/lib/brakeman/checks/check_sql.rb +24 -22
- data/lib/brakeman/checks/check_xml_dos.rb +2 -2
- data/lib/brakeman/checks/check_yaml_parsing.rb +10 -18
- data/lib/brakeman/differ.rb +16 -28
- data/lib/brakeman/file_parser.rb +4 -8
- data/lib/brakeman/file_path.rb +14 -0
- data/lib/brakeman/parsers/haml_embedded.rb +1 -1
- data/lib/brakeman/parsers/template_parser.rb +3 -1
- data/lib/brakeman/processor.rb +2 -2
- data/lib/brakeman/processors/alias_processor.rb +15 -1
- data/lib/brakeman/processors/base_processor.rb +2 -0
- data/lib/brakeman/processors/controller_processor.rb +4 -4
- data/lib/brakeman/processors/gem_processor.rb +10 -2
- data/lib/brakeman/processors/haml_template_processor.rb +87 -123
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +5 -4
- data/lib/brakeman/processors/lib/find_all_calls.rb +27 -4
- data/lib/brakeman/processors/lib/find_call.rb +3 -64
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +1 -1
- data/lib/brakeman/processors/template_alias_processor.rb +28 -0
- data/lib/brakeman/processors/template_processor.rb +10 -6
- data/lib/brakeman/report/report_text.rb +4 -5
- data/lib/brakeman/rescanner.rb +4 -0
- data/lib/brakeman/tracker.rb +26 -2
- data/lib/brakeman/tracker/config.rb +38 -73
- data/lib/brakeman/tracker/constants.rb +2 -1
- data/lib/brakeman/util.rb +5 -3
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +4 -0
- data/lib/brakeman/warning_codes.rb +3 -0
- data/lib/ruby_parser/bm_sexp.rb +7 -2
- metadata +18 -17
@@ -33,14 +33,13 @@ require 'brakeman/processors/lib/basic_processor'
|
|
33
33
|
# FindCall.new nil, /^g?sub!?$/
|
34
34
|
class Brakeman::FindCall < Brakeman::BasicProcessor
|
35
35
|
|
36
|
-
def initialize targets, methods, tracker
|
36
|
+
def initialize targets, methods, tracker
|
37
37
|
super tracker
|
38
38
|
@calls = []
|
39
39
|
@find_targets = targets
|
40
40
|
@find_methods = methods
|
41
41
|
@current_class = nil
|
42
42
|
@current_method = nil
|
43
|
-
@in_depth = in_depth
|
44
43
|
end
|
45
44
|
|
46
45
|
#Returns a list of results.
|
@@ -48,10 +47,6 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
|
|
48
47
|
#A result looks like:
|
49
48
|
#
|
50
49
|
# s(:result, :ClassName, :method_name, s(:call, ...))
|
51
|
-
#
|
52
|
-
#or
|
53
|
-
#
|
54
|
-
# s(:result, :template_name, s(:call, ...))
|
55
50
|
def matches
|
56
51
|
@calls
|
57
52
|
end
|
@@ -60,10 +55,7 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
|
|
60
55
|
#or the template. These names are used when reporting results.
|
61
56
|
#
|
62
57
|
#Use FindCall#matches to retrieve results.
|
63
|
-
def process_source exp
|
64
|
-
@current_class = klass
|
65
|
-
@current_method = method
|
66
|
-
@current_template = template
|
58
|
+
def process_source exp
|
67
59
|
process exp
|
68
60
|
end
|
69
61
|
|
@@ -74,11 +66,6 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
|
|
74
66
|
|
75
67
|
alias :process_defs :process_defn
|
76
68
|
|
77
|
-
#Process body of block
|
78
|
-
def process_rlist exp
|
79
|
-
process_all exp
|
80
|
-
end
|
81
|
-
|
82
69
|
#Look for matching calls and add them to results
|
83
70
|
def process_call exp
|
84
71
|
target = get_target exp.target
|
@@ -87,25 +74,9 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
|
|
87
74
|
process_call_args exp
|
88
75
|
|
89
76
|
if match(@find_targets, target) and match(@find_methods, method)
|
90
|
-
|
91
|
-
if @current_template
|
92
|
-
@calls << Sexp.new(:result, @current_template, exp).line(exp.line)
|
93
|
-
else
|
94
|
-
@calls << Sexp.new(:result, @current_module, @current_class, @current_method, exp).line(exp.line)
|
95
|
-
end
|
96
|
-
|
77
|
+
@calls << Sexp.new(:result, @current_module, @current_class, @current_method, exp).line(exp.line)
|
97
78
|
end
|
98
79
|
|
99
|
-
#Normally FindCall won't match a method invocation that is the target of
|
100
|
-
#another call, such as:
|
101
|
-
#
|
102
|
-
# User.find(:first, :conditions => "user = '#{params['user']}').name
|
103
|
-
#
|
104
|
-
#A search for User.find will not match this unless @in_depth is true.
|
105
|
-
if @in_depth and call? exp.target
|
106
|
-
process exp.target
|
107
|
-
end
|
108
|
-
|
109
80
|
exp
|
110
81
|
end
|
111
82
|
|
@@ -123,8 +94,6 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
|
|
123
94
|
case exp.node_type
|
124
95
|
when :ivar, :lvar, :const, :lit
|
125
96
|
exp.value
|
126
|
-
when :true, :false
|
127
|
-
exp.node_type
|
128
97
|
when :colon2
|
129
98
|
class_name exp
|
130
99
|
else
|
@@ -141,43 +110,13 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
|
|
141
110
|
when Symbol
|
142
111
|
if search_terms == item
|
143
112
|
true
|
144
|
-
elsif sexp? item
|
145
|
-
is_instance_of? item, search_terms
|
146
113
|
else
|
147
114
|
false
|
148
115
|
end
|
149
|
-
when Sexp
|
150
|
-
search_terms == item
|
151
116
|
when Enumerable
|
152
117
|
if search_terms.empty?
|
153
118
|
item == nil
|
154
|
-
else
|
155
|
-
search_terms.each do|term|
|
156
|
-
if match(term, item)
|
157
|
-
return true
|
158
|
-
end
|
159
|
-
end
|
160
|
-
false
|
161
119
|
end
|
162
|
-
when Regexp
|
163
|
-
search_terms.match item.to_s
|
164
|
-
when nil
|
165
|
-
true
|
166
|
-
else
|
167
|
-
raise "Cannot match #{search_terms} and #{item}"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
#Checks if +item+ is an instance of +klass+ by looking for Klass.new
|
172
|
-
def is_instance_of? item, klass
|
173
|
-
if call? item
|
174
|
-
if sexp? item.target
|
175
|
-
item.method == :new and item.target.node_type == :const and item.target.value == klass
|
176
|
-
else
|
177
|
-
item.method == :new and item.target == klass
|
178
|
-
end
|
179
|
-
else
|
180
|
-
false
|
181
120
|
end
|
182
121
|
end
|
183
122
|
end
|
@@ -75,7 +75,7 @@ class Brakeman::Rails2ConfigProcessor < Brakeman::BasicProcessor
|
|
75
75
|
def process_cdecl exp
|
76
76
|
#Set Rails version required
|
77
77
|
if exp.lhs == :RAILS_GEM_VERSION
|
78
|
-
@tracker.config.
|
78
|
+
@tracker.config.set_rails_version exp.rhs.value
|
79
79
|
end
|
80
80
|
|
81
81
|
exp
|
@@ -32,6 +32,34 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def process_lasgn exp
|
36
|
+
if exp.lhs == :haml_temp or haml_capture? exp.rhs
|
37
|
+
exp.rhs = process exp.rhs
|
38
|
+
|
39
|
+
# Avoid propagating contents of block
|
40
|
+
if node_type? exp.rhs, :iter
|
41
|
+
new_exp = exp.dup
|
42
|
+
new_exp.rhs = exp.rhs.block_call
|
43
|
+
|
44
|
+
super new_exp
|
45
|
+
|
46
|
+
exp # Still save the original, though
|
47
|
+
else
|
48
|
+
super exp
|
49
|
+
end
|
50
|
+
else
|
51
|
+
super exp
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
HAML_CAPTURE = [:capture, :capture_haml]
|
56
|
+
|
57
|
+
def haml_capture? exp
|
58
|
+
node_type? exp, :iter and
|
59
|
+
call? exp.block_call and
|
60
|
+
HAML_CAPTURE.include? exp.block_call.method
|
61
|
+
end
|
62
|
+
|
35
63
|
#Determine template name
|
36
64
|
def template_name name
|
37
65
|
if !name.to_s.include?('/') && @template.name.to_s.include?('/')
|
@@ -61,9 +61,9 @@ class Brakeman::TemplateProcessor < Brakeman::BaseProcessor
|
|
61
61
|
branches = [arg.then_clause, arg.else_clause].compact
|
62
62
|
|
63
63
|
if branches.empty?
|
64
|
-
s(:nil)
|
64
|
+
s(:nil).line(arg.line)
|
65
65
|
elsif branches.length == 2
|
66
|
-
Sexp.new(:or, *branches)
|
66
|
+
Sexp.new(:or, *branches).line(arg.line)
|
67
67
|
else
|
68
68
|
branches.first
|
69
69
|
end
|
@@ -77,9 +77,13 @@ class Brakeman::TemplateProcessor < Brakeman::BaseProcessor
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def add_output output, type = :output
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
80
|
+
if node_type? output, :or
|
81
|
+
Sexp.new(:or, add_output(output.lhs, type), add_output(output.rhs, type)).line(output.line)
|
82
|
+
else
|
83
|
+
s = Sexp.new(type, output)
|
84
|
+
s.line(output.line)
|
85
|
+
@current_template.add_output s
|
86
|
+
s
|
87
|
+
end
|
84
88
|
end
|
85
89
|
end
|
@@ -19,7 +19,7 @@ class Brakeman::Report::Text < Brakeman::Report::Base
|
|
19
19
|
add_chunk generate_controllers if tracker.options[:debug] or tracker.options[:report_routes]
|
20
20
|
add_chunk generate_templates if tracker.options[:debug]
|
21
21
|
add_chunk generate_obsolete
|
22
|
-
add_chunk generate_errors
|
22
|
+
add_chunk generate_errors
|
23
23
|
add_chunk generate_warnings
|
24
24
|
end
|
25
25
|
|
@@ -51,7 +51,7 @@ class Brakeman::Report::Text < Brakeman::Report::Base
|
|
51
51
|
|
52
52
|
def generate_header
|
53
53
|
[
|
54
|
-
header("Brakeman Report"),
|
54
|
+
header("Brakeman Report"),
|
55
55
|
label("Application Path", tracker.app_path),
|
56
56
|
label("Rails Version", rails_version),
|
57
57
|
label("Brakeman Version", Brakeman::Version),
|
@@ -92,7 +92,7 @@ class Brakeman::Report::Text < Brakeman::Report::Base
|
|
92
92
|
HighLine.color("No warnings found", :bold, :green)
|
93
93
|
else
|
94
94
|
warnings = tracker.filtered_warnings.sort_by do |w|
|
95
|
-
[w.confidence, w.warning_type, w.fingerprint]
|
95
|
+
[w.confidence, w.warning_type, w.file, w.line, w.fingerprint]
|
96
96
|
end.map do |w|
|
97
97
|
output_warning w
|
98
98
|
end
|
@@ -140,7 +140,7 @@ class Brakeman::Report::Text < Brakeman::Report::Base
|
|
140
140
|
end
|
141
141
|
|
142
142
|
double_space "Template Output", template_rows.sort_by { |name, value| name.to_s }.map { |template|
|
143
|
-
[HighLine.new.color(template.first
|
143
|
+
[HighLine.new.color("#{template.first}\n", :cyan)] + template[1]
|
144
144
|
}.compact
|
145
145
|
end
|
146
146
|
|
@@ -211,4 +211,3 @@ class Brakeman::Report::Text < Brakeman::Report::Base
|
|
211
211
|
double_space "Controller Overview", controllers
|
212
212
|
end
|
213
213
|
end
|
214
|
-
|
data/lib/brakeman/rescanner.rb
CHANGED
@@ -226,9 +226,13 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
226
226
|
end
|
227
227
|
|
228
228
|
def rescan_initializer path
|
229
|
+
tracker.reset_initializer path
|
230
|
+
|
229
231
|
parse_ruby_files([path]).each do |astfile|
|
230
232
|
process_initializer astfile
|
231
233
|
end
|
234
|
+
|
235
|
+
@reindex << :initializers
|
232
236
|
end
|
233
237
|
|
234
238
|
#Handle rescanning when a file is deleted
|
data/lib/brakeman/tracker.rb
CHANGED
@@ -227,6 +227,10 @@ class Brakeman::Tracker
|
|
227
227
|
finder.process_source template.src, :template => template, :file => template.file
|
228
228
|
end
|
229
229
|
|
230
|
+
self.initializers.each do |file_name, src|
|
231
|
+
finder.process_all_source src, :file => file_name
|
232
|
+
end
|
233
|
+
|
230
234
|
@call_index = Brakeman::CallIndex.new finder.calls
|
231
235
|
end
|
232
236
|
|
@@ -237,8 +241,8 @@ class Brakeman::Tracker
|
|
237
241
|
#
|
238
242
|
#This will limit reindexing to the given sets
|
239
243
|
def reindex_call_sites locations
|
240
|
-
#If reindexing templates, models,
|
241
|
-
#everything
|
244
|
+
#If reindexing templates, models, controllers,
|
245
|
+
#just redo everything.
|
242
246
|
if locations.length == 3
|
243
247
|
return index_call_sites
|
244
248
|
end
|
@@ -260,6 +264,12 @@ class Brakeman::Tracker
|
|
260
264
|
method_sets << self.controllers
|
261
265
|
end
|
262
266
|
|
267
|
+
if locations.include? :initializers
|
268
|
+
self.initializers.each do |file_name, src|
|
269
|
+
@call_index.remove_indexes_by_file file_name
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
263
273
|
@call_index.remove_indexes_by_class classes_to_reindex
|
264
274
|
|
265
275
|
finder = Brakeman::FindAllCalls.new self
|
@@ -279,6 +289,12 @@ class Brakeman::Tracker
|
|
279
289
|
end
|
280
290
|
end
|
281
291
|
|
292
|
+
if locations.include? :initializers
|
293
|
+
self.initializers.each do |file_name, src|
|
294
|
+
finder.process_all_source src, :file => file_name
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
282
298
|
@call_index.index_calls finder.calls
|
283
299
|
end
|
284
300
|
|
@@ -363,4 +379,12 @@ class Brakeman::Tracker
|
|
363
379
|
def reset_routes
|
364
380
|
@routes = {}
|
365
381
|
end
|
382
|
+
|
383
|
+
def reset_initializer path
|
384
|
+
@initializers.delete_if do |file, src|
|
385
|
+
path.relative.include? file
|
386
|
+
end
|
387
|
+
|
388
|
+
@call_index.remove_indexes_by_file path
|
389
|
+
end
|
366
390
|
end
|
@@ -4,10 +4,8 @@ module Brakeman
|
|
4
4
|
class Config
|
5
5
|
include Util
|
6
6
|
|
7
|
-
attr_reader :rails, :tracker
|
8
|
-
attr_accessor :rails_version, :ruby_version
|
7
|
+
attr_reader :gems, :rails, :ruby_version, :tracker
|
9
8
|
attr_writer :erubis, :escape_html
|
10
|
-
attr_reader :gems
|
11
9
|
|
12
10
|
def initialize tracker
|
13
11
|
@tracker = tracker
|
@@ -19,16 +17,9 @@ module Brakeman
|
|
19
17
|
@ruby_version = ""
|
20
18
|
end
|
21
19
|
|
22
|
-
def allow_forgery_protection?
|
23
|
-
@rails[:action_controller] and
|
24
|
-
@rails[:action_controller][:allow_forgery_protection] == Sexp.new(:false)
|
25
|
-
end
|
26
|
-
|
27
20
|
def default_protect_from_forgery?
|
28
|
-
if version_between? "5.2.0", "9.9.9"
|
29
|
-
if @rails
|
30
|
-
@rails[:action_controller][:default_protect_from_forgery] == Sexp.new(:false)
|
31
|
-
|
21
|
+
if version_between? "5.2.0.beta1", "9.9.9"
|
22
|
+
if @rails.dig(:action_controller, :default_protect_from_forgery) == Sexp.new(:false)
|
32
23
|
return false
|
33
24
|
else
|
34
25
|
return true
|
@@ -48,17 +39,21 @@ module Brakeman
|
|
48
39
|
|
49
40
|
def escape_html_entities_in_json?
|
50
41
|
#TODO add version-specific information here
|
51
|
-
@rails
|
52
|
-
|
42
|
+
true? @rails.dig(:active_support, :escape_html_entities_in_json)
|
43
|
+
end
|
44
|
+
|
45
|
+
def escape_filter_interpolations?
|
46
|
+
# TODO see if app is actually turning this off itself
|
47
|
+
has_gem?(:haml) and
|
48
|
+
version_between? "5.0.0", "5.99", gem_version(:haml)
|
53
49
|
end
|
54
50
|
|
55
51
|
def whitelist_attributes?
|
56
|
-
@rails
|
57
|
-
@rails[:active_record][:whitelist_attributes] == Sexp.new(:true)
|
52
|
+
@rails.dig(:active_record, :whitelist_attributes) == Sexp.new(:true)
|
58
53
|
end
|
59
54
|
|
60
55
|
def gem_version name
|
61
|
-
@gems
|
56
|
+
extract_version @gems.dig(name, :version)
|
62
57
|
end
|
63
58
|
|
64
59
|
def add_gem name, version, file, line
|
@@ -78,11 +73,16 @@ module Brakeman
|
|
78
73
|
@gems[name]
|
79
74
|
end
|
80
75
|
|
81
|
-
def set_rails_version
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
76
|
+
def set_rails_version version = nil
|
77
|
+
version = if version
|
78
|
+
# Only used by Rails2ConfigProcessor right now
|
79
|
+
extract_version(version)
|
80
|
+
else
|
81
|
+
gem_version(:rails) || gem_version(:railties)
|
82
|
+
end
|
83
|
+
|
84
|
+
if version
|
85
|
+
@rails_version = version
|
86
86
|
|
87
87
|
if tracker.options[:rails3].nil? and tracker.options[:rails4].nil?
|
88
88
|
if @rails_version.start_with? "3"
|
@@ -113,12 +113,20 @@ module Brakeman
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
+
def rails_version
|
117
|
+
# This needs to be here because Util#rails_version calls Tracker::Config#rails_version
|
118
|
+
# but Tracker::Config includes Util...
|
119
|
+
@rails_version
|
120
|
+
end
|
121
|
+
|
116
122
|
def set_ruby_version version
|
123
|
+
@ruby_version = extract_version(version)
|
124
|
+
end
|
125
|
+
|
126
|
+
def extract_version version
|
117
127
|
return unless version.is_a? String
|
118
128
|
|
119
|
-
|
120
|
-
self.ruby_version = $1
|
121
|
-
end
|
129
|
+
version[/\d+\.\d+(\.\d+.*)?/]
|
122
130
|
end
|
123
131
|
|
124
132
|
#Returns true if low_version <= RAILS_VERSION <= high_version
|
@@ -128,58 +136,15 @@ module Brakeman
|
|
128
136
|
current_version ||= rails_version
|
129
137
|
return false unless current_version
|
130
138
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
version.each_with_index do |v, i|
|
136
|
-
if lower? v, low_version.fetch(i, 0)
|
137
|
-
return false
|
138
|
-
elsif higher? v, low_version.fetch(i, 0)
|
139
|
-
break
|
140
|
-
end
|
141
|
-
end
|
139
|
+
low = Gem::Version.new(low_version)
|
140
|
+
high = Gem::Version.new(high_version)
|
141
|
+
current = Gem::Version.new(current_version)
|
142
142
|
|
143
|
-
|
144
|
-
if higher? v, high_version.fetch(i, 0)
|
145
|
-
return false
|
146
|
-
elsif lower? v, high_version.fetch(i, 0)
|
147
|
-
break
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
true
|
143
|
+
current.between?(low, high)
|
152
144
|
end
|
153
145
|
|
154
146
|
def session_settings
|
155
|
-
@rails
|
156
|
-
@rails[:action_controller][:session]
|
157
|
-
end
|
158
|
-
|
159
|
-
private
|
160
|
-
|
161
|
-
def convert_version_number value
|
162
|
-
if value.match(/\A\d+\z/)
|
163
|
-
value.to_i
|
164
|
-
else
|
165
|
-
value
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def lower? lhs, rhs
|
170
|
-
if lhs.class == rhs.class
|
171
|
-
lhs < rhs
|
172
|
-
else
|
173
|
-
false
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def higher? lhs, rhs
|
178
|
-
if lhs.class == rhs.class
|
179
|
-
lhs > rhs
|
180
|
-
else
|
181
|
-
false
|
182
|
-
end
|
147
|
+
@rails.dig(:action_controller, :session)
|
183
148
|
end
|
184
149
|
end
|
185
150
|
end
|