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
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
[](http://brakemanscanner.org/)
|
2
2
|
|
3
3
|
[](https://circleci.com/gh/presidentbeef/brakeman)
|
4
|
-
[](https://codeclimate.com/github/presidentbeef/brakeman/maintainability)
|
5
4
|
[](https://codeclimate.com/github/presidentbeef/brakeman/test_coverage)
|
6
5
|
[](https://gitter.im/presidentbeef/brakeman)
|
7
6
|
|
@@ -63,7 +62,7 @@ Outside of Rails root (note that the output file is relative to path/to/rails/ap
|
|
63
62
|
|
64
63
|
# Compatibility
|
65
64
|
|
66
|
-
Brakeman should work with any version of Rails from 2.3.x to
|
65
|
+
Brakeman should work with any version of Rails from 2.3.x to 6.x.
|
67
66
|
|
68
67
|
Brakeman can analyze code written with Ruby 1.8 syntax and newer, but requires at least Ruby 2.3.0 to run.
|
69
68
|
|
data/lib/brakeman/call_index.rb
CHANGED
@@ -27,7 +27,7 @@ class Brakeman::CallIndex
|
|
27
27
|
if options[:chained]
|
28
28
|
return find_chain options
|
29
29
|
#Find by narrowest category
|
30
|
-
elsif target
|
30
|
+
elsif target.is_a? Array and method.is_a? Array
|
31
31
|
if target.length > method.length
|
32
32
|
calls = filter_by_target calls_by_methods(method), target
|
33
33
|
else
|
@@ -35,6 +35,12 @@ class Brakeman::CallIndex
|
|
35
35
|
calls = filter_by_method calls, method
|
36
36
|
end
|
37
37
|
|
38
|
+
elsif target.is_a? Regexp and method
|
39
|
+
calls = filter_by_target(calls_by_method(method), target)
|
40
|
+
|
41
|
+
elsif method.is_a? Regexp and target
|
42
|
+
calls = filter_by_method(calls_by_target(target), method)
|
43
|
+
|
38
44
|
#Find by target, then by methods, if provided
|
39
45
|
elsif target
|
40
46
|
calls = calls_by_target target
|
@@ -85,6 +91,16 @@ class Brakeman::CallIndex
|
|
85
91
|
end
|
86
92
|
end
|
87
93
|
|
94
|
+
def remove_indexes_by_file file
|
95
|
+
[@calls_by_method, @calls_by_target].each do |calls_by|
|
96
|
+
calls_by.each do |_name, calls|
|
97
|
+
calls.delete_if do |call|
|
98
|
+
call[:location][:file] == file
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
88
104
|
def index_calls calls
|
89
105
|
calls.each do |call|
|
90
106
|
@calls_by_method[call[:method]] ||= []
|
@@ -116,8 +132,11 @@ class Brakeman::CallIndex
|
|
116
132
|
end
|
117
133
|
|
118
134
|
def calls_by_target target
|
119
|
-
|
135
|
+
case target
|
136
|
+
when Array
|
120
137
|
calls_by_targets target
|
138
|
+
when Regexp
|
139
|
+
calls_by_targets_regex target
|
121
140
|
else
|
122
141
|
@calls_by_target[target] || []
|
123
142
|
end
|
@@ -133,10 +152,24 @@ class Brakeman::CallIndex
|
|
133
152
|
calls
|
134
153
|
end
|
135
154
|
|
155
|
+
def calls_by_targets_regex targets_regex
|
156
|
+
calls = []
|
157
|
+
|
158
|
+
@calls_by_target.each do |key, value|
|
159
|
+
case key
|
160
|
+
when String, Symbol
|
161
|
+
calls.concat value if key.match targets_regex
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
calls
|
166
|
+
end
|
167
|
+
|
136
168
|
def calls_by_method method
|
137
|
-
|
169
|
+
case method
|
170
|
+
when Array
|
138
171
|
calls_by_methods method
|
139
|
-
|
172
|
+
when Regexp
|
140
173
|
calls_by_methods_regex method
|
141
174
|
else
|
142
175
|
@calls_by_method[method.to_sym] || []
|
@@ -156,26 +189,28 @@ class Brakeman::CallIndex
|
|
156
189
|
|
157
190
|
def calls_by_methods_regex methods_regex
|
158
191
|
calls = []
|
192
|
+
|
159
193
|
@calls_by_method.each do |key, value|
|
160
|
-
calls.concat value if key.
|
194
|
+
calls.concat value if key.match methods_regex
|
161
195
|
end
|
162
|
-
calls
|
163
|
-
end
|
164
196
|
|
165
|
-
|
166
|
-
@calls_by_target[nil]
|
197
|
+
calls
|
167
198
|
end
|
168
199
|
|
169
200
|
def filter calls, key, value
|
170
|
-
|
201
|
+
case value
|
202
|
+
when Array
|
171
203
|
values = Set.new value
|
172
204
|
|
173
205
|
calls.select do |call|
|
174
206
|
values.include? call[key]
|
175
207
|
end
|
176
|
-
|
208
|
+
when Regexp
|
177
209
|
calls.select do |call|
|
178
|
-
call[key]
|
210
|
+
case call[key]
|
211
|
+
when String, Symbol
|
212
|
+
call[key].match value
|
213
|
+
end
|
179
214
|
end
|
180
215
|
else
|
181
216
|
calls.select do |call|
|
@@ -197,15 +232,19 @@ class Brakeman::CallIndex
|
|
197
232
|
end
|
198
233
|
|
199
234
|
def filter_by_chain calls, target
|
200
|
-
|
235
|
+
case target
|
236
|
+
when Array
|
201
237
|
targets = Set.new target
|
202
238
|
|
203
239
|
calls.select do |call|
|
204
240
|
targets.include? call[:chain].first
|
205
241
|
end
|
206
|
-
|
242
|
+
when Regexp
|
207
243
|
calls.select do |call|
|
208
|
-
call[:chain].first
|
244
|
+
case call[:chain].first
|
245
|
+
when String, Symbol
|
246
|
+
call[:chain].first.match target
|
247
|
+
end
|
209
248
|
end
|
210
249
|
else
|
211
250
|
calls.select do |call|
|
@@ -39,24 +39,15 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
39
39
|
@active_record_models = nil
|
40
40
|
@mass_assign_disabled = nil
|
41
41
|
@has_user_input = nil
|
42
|
+
@in_array = false
|
42
43
|
@safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id]
|
43
44
|
@comparison_ops = Set[:==, :!=, :>, :<, :>=, :<=]
|
44
45
|
end
|
45
46
|
|
46
47
|
#Add result to result list, which is used to check for duplicates
|
47
|
-
def add_result result
|
48
|
-
location
|
49
|
-
location =
|
50
|
-
location = location.name if location.is_a? Brakeman::Collection
|
51
|
-
location = location.to_sym
|
52
|
-
|
53
|
-
if result.is_a? Hash
|
54
|
-
line = result[:call].original_line || result[:call].line
|
55
|
-
elsif sexp? result
|
56
|
-
line = result.original_line || result.line
|
57
|
-
else
|
58
|
-
raise ArgumentError
|
59
|
-
end
|
48
|
+
def add_result result
|
49
|
+
location = get_location result
|
50
|
+
location, line = get_location result
|
60
51
|
|
61
52
|
@results << [line, location, result]
|
62
53
|
end
|
@@ -119,9 +110,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
119
110
|
exp
|
120
111
|
end
|
121
112
|
|
113
|
+
def process_array exp
|
114
|
+
@in_array = true
|
115
|
+
process_default exp
|
116
|
+
ensure
|
117
|
+
@in_array = false
|
118
|
+
end
|
119
|
+
|
122
120
|
#Does not actually process string interpolation, but notes that it occurred.
|
123
121
|
def process_dstr exp
|
124
|
-
unless @string_interp # don't overwrite existing value
|
122
|
+
unless array_interp? exp or @string_interp # don't overwrite existing value
|
125
123
|
@string_interp = Match.new(:interp, exp)
|
126
124
|
end
|
127
125
|
|
@@ -130,6 +128,20 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
130
128
|
|
131
129
|
private
|
132
130
|
|
131
|
+
# Checking for
|
132
|
+
#
|
133
|
+
# %W[#{a}]
|
134
|
+
#
|
135
|
+
# which will be parsed as
|
136
|
+
#
|
137
|
+
# s(:array, s(:dstr, "", s(:evstr, s(:call, nil, :a))))
|
138
|
+
def array_interp? exp
|
139
|
+
@in_array and
|
140
|
+
string_interp? exp and
|
141
|
+
exp[1] == "".freeze and
|
142
|
+
exp.length == 3 # only one interpolated value
|
143
|
+
end
|
144
|
+
|
133
145
|
def always_safe_method? meth
|
134
146
|
@safe_input_attributes.include? meth or
|
135
147
|
@comparison_ops.include? meth
|
@@ -170,8 +182,9 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
170
182
|
@mass_assign_disabled = true
|
171
183
|
else
|
172
184
|
#Check for ActiveRecord::Base.send(:attr_accessible, nil)
|
173
|
-
tracker.
|
174
|
-
call = result
|
185
|
+
tracker.find_call(target: :"ActiveRecord::Base", method: :attr_accessible).each do |result|
|
186
|
+
call = result[:call]
|
187
|
+
|
175
188
|
if call? call
|
176
189
|
if call.first_arg == Sexp.new(:nil)
|
177
190
|
@mass_assign_disabled = true
|
@@ -180,26 +193,12 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
180
193
|
end
|
181
194
|
end
|
182
195
|
|
183
|
-
unless @mass_assign_disabled
|
184
|
-
tracker.check_initializers(:"ActiveRecord::Base", :send).each do |result|
|
185
|
-
call = result.call
|
186
|
-
if call? call
|
187
|
-
if call.first_arg == Sexp.new(:lit, :attr_accessible) and call.second_arg == Sexp.new(:nil)
|
188
|
-
@mass_assign_disabled = true
|
189
|
-
break
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
196
|
unless @mass_assign_disabled
|
196
197
|
#Check for
|
197
198
|
# class ActiveRecord::Base
|
198
199
|
# attr_accessible nil
|
199
200
|
# end
|
200
|
-
|
201
|
-
|
202
|
-
matches.each do |result|
|
201
|
+
tracker.check_initializers([], :attr_accessible).each do |result|
|
203
202
|
if result.module == "ActiveRecord" and result.result_class == :Base
|
204
203
|
arg = result.call.first_arg
|
205
204
|
|
@@ -227,10 +226,8 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
227
226
|
end
|
228
227
|
|
229
228
|
unless @mass_assign_disabled
|
230
|
-
|
231
|
-
|
232
|
-
matches.each do |result|
|
233
|
-
call = result.call
|
229
|
+
tracker.find_call(target: :"ActiveRecord::Base", method: [:send, :include]).each do |result|
|
230
|
+
call = result[:call]
|
234
231
|
if call? call and (call.first_arg == forbidden_protection or call.second_arg == forbidden_protection)
|
235
232
|
@mass_assign_disabled = true
|
236
233
|
end
|
@@ -250,6 +247,22 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
250
247
|
#This is to avoid reporting duplicates. Checks if the result has been
|
251
248
|
#reported already from the same line number.
|
252
249
|
def duplicate? result, location = nil
|
250
|
+
location, line = get_location result
|
251
|
+
|
252
|
+
@results.each do |r|
|
253
|
+
if r[0] == line and r[1] == location
|
254
|
+
if tracker.options[:combine_locations]
|
255
|
+
return true
|
256
|
+
elsif r[2] == result
|
257
|
+
return true
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
false
|
263
|
+
end
|
264
|
+
|
265
|
+
def get_location result
|
253
266
|
if result.is_a? Hash
|
254
267
|
line = result[:call].original_line || result[:call].line
|
255
268
|
elsif sexp? result
|
@@ -258,23 +271,13 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
258
271
|
raise ArgumentError
|
259
272
|
end
|
260
273
|
|
261
|
-
location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template]
|
274
|
+
location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template] || result[:location][:file].to_s
|
262
275
|
|
263
276
|
location = location[:name] if location.is_a? Hash
|
264
277
|
location = location.name if location.is_a? Brakeman::Collection
|
265
278
|
location = location.to_sym
|
266
279
|
|
267
|
-
|
268
|
-
if r[0] == line and r[1] == location
|
269
|
-
if tracker.options[:combine_locations]
|
270
|
-
return true
|
271
|
-
elsif r[2] == result
|
272
|
-
return true
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
false
|
280
|
+
return location, line
|
278
281
|
end
|
279
282
|
|
280
283
|
#Checks if an expression contains string interpolation.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckCookieSerialization < Brakeman::BaseCheck
|
4
|
+
Brakeman::Checks.add self
|
5
|
+
|
6
|
+
@description = "Check for use of Marshal for cookie serialization"
|
7
|
+
|
8
|
+
def run_check
|
9
|
+
tracker.find_call(target: :'Rails.application.config.action_dispatch', method: :cookies_serializer=).each do |result|
|
10
|
+
setting = result[:call].first_arg
|
11
|
+
|
12
|
+
if symbol? setting and [:marshal, :hybrid].include? setting.value
|
13
|
+
warn :result => result,
|
14
|
+
:warning_type => "Remote Code Execution",
|
15
|
+
:warning_code => :unsafe_cookie_serialization,
|
16
|
+
:message => msg("Use of unsafe cookie serialization strategy ", msg_code(setting.value.inspect), " might lead to remote code execution"),
|
17
|
+
:confidence => :medium,
|
18
|
+
:link_path => "unsafe_deserialization"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -287,7 +287,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
|
|
287
287
|
|
288
288
|
def setup
|
289
289
|
@ignore_methods = Set[:==, :!=, :button_to, :check_box, :content_tag, :escapeHTML, :escape_once,
|
290
|
-
:field_field, :fields_for, :h, :hidden_field,
|
290
|
+
:field_field, :fields_for, :form_for, :h, :hidden_field,
|
291
291
|
:hidden_field, :hidden_field_tag, :image_tag, :label,
|
292
292
|
:link_to, :mail_to, :radio_button, :select,
|
293
293
|
:submit_tag, :text_area, :text_field,
|
@@ -316,11 +316,11 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
|
|
316
316
|
end
|
317
317
|
|
318
318
|
json_escape_on = false
|
319
|
-
initializers = tracker.
|
320
|
-
initializers.each {|result| json_escape_on = true?(result
|
319
|
+
initializers = tracker.find_call(target: :ActiveSupport, method: :escape_html_entities_in_json=)
|
320
|
+
initializers.each {|result| json_escape_on = true?(result[:call].first_arg) }
|
321
321
|
|
322
322
|
if tracker.config.escape_html_entities_in_json?
|
323
|
-
|
323
|
+
json_escape_on = true
|
324
324
|
elsif version_between? "4.0.0", "9.9.9"
|
325
325
|
json_escape_on = true
|
326
326
|
end
|
@@ -80,13 +80,10 @@ class Brakeman::CheckDeserialize < Brakeman::BaseCheck
|
|
80
80
|
def oj_safe_default?
|
81
81
|
safe_default = false
|
82
82
|
|
83
|
-
|
84
|
-
if tracker.check_initializers(:Oj, :mimic_JSON).any?
|
83
|
+
if tracker.find_call(target: :Oj, method: :mimic_JSON).any?
|
85
84
|
safe_default = true
|
86
|
-
|
87
|
-
|
88
|
-
if result = tracker.check_initializers(:Oj, :default_options=).first
|
89
|
-
options = result.call.first_arg
|
85
|
+
elsif result = tracker.find_call(target: :Oj, method: :default_options=).first
|
86
|
+
options = result[:call].first_arg
|
90
87
|
|
91
88
|
if oj_safe_mode? options
|
92
89
|
safe_default = true
|
@@ -21,6 +21,10 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
21
21
|
SHELL_ESCAPE_MODULE_METHODS = Set[:escape, :join, :shellescape, :shelljoin]
|
22
22
|
SHELL_ESCAPE_MIXIN_METHODS = Set[:shellescape, :shelljoin]
|
23
23
|
|
24
|
+
# These are common shells that are known to allow the execution of commands
|
25
|
+
# via a -c flag. See dash_c_shell_command? for more info.
|
26
|
+
KNOWN_SHELL_COMMANDS = Set["sh", "bash", "ksh", "csh", "tcsh", "zsh"]
|
27
|
+
|
24
28
|
SHELLWORDS = s(:const, :Shellwords)
|
25
29
|
|
26
30
|
#Check models, controllers, and views for command injection.
|
@@ -42,6 +46,8 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
49
|
+
private
|
50
|
+
|
45
51
|
#Processes results from Tracker#find_call.
|
46
52
|
def process_result result
|
47
53
|
call = result[:call]
|
@@ -54,7 +60,17 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
54
60
|
failure = include_user_input?(args) || dangerous_interp?(args)
|
55
61
|
end
|
56
62
|
when :system, :exec
|
57
|
-
|
63
|
+
# Normally, if we're in a `system` or `exec` call, we only are worried
|
64
|
+
# about shell injection when there's a single argument, because comma-
|
65
|
+
# separated arguments are always escaped by Ruby. However, an exception is
|
66
|
+
# when the first two arguments are something like "bash -c" because then
|
67
|
+
# the third argument is effectively the command being run and might be
|
68
|
+
# a malicious executable if it comes (partially or fully) from user input.
|
69
|
+
if dash_c_shell_command?(first_arg, call.second_arg)
|
70
|
+
failure = include_user_input?(args[3]) || dangerous_interp?(args[3])
|
71
|
+
else
|
72
|
+
failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
|
73
|
+
end
|
58
74
|
else
|
59
75
|
failure = include_user_input?(args) || dangerous_interp?(args)
|
60
76
|
end
|
@@ -77,6 +93,15 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
77
93
|
end
|
78
94
|
end
|
79
95
|
|
96
|
+
# @return [Boolean] true iff the command given by `first_arg`, `second_arg`
|
97
|
+
# invokes a new shell process via `<shell_command> -c` (like `bash -c`)
|
98
|
+
def dash_c_shell_command?(first_arg, second_arg)
|
99
|
+
string?(first_arg) &&
|
100
|
+
KNOWN_SHELL_COMMANDS.include?(first_arg.value) &&
|
101
|
+
string?(second_arg) &&
|
102
|
+
second_arg.value == "-c"
|
103
|
+
end
|
104
|
+
|
80
105
|
def check_open_calls
|
81
106
|
tracker.find_call(:targets => [nil, :Kernel], :method => :open).each do |result|
|
82
107
|
if match = dangerous_open_arg?(result[:call].first_arg)
|
@@ -32,7 +32,7 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
|
|
32
32
|
|
33
33
|
file_name = call.first_arg
|
34
34
|
|
35
|
-
return if called_on_tempfile?(file_name)
|
35
|
+
return if called_on_tempfile?(file_name) || sanitized?(file_name)
|
36
36
|
|
37
37
|
if match = has_immediate_user_input?(file_name)
|
38
38
|
confidence = :high
|
@@ -71,6 +71,12 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
|
|
71
71
|
call?(file_name) && file_name.target == s(:const, :Tempfile)
|
72
72
|
end
|
73
73
|
|
74
|
+
def sanitized? file
|
75
|
+
call?(file) &&
|
76
|
+
call?(file.target) &&
|
77
|
+
class_name(file.target.target) == :"ActiveStorage::Filename"
|
78
|
+
end
|
79
|
+
|
74
80
|
def temp_file_method? exp
|
75
81
|
if call? exp
|
76
82
|
return true if exp.call_chain.include? :tempfile
|