brakeman-lib 4.10.0 → 5.0.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 +46 -0
- data/README.md +11 -2
- data/lib/brakeman.rb +21 -4
- data/lib/brakeman/app_tree.rb +36 -3
- data/lib/brakeman/checks/base_check.rb +7 -1
- data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
- data/lib/brakeman/checks/check_evaluation.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +2 -1
- data/lib/brakeman/checks/check_mass_assignment.rb +4 -6
- data/lib/brakeman/checks/check_regex_dos.rb +1 -1
- data/lib/brakeman/checks/check_sanitize_methods.rb +2 -1
- data/lib/brakeman/checks/check_sql.rb +16 -3
- data/lib/brakeman/checks/check_unsafe_reflection_methods.rb +68 -0
- data/lib/brakeman/checks/check_verb_confusion.rb +75 -0
- data/lib/brakeman/file_parser.rb +50 -22
- data/lib/brakeman/options.rb +5 -1
- data/lib/brakeman/parsers/template_parser.rb +26 -3
- data/lib/brakeman/processors/alias_processor.rb +91 -19
- data/lib/brakeman/processors/base_processor.rb +4 -4
- data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
- data/lib/brakeman/processors/controller_processor.rb +1 -1
- data/lib/brakeman/processors/haml_template_processor.rb +8 -1
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +10 -0
- data/lib/brakeman/processors/lib/file_type_detector.rb +64 -0
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +16 -16
- data/lib/brakeman/processors/lib/rails4_config_processor.rb +2 -1
- data/lib/brakeman/processors/library_processor.rb +9 -0
- data/lib/brakeman/processors/output_processor.rb +1 -1
- data/lib/brakeman/processors/template_alias_processor.rb +5 -0
- data/lib/brakeman/report.rb +12 -1
- data/lib/brakeman/report/ignore/interactive.rb +1 -1
- data/lib/brakeman/report/report_base.rb +0 -2
- data/lib/brakeman/report/report_csv.rb +37 -60
- data/lib/brakeman/report/report_github.rb +31 -0
- data/lib/brakeman/report/report_junit.rb +2 -2
- data/lib/brakeman/report/report_sarif.rb +1 -1
- data/lib/brakeman/report/report_sonar.rb +38 -0
- data/lib/brakeman/report/report_tabs.rb +1 -1
- data/lib/brakeman/report/report_text.rb +1 -1
- data/lib/brakeman/rescanner.rb +7 -5
- data/lib/brakeman/scanner.rb +47 -18
- data/lib/brakeman/tracker.rb +39 -4
- data/lib/brakeman/tracker/collection.rb +27 -5
- data/lib/brakeman/tracker/config.rb +73 -0
- data/lib/brakeman/tracker/controller.rb +1 -1
- data/lib/brakeman/tracker/method_info.rb +29 -0
- data/lib/brakeman/util.rb +17 -4
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +10 -2
- data/lib/brakeman/warning_codes.rb +2 -0
- data/lib/ruby_parser/bm_sexp.rb +9 -9
- metadata +39 -5
data/lib/brakeman/file_parser.rb
CHANGED
@@ -1,51 +1,79 @@
|
|
1
|
+
require 'parallel'
|
2
|
+
|
1
3
|
module Brakeman
|
2
4
|
ASTFile = Struct.new(:path, :ast)
|
3
5
|
|
4
6
|
# This class handles reading and parsing files.
|
5
7
|
class FileParser
|
6
|
-
attr_reader :file_list
|
8
|
+
attr_reader :file_list, :errors
|
7
9
|
|
8
|
-
def initialize
|
9
|
-
@
|
10
|
-
@timeout =
|
11
|
-
@
|
12
|
-
@
|
10
|
+
def initialize app_tree, timeout
|
11
|
+
@app_tree = app_tree
|
12
|
+
@timeout = timeout
|
13
|
+
@file_list = []
|
14
|
+
@errors = []
|
13
15
|
end
|
14
16
|
|
15
|
-
def parse_files list
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def parse_files list
|
18
|
+
# Parse the files in parallel.
|
19
|
+
# By default, the parsing will be in separate processes.
|
20
|
+
# So we map the result to ASTFiles and/or Exceptions
|
21
|
+
# then partition them into ASTFiles and Exceptions
|
22
|
+
# and add the Exceptions to @errors
|
23
|
+
#
|
24
|
+
# Basically just a funky way to deal with two possible
|
25
|
+
# return types that are returned from isolated processes.
|
26
|
+
#
|
27
|
+
# Note this method no longer uses read_files
|
28
|
+
@file_list, new_errors = Parallel.map(list) do |file_name|
|
29
|
+
file_path = @app_tree.file_path(file_name)
|
30
|
+
contents = file_path.read
|
31
|
+
|
32
|
+
begin
|
33
|
+
if ast = parse_ruby(contents, file_path.relative)
|
34
|
+
ASTFile.new(file_name, ast)
|
35
|
+
end
|
36
|
+
rescue Exception => e
|
37
|
+
e
|
19
38
|
end
|
39
|
+
end.compact.partition do |result|
|
40
|
+
result.is_a? ASTFile
|
20
41
|
end
|
21
|
-
end
|
22
42
|
|
23
|
-
|
24
|
-
|
43
|
+
errors.concat new_errors
|
44
|
+
end
|
25
45
|
|
46
|
+
def read_files list
|
26
47
|
list.each do |path|
|
27
48
|
file = @app_tree.file_path(path)
|
28
49
|
|
29
|
-
|
30
|
-
|
31
|
-
|
50
|
+
begin
|
51
|
+
result = yield file, file.read
|
52
|
+
|
53
|
+
if result
|
54
|
+
@file_list << result
|
55
|
+
end
|
56
|
+
rescue Exception => e
|
57
|
+
@errors << e
|
32
58
|
end
|
33
59
|
end
|
34
60
|
end
|
35
61
|
|
62
|
+
# _path_ can be a string or a Brakeman::FilePath
|
36
63
|
def parse_ruby input, path
|
64
|
+
if path.is_a? Brakeman::FilePath
|
65
|
+
path = path.relative
|
66
|
+
end
|
67
|
+
|
37
68
|
begin
|
38
69
|
Brakeman.debug "Parsing #{path}"
|
39
70
|
RubyParser.new.parse input, path, @timeout
|
40
71
|
rescue Racc::ParseError => e
|
41
|
-
|
42
|
-
nil
|
72
|
+
raise e.exception(e.message + "\nCould not parse #{path}")
|
43
73
|
rescue Timeout::Error => e
|
44
|
-
|
45
|
-
nil
|
74
|
+
raise Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout")
|
46
75
|
rescue => e
|
47
|
-
|
48
|
-
nil
|
76
|
+
raise e.exception(e.message + "\nWhile processing #{path}")
|
49
77
|
end
|
50
78
|
end
|
51
79
|
end
|
data/lib/brakeman/options.rb
CHANGED
@@ -166,6 +166,10 @@ module Brakeman::Options
|
|
166
166
|
options[:only_files].merge files
|
167
167
|
end
|
168
168
|
|
169
|
+
opts.on "--[no-]skip-vendor", "Skip processing vendor directory (Default)" do |skip|
|
170
|
+
options[:skip_vendor] = skip
|
171
|
+
end
|
172
|
+
|
169
173
|
opts.on "--skip-libs", "Skip processing lib directory" do
|
170
174
|
options[:skip_libs] = true
|
171
175
|
end
|
@@ -229,7 +233,7 @@ module Brakeman::Options
|
|
229
233
|
|
230
234
|
opts.on "-f",
|
231
235
|
"--format TYPE",
|
232
|
-
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif],
|
236
|
+
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar, :github],
|
233
237
|
"Specify output formats. Default is text" do |type|
|
234
238
|
|
235
239
|
type = "s" if type == :text
|
@@ -9,7 +9,7 @@ module Brakeman
|
|
9
9
|
def initialize tracker, file_parser
|
10
10
|
@tracker = tracker
|
11
11
|
@file_parser = file_parser
|
12
|
-
@
|
12
|
+
@slim_smart = nil # Load slim/smart ?
|
13
13
|
end
|
14
14
|
|
15
15
|
def parse_template path, text
|
@@ -33,7 +33,7 @@ module Brakeman
|
|
33
33
|
end
|
34
34
|
|
35
35
|
if src and ast = @file_parser.parse_ruby(src, path)
|
36
|
-
@file_parser.file_list
|
36
|
+
@file_parser.file_list << TemplateFile.new(path, ast, name, type)
|
37
37
|
end
|
38
38
|
rescue Racc::ParseError => e
|
39
39
|
tracker.error e, "Could not parse #{path}"
|
@@ -89,6 +89,14 @@ module Brakeman
|
|
89
89
|
|
90
90
|
def parse_slim path, text
|
91
91
|
Brakeman.load_brakeman_dependency 'slim'
|
92
|
+
|
93
|
+
if @slim_smart.nil? and load_slim_smart?
|
94
|
+
@slim_smart = true
|
95
|
+
Brakeman.load_brakeman_dependency 'slim/smart'
|
96
|
+
else
|
97
|
+
@slim_smart = false
|
98
|
+
end
|
99
|
+
|
92
100
|
require_relative 'slim_embedded'
|
93
101
|
|
94
102
|
Slim::Template.new(path,
|
@@ -96,8 +104,23 @@ module Brakeman
|
|
96
104
|
:generator => Temple::Generators::RailsOutputBuffer) { text }.precompiled_template
|
97
105
|
end
|
98
106
|
|
107
|
+
def load_slim_smart?
|
108
|
+
return !@slim_smart unless @slim_smart.nil?
|
109
|
+
|
110
|
+
# Terrible hack to find
|
111
|
+
# gem "slim", "~> 3.0.1", require: ["slim", "slim/smart"]
|
112
|
+
if tracker.app_tree.exists? 'Gemfile'
|
113
|
+
gemfile_contents = tracker.app_tree.file_path('Gemfile').read
|
114
|
+
if gemfile_contents.include? 'slim/smart'
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
false
|
120
|
+
end
|
121
|
+
|
99
122
|
def self.parse_inline_erb tracker, text
|
100
|
-
fp = Brakeman::FileParser.new(tracker)
|
123
|
+
fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
|
101
124
|
tp = self.new(tracker, fp)
|
102
125
|
src = tp.parse_erb '_inline_', text
|
103
126
|
type = tp.erubis? ? :erubis : :erb
|
@@ -161,6 +161,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
161
161
|
ARRAY_CONST = s(:const, :Array)
|
162
162
|
HASH_CONST = s(:const, :Hash)
|
163
163
|
RAILS_TEST = s(:call, s(:call, s(:const, :Rails), :env), :test?)
|
164
|
+
RAILS_DEV = s(:call, s(:call, s(:const, :Rails), :env), :development?)
|
164
165
|
|
165
166
|
#Process a method call.
|
166
167
|
def process_call exp
|
@@ -182,11 +183,17 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
182
183
|
return exp
|
183
184
|
end
|
184
185
|
|
186
|
+
# If x(*[1,2,3]) change to x(1,2,3)
|
187
|
+
# if that's the only argument
|
188
|
+
if splat_array? exp.first_arg and exp.second_arg.nil?
|
189
|
+
exp.arglist = exp.first_arg[1].sexp_body
|
190
|
+
end
|
191
|
+
|
185
192
|
target = exp.target
|
186
193
|
method = exp.method
|
187
194
|
first_arg = exp.first_arg
|
188
195
|
|
189
|
-
if method == :send or method == :try
|
196
|
+
if method == :send or method == :__send__ or method == :try
|
190
197
|
collapse_send_call exp, first_arg
|
191
198
|
end
|
192
199
|
|
@@ -194,11 +201,11 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
194
201
|
res = process_or_simple_operation(exp)
|
195
202
|
return res if res
|
196
203
|
elsif target == ARRAY_CONST and method == :new
|
197
|
-
return Sexp.new(:array, *exp.args)
|
204
|
+
return Sexp.new(:array, *exp.args).line(exp.line)
|
198
205
|
elsif target == HASH_CONST and method == :new and first_arg.nil? and !node_type?(@exp_context.last, :iter)
|
199
|
-
return Sexp.new(:hash)
|
200
|
-
elsif exp == RAILS_TEST
|
201
|
-
return Sexp.new(:false)
|
206
|
+
return Sexp.new(:hash).line(exp.line)
|
207
|
+
elsif exp == RAILS_TEST or exp == RAILS_DEV
|
208
|
+
return Sexp.new(:false).line(exp.line)
|
202
209
|
end
|
203
210
|
|
204
211
|
#See if it is possible to simplify some basic cases
|
@@ -213,13 +220,28 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
213
220
|
exp = math_op(:+, target, first_arg, exp)
|
214
221
|
end
|
215
222
|
when :-, :*, :/
|
216
|
-
|
223
|
+
if method == :* and array? target
|
224
|
+
if string? first_arg
|
225
|
+
exp = process_array_join(target, first_arg)
|
226
|
+
end
|
227
|
+
else
|
228
|
+
exp = math_op(method, target, first_arg, exp)
|
229
|
+
end
|
217
230
|
when :[]
|
218
231
|
if array? target
|
219
232
|
exp = process_array_access(target, exp.args, exp)
|
220
233
|
elsif hash? target
|
221
234
|
exp = process_hash_access(target, first_arg, exp)
|
222
235
|
end
|
236
|
+
when :fetch
|
237
|
+
if array? target
|
238
|
+
# Not dealing with default value
|
239
|
+
# so just pass in first argument, but process_array_access expects
|
240
|
+
# an array of arguments.
|
241
|
+
exp = process_array_access(target, [first_arg], exp)
|
242
|
+
elsif hash? target
|
243
|
+
exp = process_hash_access(target, first_arg, exp)
|
244
|
+
end
|
223
245
|
when :merge!, :update
|
224
246
|
if hash? target and hash? first_arg
|
225
247
|
target = process_hash_merge! target, first_arg
|
@@ -236,7 +258,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
236
258
|
env[target_var] = target
|
237
259
|
return target
|
238
260
|
elsif string? target and string_interp? first_arg
|
239
|
-
exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg
|
261
|
+
exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg.sexp_body(2)).line(exp.line)
|
240
262
|
env[target_var] = exp
|
241
263
|
elsif string? first_arg and string_interp? target
|
242
264
|
if string? target.last
|
@@ -259,6 +281,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
259
281
|
target = find_push_target(target_var)
|
260
282
|
env[target] = exp unless target.nil? # Happens in TemplateAliasProcessor
|
261
283
|
end
|
284
|
+
when :push
|
285
|
+
if array? target
|
286
|
+
target << first_arg
|
287
|
+
env[target_var] = target
|
288
|
+
return target
|
289
|
+
end
|
262
290
|
when :first
|
263
291
|
if array? target and first_arg.nil? and sexp? target[1]
|
264
292
|
exp = target[1]
|
@@ -272,7 +300,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
272
300
|
exp = target
|
273
301
|
end
|
274
302
|
when :join
|
275
|
-
if array? target and
|
303
|
+
if array? target and (string? first_arg or first_arg.nil?)
|
276
304
|
exp = process_array_join(target, first_arg)
|
277
305
|
end
|
278
306
|
when :!
|
@@ -280,6 +308,15 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
280
308
|
if call? target and target.method == :!
|
281
309
|
exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line)
|
282
310
|
end
|
311
|
+
when :values
|
312
|
+
# Hash literal
|
313
|
+
if node_type? target, :hash
|
314
|
+
exp = hash_values(target)
|
315
|
+
end
|
316
|
+
when :values_at
|
317
|
+
if hash? target
|
318
|
+
exp = hash_values_at target, exp.args
|
319
|
+
end
|
283
320
|
end
|
284
321
|
|
285
322
|
exp
|
@@ -287,7 +324,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
287
324
|
|
288
325
|
# Painful conversion of Array#join into string interpolation
|
289
326
|
def process_array_join array, join_str
|
290
|
-
|
327
|
+
# Empty array
|
328
|
+
if array.length == 1
|
329
|
+
return s(:str, '').line(array.line)
|
330
|
+
end
|
331
|
+
|
332
|
+
result = s().line(array.line)
|
291
333
|
|
292
334
|
join_value = if string? join_str
|
293
335
|
join_str.value
|
@@ -295,8 +337,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
295
337
|
nil
|
296
338
|
end
|
297
339
|
|
298
|
-
array
|
299
|
-
|
340
|
+
if array.length > 2
|
341
|
+
array[1..-2].each do |e|
|
342
|
+
result << join_item(e, join_value)
|
343
|
+
end
|
300
344
|
end
|
301
345
|
|
302
346
|
result << join_item(array.last, nil)
|
@@ -325,27 +369,52 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
325
369
|
result.unshift combined_first
|
326
370
|
|
327
371
|
# Have to fix up strings that follow interpolation
|
328
|
-
result.reduce(s(:dstr)) do |memo, e|
|
372
|
+
string = result.reduce(s(:dstr).line(array.line)) do |memo, e|
|
329
373
|
if string? e and node_type? memo.last, :evstr
|
330
374
|
e.value = "#{join_value}#{e.value}"
|
331
375
|
elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr
|
332
|
-
memo << s(:str, join_value)
|
376
|
+
memo << s(:str, join_value).line(e.line)
|
333
377
|
end
|
334
378
|
|
335
379
|
memo << e
|
336
380
|
end
|
381
|
+
|
382
|
+
# Convert (:dstr, "hello world")
|
383
|
+
# to (:str, "hello world")
|
384
|
+
if string.length == 2 and string.last.is_a? String
|
385
|
+
string[0] = :str
|
386
|
+
end
|
387
|
+
|
388
|
+
string
|
337
389
|
end
|
338
390
|
|
339
391
|
def join_item item, join_value
|
340
392
|
if item.is_a? String
|
341
393
|
"#{item}#{join_value}"
|
342
394
|
elsif string? item or symbol? item or number? item
|
343
|
-
s(:str, "#{item.value}#{join_value}")
|
395
|
+
s(:str, "#{item.value}#{join_value}").line(item.line)
|
344
396
|
else
|
345
|
-
s(:evstr, item)
|
397
|
+
s(:evstr, item).line(item.line)
|
346
398
|
end
|
347
399
|
end
|
348
400
|
|
401
|
+
TEMP_FILE_CLASS = s(:const, :Tempfile)
|
402
|
+
|
403
|
+
def temp_file_open? exp
|
404
|
+
call? exp and
|
405
|
+
exp.target == TEMP_FILE_CLASS and
|
406
|
+
exp.method == :open
|
407
|
+
end
|
408
|
+
|
409
|
+
def temp_file_new line
|
410
|
+
s(:call, TEMP_FILE_CLASS, :new).line(line)
|
411
|
+
end
|
412
|
+
|
413
|
+
def splat_array? exp
|
414
|
+
node_type? exp, :splat and
|
415
|
+
node_type? exp[1], :array
|
416
|
+
end
|
417
|
+
|
349
418
|
def process_iter exp
|
350
419
|
@exp_context.push exp
|
351
420
|
exp[1] = process exp.block_call
|
@@ -363,6 +432,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
363
432
|
# Iterating over an array of all literal values
|
364
433
|
local = Sexp.new(:lvar, block_args.last)
|
365
434
|
env.current[local] = safe_literal(exp.line)
|
435
|
+
elsif temp_file_open? call
|
436
|
+
local = Sexp.new(:lvar, block_args.last)
|
437
|
+
env.current[local] = temp_file_new(exp.line)
|
366
438
|
else
|
367
439
|
block_args.each do |e|
|
368
440
|
#Force block arg(s) to be local
|
@@ -663,7 +735,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
663
735
|
end
|
664
736
|
end
|
665
737
|
else
|
666
|
-
new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value)
|
738
|
+
new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value).line(exp.line)
|
667
739
|
|
668
740
|
env[match] = new_value
|
669
741
|
end
|
@@ -941,7 +1013,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
941
1013
|
args = exp.args
|
942
1014
|
exp.pop # remove last arg
|
943
1015
|
if args.length > 1
|
944
|
-
exp.arglist = args
|
1016
|
+
exp.arglist = args.sexp_body
|
945
1017
|
end
|
946
1018
|
end
|
947
1019
|
|
@@ -986,8 +1058,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
986
1058
|
method_name = call.method
|
987
1059
|
|
988
1060
|
#Look for helper methods and see if we can get a return value
|
989
|
-
if found_method = find_method(method_name, @current_class)
|
990
|
-
helper = found_method
|
1061
|
+
if found_method = tracker.find_method(method_name, @current_class)
|
1062
|
+
helper = found_method.src
|
991
1063
|
|
992
1064
|
if sexp? helper
|
993
1065
|
value = process_helper_method helper, call.args
|
@@ -8,7 +8,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
8
8
|
include Brakeman::SafeCallHelper
|
9
9
|
include Brakeman::Util
|
10
10
|
|
11
|
-
IGNORE = Sexp.new
|
11
|
+
IGNORE = Sexp.new(:ignore).line(0)
|
12
12
|
|
13
13
|
#Return a new Processor.
|
14
14
|
def initialize tracker
|
@@ -216,7 +216,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
216
216
|
#
|
217
217
|
#And also :layout for inside templates
|
218
218
|
def find_render_type call, in_view = false
|
219
|
-
rest = Sexp.new(:hash)
|
219
|
+
rest = Sexp.new(:hash).line(call.line)
|
220
220
|
type = nil
|
221
221
|
value = nil
|
222
222
|
first_arg = call.first_arg
|
@@ -236,7 +236,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
236
236
|
end
|
237
237
|
elsif first_arg.is_a? Symbol or first_arg.is_a? String
|
238
238
|
type = :action
|
239
|
-
value = Sexp.new(:lit, first_arg.to_sym)
|
239
|
+
value = Sexp.new(:lit, first_arg.to_sym).line(call.line)
|
240
240
|
elsif first_arg.nil?
|
241
241
|
type = :default
|
242
242
|
elsif not hash? first_arg
|
@@ -293,6 +293,6 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
293
293
|
@tracker.processor.process_template(template_name, ast, type, nil, @current_file)
|
294
294
|
@tracker.processor.process_template_alias(@tracker.templates[template_name])
|
295
295
|
|
296
|
-
return s(:lit, template_name), options
|
296
|
+
return s(:lit, template_name).line(value.line), options
|
297
297
|
end
|
298
298
|
end
|