brakeman 1.9.5 → 2.0.0.pre2
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 +27 -0
- data/README.md +5 -2
- data/bin/brakeman +20 -15
- data/lib/brakeman.rb +106 -80
- data/lib/brakeman/app_tree.rb +22 -11
- data/lib/brakeman/call_index.rb +4 -4
- data/lib/brakeman/checks/base_check.rb +33 -5
- data/lib/brakeman/checks/check_basic_auth.rb +2 -1
- data/lib/brakeman/checks/check_content_tag.rb +8 -29
- data/lib/brakeman/checks/check_cross_site_scripting.rb +10 -19
- data/lib/brakeman/checks/check_deserialize.rb +57 -0
- data/lib/brakeman/checks/check_execute.rb +3 -11
- data/lib/brakeman/checks/check_file_access.rb +1 -14
- data/lib/brakeman/checks/check_forgery_setting.rb +5 -4
- data/lib/brakeman/checks/check_link_to.rb +4 -15
- data/lib/brakeman/checks/check_link_to_href.rb +1 -8
- data/lib/brakeman/checks/check_mass_assignment.rb +6 -2
- data/lib/brakeman/checks/check_model_attributes.rb +1 -0
- data/lib/brakeman/checks/check_model_serialize.rb +2 -1
- data/lib/brakeman/checks/check_render.rb +2 -15
- data/lib/brakeman/checks/check_select_tag.rb +1 -1
- data/lib/brakeman/checks/check_select_vulnerability.rb +2 -2
- data/lib/brakeman/checks/check_send.rb +3 -0
- data/lib/brakeman/checks/check_session_settings.rb +2 -3
- data/lib/brakeman/checks/check_skip_before_filter.rb +5 -2
- data/lib/brakeman/checks/check_sql.rb +59 -50
- data/lib/brakeman/checks/check_symbol_dos.rb +5 -14
- data/lib/brakeman/checks/check_unsafe_reflection.rb +2 -15
- data/lib/brakeman/options.rb +14 -9
- data/lib/brakeman/processors/controller_alias_processor.rb +4 -10
- data/lib/brakeman/processors/controller_processor.rb +53 -14
- data/lib/brakeman/processors/lib/find_all_calls.rb +40 -40
- data/lib/brakeman/processors/lib/processor_helper.rb +5 -1
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -0
- data/lib/brakeman/processors/output_processor.rb +23 -1
- data/lib/brakeman/report.rb +28 -14
- data/lib/brakeman/scanner.rb +5 -3
- data/lib/brakeman/tracker.rb +7 -7
- data/lib/brakeman/util.rb +5 -3
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +12 -9
- data/lib/ruby_parser/bm_sexp.rb +5 -1
- data/lib/tasks/brakeman.rake +10 -0
- metadata +11 -9
- data/lib/brakeman/checks/check_yaml_load.rb +0 -55
@@ -13,10 +13,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
|
|
13
13
|
|
14
14
|
#Process the given source. Provide either class and method being searched
|
15
15
|
#or the template. These names are used when reporting results.
|
16
|
-
def process_source exp,
|
17
|
-
@current_class =
|
18
|
-
@current_method = method
|
19
|
-
@current_template = template
|
16
|
+
def process_source exp, opts
|
17
|
+
@current_class = opts[:class]
|
18
|
+
@current_method = opts[:method]
|
19
|
+
@current_template = opts[:template]
|
20
|
+
@current_file = opts[:file]
|
20
21
|
process exp
|
21
22
|
end
|
22
23
|
|
@@ -48,15 +49,12 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
|
|
48
49
|
method = exp.method
|
49
50
|
process_call_args exp
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
@calls << call
|
52
|
+
@calls << { :target => target,
|
53
|
+
:method => method,
|
54
|
+
:call => exp,
|
55
|
+
:nested => @in_target,
|
56
|
+
:chain => get_chain(exp),
|
57
|
+
:location => make_location }
|
60
58
|
|
61
59
|
exp
|
62
60
|
end
|
@@ -66,15 +64,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
|
|
66
64
|
def process_render exp
|
67
65
|
process exp.last if sexp? exp.last
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
call[:location] = [:class, @current_class, @current_method]
|
75
|
-
end
|
76
|
-
|
77
|
-
@calls << call
|
67
|
+
@calls << { :target => nil,
|
68
|
+
:method => :render,
|
69
|
+
:call => exp,
|
70
|
+
:nested => false,
|
71
|
+
:location => make_location }
|
78
72
|
|
79
73
|
exp
|
80
74
|
end
|
@@ -84,15 +78,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
|
|
84
78
|
def process_dxstr exp
|
85
79
|
process exp.last if sexp? exp.last
|
86
80
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
call[:location] = [:class, @current_class, @current_method]
|
93
|
-
end
|
94
|
-
|
95
|
-
@calls << call
|
81
|
+
@calls << { :target => nil,
|
82
|
+
:method => :`,
|
83
|
+
:call => exp,
|
84
|
+
:nested => false,
|
85
|
+
:location => make_location }
|
96
86
|
|
97
87
|
exp
|
98
88
|
end
|
@@ -101,15 +91,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
|
|
101
91
|
def process_dsym exp
|
102
92
|
exp.each { |arg| process arg if sexp? arg }
|
103
93
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
call[:location] = [:class, @current_class, @current_method]
|
110
|
-
end
|
111
|
-
|
112
|
-
@calls << call
|
94
|
+
@calls << { :target => nil,
|
95
|
+
:method => :literal_to_sym,
|
96
|
+
:call => exp,
|
97
|
+
:nested => false,
|
98
|
+
:location => make_location }
|
113
99
|
|
114
100
|
exp
|
115
101
|
end
|
@@ -155,4 +141,18 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
|
|
155
141
|
[get_target(call)]
|
156
142
|
end
|
157
143
|
end
|
144
|
+
|
145
|
+
def make_location
|
146
|
+
if @current_template
|
147
|
+
{ :type => :template,
|
148
|
+
:template => @current_template,
|
149
|
+
:file => @current_file }
|
150
|
+
else
|
151
|
+
{ :type => :class,
|
152
|
+
:class => @current_class,
|
153
|
+
:method => @current_method,
|
154
|
+
:file => @current_file }
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
158
|
end
|
@@ -72,6 +72,14 @@ class Brakeman::Rails3ConfigProcessor < Brakeman::BaseProcessor
|
|
72
72
|
level = @tracker.config[:rails]
|
73
73
|
options[0..-2].each do |o|
|
74
74
|
level[o] ||= {}
|
75
|
+
|
76
|
+
option = level[o]
|
77
|
+
|
78
|
+
if not option.is_a? Hash
|
79
|
+
Brakeman.debug "[Notice] Skipping config setting: #{options.map(&:to_s).join(".")}"
|
80
|
+
return exp
|
81
|
+
end
|
82
|
+
|
75
83
|
level = level[o]
|
76
84
|
end
|
77
85
|
|
@@ -14,7 +14,6 @@ class Brakeman::OutputProcessor < Ruby2Ruby
|
|
14
14
|
end
|
15
15
|
|
16
16
|
alias process_safely format
|
17
|
-
alias process_methdef process_defn
|
18
17
|
|
19
18
|
def process exp
|
20
19
|
begin
|
@@ -99,6 +98,29 @@ class Brakeman::OutputProcessor < Ruby2Ruby
|
|
99
98
|
out
|
100
99
|
end
|
101
100
|
|
101
|
+
def process_defn exp
|
102
|
+
# Copied from Ruby2Ruby except without the whole
|
103
|
+
# "convert methods to attr_*" stuff
|
104
|
+
name = exp.shift
|
105
|
+
args = process exp.shift
|
106
|
+
args = "" if args == "()"
|
107
|
+
|
108
|
+
exp.shift if exp == s(s(:nil)) # empty it out of a default nil expression
|
109
|
+
|
110
|
+
body = []
|
111
|
+
until exp.empty? do
|
112
|
+
body << indent(process(exp.shift))
|
113
|
+
end
|
114
|
+
|
115
|
+
body << indent("# do nothing") if body.empty?
|
116
|
+
|
117
|
+
body = body.join("\n")
|
118
|
+
|
119
|
+
return "def #{name}#{args}\n#{body}\nend".gsub(/\n\s*\n+/, "\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
alias process_methdef process_defn
|
123
|
+
|
102
124
|
def process_call_with_block exp
|
103
125
|
call = process exp[0]
|
104
126
|
block = process_rlist exp[2..-1]
|
data/lib/brakeman/report.rb
CHANGED
@@ -3,7 +3,7 @@ require 'set'
|
|
3
3
|
require 'brakeman/processors/output_processor'
|
4
4
|
require 'brakeman/util'
|
5
5
|
require 'terminal-table'
|
6
|
-
require 'highline
|
6
|
+
require 'highline'
|
7
7
|
require "csv"
|
8
8
|
require 'multi_json'
|
9
9
|
require 'brakeman/version'
|
@@ -283,6 +283,26 @@ class Brakeman::Report
|
|
283
283
|
end
|
284
284
|
end
|
285
285
|
|
286
|
+
# format output from filename or format
|
287
|
+
def format(filename_or_format)
|
288
|
+
case filename_or_format
|
289
|
+
when /\.html/, :to_html
|
290
|
+
to_html
|
291
|
+
when /\.pdf/, :to_pdf
|
292
|
+
to_pdf
|
293
|
+
when /\.csv/, :to_csv
|
294
|
+
to_csv
|
295
|
+
when /\.json/, :to_json
|
296
|
+
to_json
|
297
|
+
when /\.tabs/, :to_tabs
|
298
|
+
to_tabs
|
299
|
+
when :to_test
|
300
|
+
to_test
|
301
|
+
else
|
302
|
+
to_s
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
286
306
|
#Generate HTML output
|
287
307
|
def to_html
|
288
308
|
out = html_header <<
|
@@ -504,7 +524,7 @@ HEADER
|
|
504
524
|
message
|
505
525
|
end <<
|
506
526
|
"<table id='#{code_id}' class='context' style='display:none'>" <<
|
507
|
-
"<caption>#{warning_file(warning
|
527
|
+
"<caption>#{warning_file(warning) || ''}</caption>"
|
508
528
|
|
509
529
|
unless context.empty?
|
510
530
|
if warning.line - 1 == 1 or warning.line + 1 == 1
|
@@ -567,7 +587,7 @@ HEADER
|
|
567
587
|
checks.send(meth).map do |w|
|
568
588
|
line = w.line || 0
|
569
589
|
w.warning_type.gsub!(/[^\w\s]/, ' ')
|
570
|
-
"#{warning_file
|
590
|
+
"#{warning_file(w, :absolute)}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
|
571
591
|
end.join "\n"
|
572
592
|
|
573
593
|
end.join "\n"
|
@@ -584,11 +604,6 @@ HEADER
|
|
584
604
|
report[meth] = @checks.send(meth)
|
585
605
|
report[meth].each do |w|
|
586
606
|
w.message = w.format_message
|
587
|
-
if w.code
|
588
|
-
w.code = w.format_code
|
589
|
-
else
|
590
|
-
w.code = ""
|
591
|
-
end
|
592
607
|
w.context = context_for(@app_tree, w).join("\n")
|
593
608
|
end
|
594
609
|
end
|
@@ -614,10 +629,9 @@ HEADER
|
|
614
629
|
:security_warnings => all_warnings.length,
|
615
630
|
:start_time => tracker.start_time.to_s,
|
616
631
|
:end_time => tracker.end_time.to_s,
|
617
|
-
:timestamp => tracker.end_time.to_s,
|
618
632
|
:duration => tracker.duration,
|
619
633
|
:checks_performed => checks.checks_run.sort,
|
620
|
-
:number_of_controllers =>tracker.controllers.length,
|
634
|
+
:number_of_controllers => tracker.controllers.length,
|
621
635
|
# ignore the "fake" model
|
622
636
|
:number_of_models => tracker.models.length - 1,
|
623
637
|
:number_of_templates => number_of_templates(@tracker),
|
@@ -642,13 +656,13 @@ HEADER
|
|
642
656
|
Set.new(tracker.templates.map {|k,v| v[:name].to_s[/[^.]+/]}).length
|
643
657
|
end
|
644
658
|
|
645
|
-
def warning_file warning,
|
659
|
+
def warning_file warning, absolute = @tracker.options[:absolute_paths]
|
646
660
|
return nil if warning.file.nil?
|
647
661
|
|
648
|
-
if
|
649
|
-
relative_path warning.file
|
650
|
-
else
|
662
|
+
if absolute
|
651
663
|
warning.file
|
664
|
+
else
|
665
|
+
relative_path warning.file
|
652
666
|
end
|
653
667
|
end
|
654
668
|
|
data/lib/brakeman/scanner.rb
CHANGED
@@ -33,7 +33,7 @@ class Brakeman::Scanner
|
|
33
33
|
@app_tree = Brakeman::AppTree.from_options(options)
|
34
34
|
|
35
35
|
if !@app_tree.root || !@app_tree.exists?("app")
|
36
|
-
|
36
|
+
raise NoApplication, "Please supply the path to a Rails application."
|
37
37
|
end
|
38
38
|
|
39
39
|
if @app_tree.exists?("script/rails")
|
@@ -104,8 +104,8 @@ class Brakeman::Scanner
|
|
104
104
|
end
|
105
105
|
|
106
106
|
rescue Exception => e
|
107
|
-
Brakeman.notify "[Notice] Error while processing
|
108
|
-
tracker.error e.exception(e.message + "\nwhile processing
|
107
|
+
Brakeman.notify "[Notice] Error while processing #{path}"
|
108
|
+
tracker.error e.exception(e.message + "\nwhile processing #{path}"), e.backtrace
|
109
109
|
end
|
110
110
|
|
111
111
|
private :process_config_file
|
@@ -355,4 +355,6 @@ class Brakeman::Scanner
|
|
355
355
|
def parse_ruby input
|
356
356
|
@ruby_parser.new.parse input
|
357
357
|
end
|
358
|
+
|
359
|
+
class NoApplication < RuntimeError; end
|
358
360
|
end
|
data/lib/brakeman/tracker.rb
CHANGED
@@ -25,7 +25,7 @@ class Brakeman::Tracker
|
|
25
25
|
@processor = processor
|
26
26
|
@options = options
|
27
27
|
|
28
|
-
@config = {}
|
28
|
+
@config = { :rails => {} }
|
29
29
|
@templates = {}
|
30
30
|
@controllers = {}
|
31
31
|
#Initialize models with the unknown model so
|
@@ -86,7 +86,7 @@ class Brakeman::Tracker
|
|
86
86
|
method_name = "#{definition[1]}.#{method_name}"
|
87
87
|
end
|
88
88
|
|
89
|
-
yield definition, set_name, method_name
|
89
|
+
yield definition, set_name, method_name, info[:file]
|
90
90
|
|
91
91
|
end
|
92
92
|
end
|
@@ -155,12 +155,12 @@ class Brakeman::Tracker
|
|
155
155
|
def index_call_sites
|
156
156
|
finder = Brakeman::FindAllCalls.new self
|
157
157
|
|
158
|
-
self.each_method do |definition, set_name, method_name|
|
159
|
-
finder.process_source definition, set_name, method_name
|
158
|
+
self.each_method do |definition, set_name, method_name, file|
|
159
|
+
finder.process_source definition, :class => set_name, :method => method_name, :file => file
|
160
160
|
end
|
161
161
|
|
162
162
|
self.each_template do |name, template|
|
163
|
-
finder.process_source template[:src],
|
163
|
+
finder.process_source template[:src], :template => template, :file => template[:file]
|
164
164
|
end
|
165
165
|
|
166
166
|
@call_index = Brakeman::CallIndex.new finder.calls
|
@@ -208,7 +208,7 @@ class Brakeman::Tracker
|
|
208
208
|
method_name = "#{definition[1]}.#{method_name}"
|
209
209
|
end
|
210
210
|
|
211
|
-
finder.process_source definition, set_name, method_name
|
211
|
+
finder.process_source definition, :class => set_name, :method => method_name, :file => info[:file]
|
212
212
|
|
213
213
|
end
|
214
214
|
end
|
@@ -217,7 +217,7 @@ class Brakeman::Tracker
|
|
217
217
|
|
218
218
|
if locations.include? :templates
|
219
219
|
self.each_template do |name, template|
|
220
|
-
finder.process_source template[:src],
|
220
|
+
finder.process_source template[:src], :template => template, :file => template[:file]
|
221
221
|
end
|
222
222
|
end
|
223
223
|
|
data/lib/brakeman/util.rb
CHANGED
@@ -275,6 +275,8 @@ module Brakeman::Util
|
|
275
275
|
|
276
276
|
if warning.file
|
277
277
|
File.expand_path warning.file, tracker.options[:app_path]
|
278
|
+
elsif warning.template.is_a? Hash and warning.template[:file]
|
279
|
+
warning.template[:file]
|
278
280
|
else
|
279
281
|
case warning.warning_set
|
280
282
|
when :controller
|
@@ -305,7 +307,7 @@ module Brakeman::Util
|
|
305
307
|
unless type
|
306
308
|
if string_name =~ /Controller$/
|
307
309
|
type = :controller
|
308
|
-
elsif camelize(string_name) == string_name
|
310
|
+
elsif camelize(string_name) == string_name # This is not always true
|
309
311
|
type = :model
|
310
312
|
else
|
311
313
|
type = :template
|
@@ -325,7 +327,7 @@ module Brakeman::Util
|
|
325
327
|
if tracker.models[name] and tracker.models[name][:file]
|
326
328
|
path = tracker.models[name][:file]
|
327
329
|
else
|
328
|
-
path += "/app/
|
330
|
+
path += "/app/models/#{underscore(string_name)}.rb"
|
329
331
|
end
|
330
332
|
when :template
|
331
333
|
if tracker.templates[name] and tracker.templates[name][:file]
|
@@ -383,7 +385,7 @@ module Brakeman::Util
|
|
383
385
|
|
384
386
|
def truncate_table str
|
385
387
|
@terminal_width ||= if $stdin && $stdin.tty?
|
386
|
-
::HighLine
|
388
|
+
::HighLine.new.terminal_size[0]
|
387
389
|
else
|
388
390
|
80
|
389
391
|
end
|
data/lib/brakeman/version.rb
CHANGED
data/lib/brakeman/warning.rb
CHANGED
@@ -5,9 +5,10 @@ require 'brakeman/warning_codes'
|
|
5
5
|
#The Warning class stores information about warnings
|
6
6
|
class Brakeman::Warning
|
7
7
|
attr_reader :called_from, :check, :class, :confidence, :controller,
|
8
|
-
:line, :method, :model, :template, :user_input, :
|
8
|
+
:line, :method, :model, :template, :user_input, :warning_code, :warning_set,
|
9
|
+
:warning_type
|
9
10
|
|
10
|
-
attr_accessor :code, :context, :file, :message
|
11
|
+
attr_accessor :code, :context, :file, :message, :relative_path
|
11
12
|
|
12
13
|
TEXT_CONFIDENCE = [ "High", "Medium", "Weak" ]
|
13
14
|
|
@@ -23,13 +24,14 @@ class Brakeman::Warning
|
|
23
24
|
|
24
25
|
result = options[:result]
|
25
26
|
if result
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
@code ||= result[:call]
|
28
|
+
@file ||= result[:location][:file]
|
29
|
+
|
30
|
+
if result[:location][:type] == :template #template result
|
31
|
+
@template ||= result[:location][:template]
|
29
32
|
else
|
30
|
-
@class ||= result[:location][
|
31
|
-
@method ||= result[:location][
|
32
|
-
@code ||= result[:call]
|
33
|
+
@class ||= result[:location][:class]
|
34
|
+
@method ||= result[:location][:method]
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -160,9 +162,10 @@ class Brakeman::Warning
|
|
160
162
|
|
161
163
|
def fingerprint
|
162
164
|
loc = self.location
|
163
|
-
location_string = loc && loc.sort_by { |k, v| k.to_s }.
|
165
|
+
location_string = loc && loc.sort_by { |k, v| k.to_s }.inspect
|
164
166
|
warning_code_string = sprintf("%03d", @warning_code)
|
165
167
|
code_string = @code.inspect
|
168
|
+
|
166
169
|
Digest::SHA2.new(256).update("#{warning_code_string}#{code_string}#{location_string}#{@relative_path}#{self.confidence}").to_s
|
167
170
|
end
|
168
171
|
|
data/lib/ruby_parser/bm_sexp.rb
CHANGED
@@ -371,7 +371,11 @@ class Sexp
|
|
371
371
|
# s(:call, nil, :p, s(:arglist, s(:lvar, :y))))
|
372
372
|
def block_args
|
373
373
|
expect :iter, :call_with_block
|
374
|
-
self[2]
|
374
|
+
if self[2] == 0 # ?! See https://github.com/presidentbeef/brakeman/issues/331
|
375
|
+
return Sexp.new(:args)
|
376
|
+
else
|
377
|
+
self[2]
|
378
|
+
end
|
375
379
|
end
|
376
380
|
|
377
381
|
def first_param
|