brakeman-min 3.1.5 → 3.2.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +11 -0
- data/lib/brakeman.rb +4 -4
- data/lib/brakeman/call_index.rb +22 -31
- data/lib/brakeman/checks.rb +59 -73
- data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +0 -21
- data/lib/brakeman/checks/check_cross_site_scripting.rb +2 -2
- data/lib/brakeman/checks/check_render.rb +9 -1
- data/lib/brakeman/checks/check_sql.rb +3 -3
- data/lib/brakeman/processors/alias_processor.rb +1 -1
- data/lib/brakeman/processors/base_processor.rb +8 -0
- data/lib/brakeman/processors/erb_template_processor.rb +1 -1
- data/lib/brakeman/processors/erubis_template_processor.rb +1 -1
- data/lib/brakeman/processors/haml_template_processor.rb +2 -1
- data/lib/brakeman/processors/lib/basic_processor.rb +16 -0
- data/lib/brakeman/processors/lib/find_all_calls.rb +4 -2
- data/lib/brakeman/processors/lib/find_call.rb +1 -1
- data/lib/brakeman/processors/lib/render_path.rb +2 -1
- data/lib/brakeman/report/ignore/config.rb +3 -3
- data/lib/brakeman/report/report_csv.rb +1 -2
- data/lib/brakeman/report/report_json.rb +1 -4
- data/lib/brakeman/tracker.rb +21 -0
- data/lib/brakeman/util.rb +4 -3
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +2 -2
- data/lib/ruby_parser/bm_sexp.rb +23 -23
- metadata +8 -30
- data/lib/brakeman/report/initializers/faster_csv.rb +0 -7
- data/lib/brakeman/report/initializers/multi_json.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1db53f4a7deaf2e3b3cade6acd5cac5e88f285f
|
4
|
+
data.tar.gz: 274429ac40d7afbdd8ac1d9caffbc5a3b5ebcdcb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e721a649d7dbc8d8dab8690ebe1dc166ba441610b85364c324a1caeaa6d49f45cc09d2329a37000d2c09bfa8de38cd5377a34a0869d4743073fd0fd53f8cbc45
|
7
|
+
data.tar.gz: 85a9eb5190847ebb9135e0496c69a103e30fdbbdf166e0d996c37ada9223578f69ab5f3151ad0e23d900ab9621dc5873bb130a643e41d33d759c0a4efbc09b27
|
data/CHANGES
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 3.2.0.pre1
|
2
|
+
|
3
|
+
* Support calls using `&.` operator
|
4
|
+
* Update ruby_parser dependency to 3.8.1
|
5
|
+
* Remove `fastercsv` dependency
|
6
|
+
* Fix finding calls with `targets: nil`
|
7
|
+
* Remove `multi-json` dependecy
|
8
|
+
* Handle CoffeeScript in HAML
|
9
|
+
* Avoid render warnings about params[:action]/params[:controller]
|
10
|
+
* Index calls in class bodies but outside methods
|
11
|
+
|
1
12
|
# 3.1.5
|
2
13
|
|
3
14
|
* Fix CodeClimate construction of --only-files (Will Fleming)
|
data/lib/brakeman.rb
CHANGED
@@ -407,20 +407,20 @@ module Brakeman
|
|
407
407
|
|
408
408
|
# Compare JSON ouptut from a previous scan and return the diff of the two scans
|
409
409
|
def self.compare options
|
410
|
-
require '
|
410
|
+
require 'json'
|
411
411
|
require 'brakeman/differ'
|
412
412
|
raise ArgumentError.new("Comparison file doesn't exist") unless File.exist? options[:previous_results_json]
|
413
413
|
|
414
414
|
begin
|
415
|
-
previous_results =
|
416
|
-
rescue
|
415
|
+
previous_results = JSON.parse(File.read(options[:previous_results_json]), :symbolize_names => true)[:warnings]
|
416
|
+
rescue JSON::ParserError
|
417
417
|
self.notify "Error parsing comparison file: #{options[:previous_results_json]}"
|
418
418
|
exit!
|
419
419
|
end
|
420
420
|
|
421
421
|
tracker = run(options)
|
422
422
|
|
423
|
-
new_results =
|
423
|
+
new_results = JSON.parse(tracker.report.to_json, :symbolize_names => true)[:warnings]
|
424
424
|
|
425
425
|
Brakeman::Differ.new(new_results, previous_results).diff
|
426
426
|
end
|
data/lib/brakeman/call_index.rb
CHANGED
@@ -5,8 +5,8 @@ class Brakeman::CallIndex
|
|
5
5
|
|
6
6
|
#Initialize index with calls from FindAllCalls
|
7
7
|
def initialize calls
|
8
|
-
@calls_by_method = Hash.new
|
9
|
-
@calls_by_target = Hash.new
|
8
|
+
@calls_by_method = Hash.new { |h, k| h[k] = [] }
|
9
|
+
@calls_by_target = Hash.new { |h, k| h[k] = [] }
|
10
10
|
|
11
11
|
index_calls calls
|
12
12
|
end
|
@@ -45,7 +45,7 @@ class Brakeman::CallIndex
|
|
45
45
|
|
46
46
|
#Find calls with no explicit target
|
47
47
|
#with either :target => nil or :target => false
|
48
|
-
elsif options.key? :target and not target and method
|
48
|
+
elsif (options.key? :target or options.key? :targets) and not target and method
|
49
49
|
calls = calls_by_method method
|
50
50
|
calls = filter_by_target calls, nil
|
51
51
|
|
@@ -66,44 +66,35 @@ class Brakeman::CallIndex
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def remove_template_indexes template_name = nil
|
69
|
-
@calls_by_method.each do |
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@calls_by_target.each do |name, calls|
|
76
|
-
calls.delete_if do |call|
|
77
|
-
from_template call, template_name
|
69
|
+
[@calls_by_method, @calls_by_target].each do |calls_by|
|
70
|
+
calls_by.each do |name, calls|
|
71
|
+
calls.delete_if do |call|
|
72
|
+
from_template call, template_name
|
73
|
+
end
|
78
74
|
end
|
79
75
|
end
|
80
76
|
end
|
81
77
|
|
82
78
|
def remove_indexes_by_class classes
|
83
|
-
@calls_by_method.each do |
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@calls_by_target.each do |name, calls|
|
90
|
-
calls.delete_if do |call|
|
91
|
-
call[:location][:type] == :class and classes.include? call[:location][:class]
|
79
|
+
[@calls_by_method, @calls_by_target].each do |calls_by|
|
80
|
+
calls_by.each do |name, calls|
|
81
|
+
calls.delete_if do |call|
|
82
|
+
call[:location][:type] == :class and classes.include? call[:location][:class]
|
83
|
+
end
|
92
84
|
end
|
93
85
|
end
|
94
86
|
end
|
95
87
|
|
96
88
|
def index_calls calls
|
97
89
|
calls.each do |call|
|
98
|
-
@calls_by_method[call[:method]] ||= []
|
99
90
|
@calls_by_method[call[:method]] << call
|
100
91
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@calls_by_target[
|
92
|
+
target = call[:target]
|
93
|
+
|
94
|
+
if not target.is_a? Sexp
|
95
|
+
@calls_by_target[target] << call
|
96
|
+
elsif target.node_type == :params or target.node_type == :session
|
97
|
+
@calls_by_target[target.node_type] << call
|
107
98
|
end
|
108
99
|
end
|
109
100
|
end
|
@@ -115,7 +106,7 @@ class Brakeman::CallIndex
|
|
115
106
|
method = options[:method] || options[:methods]
|
116
107
|
|
117
108
|
calls = calls_by_method method
|
118
|
-
|
109
|
+
|
119
110
|
return [] if calls.nil?
|
120
111
|
|
121
112
|
calls = filter_by_chain calls, target
|
@@ -145,7 +136,7 @@ class Brakeman::CallIndex
|
|
145
136
|
elsif method.is_a? Regexp
|
146
137
|
calls_by_methods_regex method
|
147
138
|
else
|
148
|
-
@calls_by_method[method.to_sym]
|
139
|
+
@calls_by_method[method.to_sym]
|
149
140
|
end
|
150
141
|
end
|
151
142
|
|
@@ -169,7 +160,7 @@ class Brakeman::CallIndex
|
|
169
160
|
end
|
170
161
|
|
171
162
|
def calls_with_no_target
|
172
|
-
@calls_by_target[nil]
|
163
|
+
@calls_by_target[nil]
|
173
164
|
end
|
174
165
|
|
175
166
|
def filter calls, key, value
|
data/lib/brakeman/checks.rb
CHANGED
@@ -93,91 +93,49 @@ class Brakeman::Checks
|
|
93
93
|
#Run all the checks on the given Tracker.
|
94
94
|
#Returns a new instance of Checks with the results.
|
95
95
|
def self.run_checks(app_tree, tracker)
|
96
|
-
|
97
|
-
self.run_checks_parallel(app_tree, tracker)
|
98
|
-
else
|
99
|
-
self.run_checks_sequential(app_tree, tracker)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
#Run checks sequentially
|
104
|
-
def self.run_checks_sequential(app_tree, tracker)
|
96
|
+
checks = self.checks_to_run(tracker)
|
105
97
|
check_runner = self.new :min_confidence => tracker.options[:min_confidence]
|
106
|
-
|
107
|
-
self.checks_to_run(tracker).each do |c|
|
108
|
-
check_name = get_check_name c
|
109
|
-
|
110
|
-
#Run or don't run check based on options
|
111
|
-
unless tracker.options[:skip_checks].include? check_name or
|
112
|
-
(tracker.options[:run_checks] and not tracker.options[:run_checks].include? check_name)
|
113
|
-
|
114
|
-
Brakeman.notify " - #{check_name}"
|
115
|
-
|
116
|
-
check = c.new(app_tree, tracker)
|
117
|
-
|
118
|
-
begin
|
119
|
-
check.run_check
|
120
|
-
rescue => e
|
121
|
-
tracker.error e
|
122
|
-
end
|
123
|
-
|
124
|
-
check.warnings.each do |w|
|
125
|
-
check_runner.add_warning w
|
126
|
-
end
|
127
|
-
|
128
|
-
#Maintain list of which checks were run
|
129
|
-
#mainly for reporting purposes
|
130
|
-
check_runner.checks_run << check_name[5..-1]
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
check_runner
|
98
|
+
self.actually_run_checks(checks, check_runner, app_tree, tracker)
|
135
99
|
end
|
136
100
|
|
137
|
-
|
138
|
-
|
139
|
-
|
101
|
+
def self.actually_run_checks(checks, check_runner, app_tree, tracker)
|
102
|
+
threads = [] # Results for parallel
|
103
|
+
results = [] # Results for sequential
|
104
|
+
parallel = tracker.options[:parallel_checks]
|
140
105
|
error_mutex = Mutex.new
|
141
106
|
|
142
|
-
|
143
|
-
|
144
|
-
self.checks_to_run(tracker).each do |c|
|
107
|
+
checks.each do |c|
|
145
108
|
check_name = get_check_name c
|
109
|
+
Brakeman.notify " - #{check_name}"
|
146
110
|
|
147
|
-
|
148
|
-
unless tracker.options[:skip_checks].include? check_name or
|
149
|
-
(tracker.options[:run_checks] and not tracker.options[:run_checks].include? check_name)
|
150
|
-
|
151
|
-
Brakeman.notify " - #{check_name}"
|
152
|
-
|
111
|
+
if parallel
|
153
112
|
threads << Thread.new do
|
154
|
-
|
155
|
-
|
156
|
-
begin
|
157
|
-
check.run_check
|
158
|
-
rescue => e
|
159
|
-
error_mutex.synchronize do
|
160
|
-
tracker.error e
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
check.warnings
|
113
|
+
self.run_a_check(c, error_mutex, app_tree, tracker)
|
165
114
|
end
|
166
|
-
|
167
|
-
|
168
|
-
#mainly for reporting purposes
|
169
|
-
check_runner.checks_run << check_name[5..-1]
|
115
|
+
else
|
116
|
+
results << self.run_a_check(c, error_mutex, app_tree, tracker)
|
170
117
|
end
|
118
|
+
|
119
|
+
#Maintain list of which checks were run
|
120
|
+
#mainly for reporting purposes
|
121
|
+
check_runner.checks_run << check_name[5..-1]
|
171
122
|
end
|
172
123
|
|
173
124
|
threads.each { |t| t.join }
|
174
125
|
|
175
126
|
Brakeman.notify "Checks finished, collecting results..."
|
176
127
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
128
|
+
if parallel
|
129
|
+
threads.each do |thread|
|
130
|
+
thread.value.each do |warning|
|
131
|
+
check_runner.add_warning warning
|
132
|
+
end
|
133
|
+
end
|
134
|
+
else
|
135
|
+
results.each do |warnings|
|
136
|
+
warnings.each do |warning|
|
137
|
+
check_runner.add_warning warning
|
138
|
+
end
|
181
139
|
end
|
182
140
|
end
|
183
141
|
|
@@ -191,11 +149,39 @@ class Brakeman::Checks
|
|
191
149
|
end
|
192
150
|
|
193
151
|
def self.checks_to_run tracker
|
194
|
-
if tracker.options[:run_all_checks] or tracker.options[:run_checks]
|
195
|
-
|
196
|
-
|
197
|
-
|
152
|
+
to_run = if tracker.options[:run_all_checks] or tracker.options[:run_checks]
|
153
|
+
@checks + @optional_checks
|
154
|
+
else
|
155
|
+
@checks
|
156
|
+
end
|
157
|
+
|
158
|
+
self.filter_checks to_run, tracker
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.filter_checks checks, tracker
|
162
|
+
skipped = tracker.options[:skip_checks]
|
163
|
+
explicit = tracker.options[:run_checks]
|
164
|
+
|
165
|
+
checks.reject do |c|
|
166
|
+
check_name = self.get_check_name(c)
|
167
|
+
|
168
|
+
skipped.include? check_name or
|
169
|
+
(explicit and not explicit.include? check_name)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.run_a_check klass, mutex, app_tree, tracker
|
174
|
+
check = klass.new(app_tree, tracker)
|
175
|
+
|
176
|
+
begin
|
177
|
+
check.run_check
|
178
|
+
rescue => e
|
179
|
+
mutex.synchronize do
|
180
|
+
tracker.error e
|
181
|
+
end
|
198
182
|
end
|
183
|
+
|
184
|
+
check.warnings
|
199
185
|
end
|
200
186
|
end
|
201
187
|
|
@@ -17,31 +17,10 @@ class Brakeman::CheckBasicAuthTimingAttack < Brakeman::BaseCheck
|
|
17
17
|
return
|
18
18
|
end
|
19
19
|
|
20
|
-
check_basic_auth_filter
|
21
20
|
check_basic_auth_call
|
22
21
|
end
|
23
22
|
|
24
|
-
def check_basic_auth_filter
|
25
|
-
controllers = tracker.controllers.select do |name, c|
|
26
|
-
c.options[:http_basic_authenticate_with]
|
27
|
-
end
|
28
|
-
|
29
|
-
Hash[controllers].each do |name, controller|
|
30
|
-
controller.options[:http_basic_authenticate_with].each do |call|
|
31
|
-
warn :controller => name,
|
32
|
-
:warning_type => "Timing Attack",
|
33
|
-
:warning_code => :CVE_2015_7576,
|
34
|
-
:message => "Basic authentication in Rails #{rails_version} is vulnerable to timing attacks. Upgrade to #@upgrade",
|
35
|
-
:code => call,
|
36
|
-
:confidence => CONFIDENCE[:high],
|
37
|
-
:file => controller.file,
|
38
|
-
:link => "https://groups.google.com/d/msg/rubyonrails-security/ANv0HDHEC3k/mt7wNGxbFQAJ"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
23
|
def check_basic_auth_call
|
44
|
-
# This is relatively unusual, but found in the wild
|
45
24
|
tracker.find_call(target: nil, method: :http_basic_authenticate_with).each do |result|
|
46
25
|
warn :result => result,
|
47
26
|
:warning_type => "Timing Attack",
|
@@ -99,7 +99,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
|
|
99
99
|
link_path = "cross_site_scripting"
|
100
100
|
warning_code = :cross_site_scripting
|
101
101
|
|
102
|
-
if node_type?(out, :call, :attrasgn) && out.method == :to_json
|
102
|
+
if node_type?(out, :call, :safe_call, :attrasgn, :safe_attrasgn) && out.method == :to_json
|
103
103
|
message += " in JSON hash"
|
104
104
|
link_path += "_to_json"
|
105
105
|
warning_code = :xss_to_json
|
@@ -334,7 +334,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
|
|
334
334
|
end
|
335
335
|
|
336
336
|
def html_safe_call? exp
|
337
|
-
exp.value
|
337
|
+
call? exp.value and exp.value.method == :html_safe
|
338
338
|
end
|
339
339
|
|
340
340
|
def ignore_call? target, method
|
@@ -35,7 +35,6 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
35
35
|
if sexp? view and not duplicate? result
|
36
36
|
add_result result
|
37
37
|
|
38
|
-
|
39
38
|
if input = has_immediate_user_input?(view)
|
40
39
|
if string_interp? view
|
41
40
|
confidence = CONFIDENCE[:med]
|
@@ -49,6 +48,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
49
48
|
end
|
50
49
|
|
51
50
|
return if input.type == :model #skip models
|
51
|
+
return if safe_param? input.match
|
52
52
|
|
53
53
|
message = "Render path contains #{friendly_type_of input}"
|
54
54
|
|
@@ -71,6 +71,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
71
71
|
if sexp? view and not duplicate? result
|
72
72
|
if params? view
|
73
73
|
add_result result
|
74
|
+
return if safe_param? view
|
74
75
|
|
75
76
|
warn :result => result,
|
76
77
|
:warning_type => "Remote Code Execution",
|
@@ -81,4 +82,11 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
81
82
|
end
|
82
83
|
end
|
83
84
|
end
|
85
|
+
|
86
|
+
def safe_param? exp
|
87
|
+
if params? exp and call? exp and exp.method == :[]
|
88
|
+
arg = exp.first_arg
|
89
|
+
symbol? arg and [:controller, :action].include? arg.value
|
90
|
+
end
|
91
|
+
end
|
84
92
|
end
|
@@ -64,9 +64,9 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
64
64
|
second_arg = args[2]
|
65
65
|
next unless sexp? second_arg
|
66
66
|
|
67
|
-
if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call
|
67
|
+
if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call, :safe_call
|
68
68
|
process_scope_with_block(name, args)
|
69
|
-
elsif second_arg
|
69
|
+
elsif call? second_arg
|
70
70
|
call = second_arg
|
71
71
|
scope_calls << scope_call_hash(call, name, call.method)
|
72
72
|
else
|
@@ -107,7 +107,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
107
107
|
find_calls = Brakeman::FindAllCalls.new(tracker)
|
108
108
|
find_calls.process_source(block, :class => model_name, :method => scope_name)
|
109
109
|
find_calls.calls.each { |call| process_result(call) if @sql_targets.include?(call[:method]) }
|
110
|
-
elsif block
|
110
|
+
elsif call? block
|
111
111
|
while call? block
|
112
112
|
process_result :target => block.target, :method => block.method, :call => block,
|
113
113
|
:location => { :type => :class, :class => model_name, :method => scope_name }
|
@@ -296,7 +296,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
296
296
|
|
297
297
|
# Handles x = y = z = 1
|
298
298
|
def get_rhs exp
|
299
|
-
if node_type? exp, :lasgn, :iasgn, :gasgn, :attrasgn, :cvdecl, :cdecl
|
299
|
+
if node_type? exp, :lasgn, :iasgn, :gasgn, :attrasgn, :safe_attrasgn, :cvdecl, :cdecl
|
300
300
|
get_rhs(exp.rhs)
|
301
301
|
else
|
302
302
|
exp
|
@@ -68,6 +68,14 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
68
68
|
call
|
69
69
|
end
|
70
70
|
|
71
|
+
def process_safe_call exp
|
72
|
+
if self.respond_to? :process_call
|
73
|
+
process_call exp
|
74
|
+
else
|
75
|
+
process_default exp
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
71
79
|
#String with interpolation.
|
72
80
|
def process_dstr exp
|
73
81
|
exp = exp.dup
|
@@ -25,7 +25,7 @@ class Brakeman::ErbTemplateProcessor < Brakeman::TemplateProcessor
|
|
25
25
|
|
26
26
|
arg = exp.first_arg
|
27
27
|
|
28
|
-
if arg
|
28
|
+
if call? arg and arg.method == :to_s #erb always calls to_s on output
|
29
29
|
arg = arg.target
|
30
30
|
end
|
31
31
|
|
@@ -21,7 +21,7 @@ class Brakeman::ErubisTemplateProcessor < Brakeman::TemplateProcessor
|
|
21
21
|
arg = exp.first_arg
|
22
22
|
|
23
23
|
#We want the actual content
|
24
|
-
if arg
|
24
|
+
if call? arg and (arg.method == :to_s or arg.method == :html_safe!)
|
25
25
|
arg = arg.target
|
26
26
|
end
|
27
27
|
|
@@ -5,6 +5,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
|
|
5
5
|
HAML_FORMAT_METHOD = /format_script_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)/
|
6
6
|
HAML_HELPERS = s(:colon2, s(:const, :Haml), :Helpers)
|
7
7
|
JAVASCRIPT_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Javascript)
|
8
|
+
COFFEE_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Coffee)
|
8
9
|
|
9
10
|
#Processes call, looking for template output
|
10
11
|
def process_call exp
|
@@ -90,7 +91,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
|
|
90
91
|
elsif target == nil and method == :find_and_preserve
|
91
92
|
process exp.first_arg
|
92
93
|
elsif method == :render_with_options
|
93
|
-
if target == JAVASCRIPT_FILTER
|
94
|
+
if target == JAVASCRIPT_FILTER or target == COFFEE_FILTER
|
94
95
|
@javascript = true
|
95
96
|
end
|
96
97
|
|
@@ -14,4 +14,20 @@ class Brakeman::BasicProcessor < Brakeman::SexpProcessor
|
|
14
14
|
def process_default exp
|
15
15
|
process_all exp
|
16
16
|
end
|
17
|
+
|
18
|
+
def process_safe_call exp
|
19
|
+
if self.respond_to? :process_call
|
20
|
+
process_call exp
|
21
|
+
else
|
22
|
+
process_default exp
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_safe_attrasgn exp
|
27
|
+
if self.respond_to? :process_attrasgn
|
28
|
+
process_attrasgn exp
|
29
|
+
else
|
30
|
+
process_default exp
|
31
|
+
end
|
32
|
+
end
|
17
33
|
end
|
@@ -24,11 +24,13 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
24
24
|
|
25
25
|
#Process body of method
|
26
26
|
def process_defn exp
|
27
|
+
return exp unless @current_method
|
27
28
|
process_all exp.body
|
28
29
|
end
|
29
30
|
|
30
31
|
#Process body of method
|
31
32
|
def process_defs exp
|
33
|
+
return exp unless @current_method
|
32
34
|
process_all exp.body
|
33
35
|
end
|
34
36
|
|
@@ -139,7 +141,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
139
141
|
@current_class || @current_module || nil
|
140
142
|
when :params, :session, :cookies
|
141
143
|
exp.node_type
|
142
|
-
when :call
|
144
|
+
when :call, :safe_call
|
143
145
|
if include_calls
|
144
146
|
if exp.target.nil?
|
145
147
|
exp.method
|
@@ -165,7 +167,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
165
167
|
#Returns method chain as an array
|
166
168
|
#For example, User.human.alive.all would return [:User, :human, :alive, :all]
|
167
169
|
def get_chain call
|
168
|
-
if node_type? call, :call, :attrasgn
|
170
|
+
if node_type? call, :call, :attrasgn, :safe_call, :safe_attrasgn
|
169
171
|
get_chain(call.target) + [call.method]
|
170
172
|
elsif call.nil?
|
171
173
|
[]
|
@@ -102,7 +102,7 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
|
|
102
102
|
# User.find(:first, :conditions => "user = '#{params['user']}').name
|
103
103
|
#
|
104
104
|
#A search for User.find will not match this unless @in_depth is true.
|
105
|
-
if @in_depth and
|
105
|
+
if @in_depth and call? exp.target
|
106
106
|
process exp.target
|
107
107
|
end
|
108
108
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'set'
|
2
|
-
require '
|
2
|
+
require 'json'
|
3
3
|
|
4
4
|
module Brakeman
|
5
5
|
class IgnoreConfig
|
@@ -75,7 +75,7 @@ module Brakeman
|
|
75
75
|
# Read configuration to file
|
76
76
|
def read_from_file file = @file
|
77
77
|
if File.exist? file
|
78
|
-
@already_ignored =
|
78
|
+
@already_ignored = JSON.parse(File.read(file), :symbolize_names => true)[:ignored_warnings]
|
79
79
|
else
|
80
80
|
Brakeman.notify "[Notice] Could not find ignore configuration in #{file}"
|
81
81
|
@already_ignored = []
|
@@ -107,7 +107,7 @@ module Brakeman
|
|
107
107
|
}
|
108
108
|
|
109
109
|
File.open file, "w" do |f|
|
110
|
-
f.puts
|
110
|
+
f.puts JSON.pretty_generate(output)
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
@@ -1,6 +1,3 @@
|
|
1
|
-
Brakeman.load_brakeman_dependency 'multi_json'
|
2
|
-
require 'brakeman/report/initializers/multi_json'
|
3
|
-
|
4
1
|
class Brakeman::Report::JSON < Brakeman::Report::Base
|
5
2
|
def generate_report
|
6
3
|
errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}
|
@@ -32,7 +29,7 @@ class Brakeman::Report::JSON < Brakeman::Report::Base
|
|
32
29
|
:errors => errors
|
33
30
|
}
|
34
31
|
|
35
|
-
|
32
|
+
JSON.pretty_generate report_info
|
36
33
|
end
|
37
34
|
|
38
35
|
def convert_to_hashes warnings
|
data/lib/brakeman/tracker.rb
CHANGED
@@ -115,6 +115,23 @@ class Brakeman::Tracker
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
+
|
119
|
+
def each_class
|
120
|
+
classes = [self.controllers, self.models]
|
121
|
+
|
122
|
+
if @options[:index_libs]
|
123
|
+
classes << self.libs
|
124
|
+
end
|
125
|
+
|
126
|
+
classes.each do |set|
|
127
|
+
set.each do |set_name, collection|
|
128
|
+
collection.src.each do |file, src|
|
129
|
+
yield src, set_name, file
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
118
135
|
#Find a method call.
|
119
136
|
#
|
120
137
|
#Options:
|
@@ -178,6 +195,10 @@ class Brakeman::Tracker
|
|
178
195
|
finder.process_source definition, :class => set_name, :method => method_name, :file => file
|
179
196
|
end
|
180
197
|
|
198
|
+
self.each_class do |definition, set_name, file|
|
199
|
+
finder.process_source definition, :class => set_name, :file => file
|
200
|
+
end
|
201
|
+
|
181
202
|
self.each_template do |name, template|
|
182
203
|
finder.process_source template.src, :template => template, :file => template.file
|
183
204
|
end
|
data/lib/brakeman/util.rb
CHANGED
@@ -167,7 +167,8 @@ module Brakeman::Util
|
|
167
167
|
|
168
168
|
#Check if _exp_ represents a method call: s(:call, ...)
|
169
169
|
def call? exp
|
170
|
-
exp.is_a? Sexp and
|
170
|
+
exp.is_a? Sexp and
|
171
|
+
(exp.node_type == :call or exp.node_type == :safe_call)
|
171
172
|
end
|
172
173
|
|
173
174
|
#Check if _exp_ represents a Regexp: s(:lit, /.../)
|
@@ -214,7 +215,7 @@ module Brakeman::Util
|
|
214
215
|
if exp.is_a? Sexp
|
215
216
|
return true if exp.node_type == :params or ALL_PARAMETERS.include? exp
|
216
217
|
|
217
|
-
if exp
|
218
|
+
if call? exp
|
218
219
|
if params? exp[1]
|
219
220
|
return true
|
220
221
|
elsif exp[2] == :[]
|
@@ -230,7 +231,7 @@ module Brakeman::Util
|
|
230
231
|
if exp.is_a? Sexp
|
231
232
|
return true if exp.node_type == :cookies or exp == COOKIES
|
232
233
|
|
233
|
-
if exp
|
234
|
+
if call? exp
|
234
235
|
if cookies? exp[1]
|
235
236
|
return true
|
236
237
|
elsif exp[2] == :[]
|
data/lib/brakeman/version.rb
CHANGED
data/lib/brakeman/warning.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'json'
|
2
2
|
require 'digest/sha2'
|
3
3
|
require 'brakeman/warning_codes'
|
4
4
|
|
@@ -241,7 +241,7 @@ class Brakeman::Warning
|
|
241
241
|
end
|
242
242
|
|
243
243
|
def to_json
|
244
|
-
|
244
|
+
JSON.generate self.to_hash
|
245
245
|
end
|
246
246
|
|
247
247
|
private
|
data/lib/ruby_parser/bm_sexp.rb
CHANGED
@@ -141,13 +141,13 @@ class Sexp
|
|
141
141
|
#s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
|
142
142
|
# ^-----------target-----------^
|
143
143
|
def target
|
144
|
-
expect :call, :attrasgn
|
144
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
145
145
|
self[1]
|
146
146
|
end
|
147
147
|
|
148
148
|
#Sets the target of a method call:
|
149
149
|
def target= exp
|
150
|
-
expect :call, :attrasgn
|
150
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
151
151
|
@my_hash_value = nil
|
152
152
|
self[1] = exp
|
153
153
|
end
|
@@ -157,10 +157,10 @@ class Sexp
|
|
157
157
|
#s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
|
158
158
|
# ^- method
|
159
159
|
def method
|
160
|
-
expect :call, :attrasgn, :super, :zsuper, :result
|
160
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper, :result
|
161
161
|
|
162
162
|
case self.node_type
|
163
|
-
when :call, :attrasgn
|
163
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
164
164
|
self[2]
|
165
165
|
when :super, :zsuper
|
166
166
|
:super
|
@@ -170,14 +170,14 @@ class Sexp
|
|
170
170
|
end
|
171
171
|
|
172
172
|
def method= name
|
173
|
-
expect :call
|
173
|
+
expect :call, :safe_call
|
174
174
|
|
175
175
|
self[2] = name
|
176
176
|
end
|
177
177
|
|
178
178
|
#Sets the arglist in a method call.
|
179
179
|
def arglist= exp
|
180
|
-
expect :call, :attrasgn
|
180
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
181
181
|
@my_hash_value = nil
|
182
182
|
start_index = 3
|
183
183
|
|
@@ -201,10 +201,10 @@ class Sexp
|
|
201
201
|
# s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
|
202
202
|
# ^------------ arglist ------------^
|
203
203
|
def arglist
|
204
|
-
expect :call, :attrasgn, :super, :zsuper
|
204
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper
|
205
205
|
|
206
206
|
case self.node_type
|
207
|
-
when :call, :attrasgn
|
207
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
208
208
|
self[3..-1].unshift :arglist
|
209
209
|
when :super, :zsuper
|
210
210
|
if self[1]
|
@@ -220,10 +220,10 @@ class Sexp
|
|
220
220
|
# s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
|
221
221
|
# ^--------args--------^
|
222
222
|
def args
|
223
|
-
expect :call, :attrasgn, :super, :zsuper
|
223
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper
|
224
224
|
|
225
225
|
case self.node_type
|
226
|
-
when :call, :attrasgn
|
226
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
227
227
|
if self[3]
|
228
228
|
self[3..-1]
|
229
229
|
else
|
@@ -239,11 +239,11 @@ class Sexp
|
|
239
239
|
end
|
240
240
|
|
241
241
|
def each_arg replace = false
|
242
|
-
expect :call, :attrasgn, :super, :zsuper
|
242
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper
|
243
243
|
range = nil
|
244
244
|
|
245
245
|
case self.node_type
|
246
|
-
when :call, :attrasgn
|
246
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
247
247
|
if self[3]
|
248
248
|
range = (3...self.length)
|
249
249
|
end
|
@@ -270,43 +270,43 @@ class Sexp
|
|
270
270
|
|
271
271
|
#Returns first argument of a method call.
|
272
272
|
def first_arg
|
273
|
-
expect :call, :attrasgn
|
273
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
274
274
|
self[3]
|
275
275
|
end
|
276
276
|
|
277
277
|
#Sets first argument of a method call.
|
278
278
|
def first_arg= exp
|
279
|
-
expect :call, :attrasgn
|
279
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
280
280
|
@my_hash_value = nil
|
281
281
|
self[3] = exp
|
282
282
|
end
|
283
283
|
|
284
284
|
#Returns second argument of a method call.
|
285
285
|
def second_arg
|
286
|
-
expect :call, :attrasgn
|
286
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
287
287
|
self[4]
|
288
288
|
end
|
289
289
|
|
290
290
|
#Sets second argument of a method call.
|
291
291
|
def second_arg= exp
|
292
|
-
expect :call, :attrasgn
|
292
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
293
293
|
@my_hash_value = nil
|
294
294
|
self[4] = exp
|
295
295
|
end
|
296
296
|
|
297
297
|
def third_arg
|
298
|
-
expect :call, :attrasgn
|
298
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
299
299
|
self[5]
|
300
300
|
end
|
301
301
|
|
302
302
|
def third_arg= exp
|
303
|
-
expect :call, :attrasgn
|
303
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
304
304
|
@my_hash_value = nil
|
305
305
|
self[5] = exp
|
306
306
|
end
|
307
307
|
|
308
308
|
def last_arg
|
309
|
-
expect :call, :attrasgn
|
309
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
310
310
|
|
311
311
|
if self[3]
|
312
312
|
self[-1]
|
@@ -427,9 +427,9 @@ class Sexp
|
|
427
427
|
# s(:lasgn, :x, s(:lit, 1))
|
428
428
|
# ^--rhs---^
|
429
429
|
def rhs
|
430
|
-
expect :attrasgn, *ASSIGNMENT_BOOL
|
430
|
+
expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL
|
431
431
|
|
432
|
-
if self.node_type == :attrasgn
|
432
|
+
if self.node_type == :attrasgn or self.node_type == :safe_attrasgn
|
433
433
|
self[3]
|
434
434
|
else
|
435
435
|
self[2]
|
@@ -438,10 +438,10 @@ class Sexp
|
|
438
438
|
|
439
439
|
#Sets the right hand side of assignment or boolean.
|
440
440
|
def rhs= exp
|
441
|
-
expect :attrasgn, *ASSIGNMENT_BOOL
|
441
|
+
expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL
|
442
442
|
@my_hash_value = nil
|
443
443
|
|
444
|
-
if self.node_type == :attrasgn
|
444
|
+
if self.node_type == :attrasgn or self.node_type == :safe_attrasgn
|
445
445
|
self[3] = exp
|
446
446
|
else
|
447
447
|
self[2] = exp
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brakeman-min
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0.pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Collins
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain:
|
11
11
|
- brakeman-public_cert.pem
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-02-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: test-unit
|
@@ -31,48 +31,28 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 3.
|
34
|
+
version: 3.8.1
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 3.
|
41
|
+
version: 3.8.1
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: ruby2ruby
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: 2.1.1
|
49
|
-
- - "<"
|
50
|
-
- !ruby/object:Gem::Version
|
51
|
-
version: 2.3.0
|
52
|
-
type: :runtime
|
53
|
-
prerelease: false
|
54
|
-
version_requirements: !ruby/object:Gem::Requirement
|
55
|
-
requirements:
|
56
|
-
- - ">="
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
version: 2.1.1
|
59
|
-
- - "<"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 2.3.0
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: multi_json
|
64
44
|
requirement: !ruby/object:Gem::Requirement
|
65
45
|
requirements:
|
66
46
|
- - "~>"
|
67
47
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
48
|
+
version: 2.3.0
|
69
49
|
type: :runtime
|
70
50
|
prerelease: false
|
71
51
|
version_requirements: !ruby/object:Gem::Requirement
|
72
52
|
requirements:
|
73
53
|
- - "~>"
|
74
54
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
55
|
+
version: 2.3.0
|
76
56
|
- !ruby/object:Gem::Dependency
|
77
57
|
name: safe_yaml
|
78
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -214,8 +194,6 @@ files:
|
|
214
194
|
- lib/brakeman/report/config/remediation.yml
|
215
195
|
- lib/brakeman/report/ignore/config.rb
|
216
196
|
- lib/brakeman/report/ignore/interactive.rb
|
217
|
-
- lib/brakeman/report/initializers/faster_csv.rb
|
218
|
-
- lib/brakeman/report/initializers/multi_json.rb
|
219
197
|
- lib/brakeman/report/renderer.rb
|
220
198
|
- lib/brakeman/report/report_base.rb
|
221
199
|
- lib/brakeman/report/report_codeclimate.rb
|
@@ -267,9 +245,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
267
245
|
version: '0'
|
268
246
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
269
247
|
requirements:
|
270
|
-
- - "
|
248
|
+
- - ">"
|
271
249
|
- !ruby/object:Gem::Version
|
272
|
-
version:
|
250
|
+
version: 1.3.1
|
273
251
|
requirements: []
|
274
252
|
rubyforge_project:
|
275
253
|
rubygems_version: 2.4.8
|
@@ -1,29 +0,0 @@
|
|
1
|
-
#MultiJson interface changed in 1.3.0, but need
|
2
|
-
#to support older MultiJson for Rails 3.1.
|
3
|
-
mj_engine = nil
|
4
|
-
|
5
|
-
if MultiJson.respond_to? :default_adapter
|
6
|
-
mj_engine = MultiJson.default_adapter
|
7
|
-
else
|
8
|
-
mj_engine = MultiJson.default_engine
|
9
|
-
|
10
|
-
module MultiJson
|
11
|
-
def self.dump *args
|
12
|
-
encode *args
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.load *args
|
16
|
-
decode *args
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
#This is so OkJson will work with symbol values
|
22
|
-
if mj_engine == :ok_json
|
23
|
-
class Symbol
|
24
|
-
def to_json
|
25
|
-
self.to_s.inspect
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|