Class: Brakeman::AliasProcessor

Inherits:
SexpProcessor
  • Object
show all
Includes:
ProcessorHelper, Util
Defined in:
lib/brakeman/processors/alias_processor.rb

Overview

Returns an s-expression with aliases replaced with their value. This does not preserve semantics (due to side effects, etc.), but it makes processing easier when searching for various things.

Direct Known Subclasses

ConfigAliasProcessor, ControllerAliasProcessor, RouteAliasProcessor, TemplateAliasProcessor

Constant Summary

Constant Summary

Constants included from Util

Util::ALL_PARAMETERS, Util::COOKIES, Util::PARAMETERS, Util::PATH_PARAMETERS, Util::QUERY_PARAMETERS, Util::REQUEST_PARAMETERS, Util::SESSION

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Methods included from Util

#array?, #call?, #camelize, #cookies?, #false?, #hash?, #hash_insert, #hash_iterate, #integer?, #number?, #params?, #pluralize, #regexp?, #result?, #set_env_defaults, #sexp?, #string?, #symbol?, #true?, #underscore

Methods included from ProcessorHelper

#class_name, #process_module

Constructor Details

- (AliasProcessor) initialize

Returns a new AliasProcessor with an empty environment.

The recommended usage is:

AliasProcessor.new.process_safely src



20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/brakeman/processors/alias_processor.rb', line 20

def initialize
  super()
  self.strict = false
  self.auto_shift_type = false
  self.require_empty = false
  self.default_method = :process_default
  self.warn_on_default = false
  @env = SexpProcessor::Environment.new
  @inside_if = false
  @ignore_ifs = false
  @tracker = nil #set in subclass as necessary
  set_env_defaults
end

Instance Attribute Details

- (Object) result (readonly)

Returns the value of attribute result



13
14
15
# File 'lib/brakeman/processors/alias_processor.rb', line 13

def result
  @result
end

Instance Method Details

- (Object) find_push_target(exp)

Finds the inner most call target which is not the target of a call to <<



483
484
485
486
487
488
489
# File 'lib/brakeman/processors/alias_processor.rb', line 483

def find_push_target exp
  if call? exp and exp[2] == :<<
    find_push_target exp[1]
  else
    exp
  end
end

- (Object) join_arrays(array1, array2)

Join two array literals into one.



444
445
446
447
448
# File 'lib/brakeman/processors/alias_processor.rb', line 444

def join_arrays array1, array2
  result = Sexp.new(:array)
  result.concat array1[1..-1]
  result.concat array2[1..-1]
end

- (Object) join_strings(string1, string2)

Join two string literals into one.



451
452
453
454
455
# File 'lib/brakeman/processors/alias_processor.rb', line 451

def join_strings string1, string2
  result = Sexp.new(:str)
  result[1] = string1[1] + string2[1]
  result
end

- (Object) only_ivars

Returns a new SexpProcessor::Environment containing only instance variables. This is useful, for example, when processing views.



459
460
461
462
463
464
465
466
# File 'lib/brakeman/processors/alias_processor.rb', line 459

def only_ivars
  res = SexpProcessor::Environment.new
  env.all.each do |k, v|
    #TODO Why would this have nil values?
    res[k] = v.dup if k.node_type == :ivar and not v.nil?
  end
  res
end

- (Object) process_array_access(target, args)

Process single integer access to an array.

Returns the value inside the array, if possible.



419
420
421
422
423
424
425
426
# File 'lib/brakeman/processors/alias_processor.rb', line 419

def process_array_access target, args
  if args.length == 1 and integer? args[0]
    index = args[0][1]
    target[index + 1]
  else
    nil
  end
end

- (Object) process_attrasgn(exp)

'Attribute' assignment x.y = 1 or x = 1



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/brakeman/processors/alias_processor.rb', line 282

def process_attrasgn exp
  tar_variable = exp[1]
  target = exp[1] = process(exp[1])
  method = exp[2]
  if method == :[]=
    index = exp[3][1] = process(exp[3][1])
    value = exp[3][2] = process(exp[3][2])
    match = Sexp.new(:call, target, :[], Sexp.new(:arglist, index))
    env[match] = value

    if hash? target
      env[tar_variable] = hash_insert target.deep_clone, index, value
    end
  elsif method.to_s[-1,1] == "="
    value = exp[3][1] = process(exp[3][1])
    #This is what we'll replace with the value
    match = Sexp.new(:call, target, method.to_s[0..-2].to_sym, Sexp.new(:arglist))

    if @inside_if and val = env[match]
      if val != value
        env[match] = Sexp.new(:or, env[match], value)
      end
    else
      env[match] = value
    end
  else
    raise "Unrecognized assignment: #{exp}"
  end
  exp
end

- (Object) process_block(exp)

Start new scope for block.



183
184
185
186
187
# File 'lib/brakeman/processors/alias_processor.rb', line 183

def process_block exp
  env.scope do
    process_default exp
  end
end

- (Object) process_call(exp)

Process a method call.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/brakeman/processors/alias_processor.rb', line 100

def process_call exp
  target_var = exp[1]
  exp = process_default exp

  #In case it is replaced with something else
  return exp unless call? exp

  target = exp[1]
  method = exp[2]
  args = exp[3]

  #See if it is possible to simplify some basic cases
  #of addition/concatenation.
  case method
  when :+
    if array? target and array? args[1]
      joined = join_arrays target, args[1] 
      joined.line(exp.line)
      exp = joined
    elsif string? target and string? args[1]
      joined = join_strings target, args[1]
      joined.line(exp.line)
      exp = joined
    elsif number? target and number? args[1]
      exp = Sexp.new(:lit, target[1] + args[1][1])
    end
  when :-
    if number? target and number? args[1]
      exp = Sexp.new(:lit, target[1] - args[1][1])
    end
  when :*
    if number? target and number? args[1]
      exp = Sexp.new(:lit, target[1] * args[1][1])
    end
  when :/
    if number? target and number? args[1]
      exp = Sexp.new(:lit, target[1] / args[1][1])
    end
  when :[]
    if array? target
      temp_exp = process_array_access target, args[1..-1]
      exp = temp_exp if temp_exp
    elsif hash? target
      temp_exp = process_hash_access target, args[1..-1]
      exp = temp_exp if temp_exp
    end
  when :merge!, :update
    if hash? target and hash? args[1]
       target = process_hash_merge! target, args[1]
       env[target_var] = target
       return target
    end
  when :merge
    if hash? target and hash? args[1]
      return process_hash_merge(target, args[1])
    end
  when :<<
    if string? target and string? args[1]
      target[1] << args[1][1]
      env[target_var] = target
      return target
    elsif array? target
      target << args[1]
      env[target_var] = target
      return target
    else
      target = find_push_target exp
      env[target] = exp unless target.nil? #Happens in TemplateAliasProcessor
    end
  end

  exp
end

- (Object) process_cdecl(exp)

Constant assignments like BIG_CONSTANT = 234810983



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/brakeman/processors/alias_processor.rb', line 374

def process_cdecl exp
  if sexp? exp[2]
    exp[2] = process exp[2]
  end

  if exp[1].is_a? Symbol
    match = Sexp.new(:const, exp[1])
  else
    match = exp[1]
  end

  env[match] = exp[2]

  exp
end

- (Object) process_cvdecl(exp)

Class variable assignment @@x = 1



263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/brakeman/processors/alias_processor.rb', line 263

def process_cvdecl exp
  match = Sexp.new(:cvar, exp[1])
  value = exp[2] = process(exp[2])
  
  if @inside_if and val = env[match]
    if val != value
      env[match] = Sexp.new(:or, env[match], value)
    end
  else
    env[match] = value
  end

  exp
end

- (Object) process_default(exp)

Process a Sexp. If the Sexp has a value associated with it in the environment, that value will be returned.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/brakeman/processors/alias_processor.rb', line 73

def process_default exp
  begin
    type = exp.shift
    exp.each_with_index do |e, i|
      if sexp? e and not e.empty?
        exp[i] = process e
      else
        e
      end
    end
  rescue Exception => err
    @tracker.error err if @tracker
  ensure
    #The type must be put back on, or else later processing
    #will trip up on it
    exp.unshift type
  end

  #Generic replace
  if replacement = env[exp]
    set_line replacement.deep_clone, exp.line
  else
    exp
  end
end

- (Object) process_gasgn(exp)

Global assignment $x = 1



246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/brakeman/processors/alias_processor.rb', line 246

def process_gasgn exp
  match = Sexp.new(:gvar, exp[1])
  value = exp[2] = process(exp[2])

  if @inside_if and val = env[match]
    if val != value
      env[match] = Sexp.new(:or, env[match], value)
    end
  else
    env[match] = value
  end

  exp
end

- (Object) process_hash_access(target, args)

Process hash access by returning the value associated with the given arguments.



430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/brakeman/processors/alias_processor.rb', line 430

def process_hash_access target, args
  if args.length == 1
    index = args[0]
    hash_iterate(target) do |key, value|
      if key == index
        return value
      end
    end
  end

  nil
end

- (Object) process_hash_merge(hash, args)

Return a new hash Sexp with the given values merged into it.

args should be a hash Sexp as well.



329
330
331
332
333
334
335
# File 'lib/brakeman/processors/alias_processor.rb', line 329

def process_hash_merge hash, args
  hash = hash.deep_clone
  hash_iterate args do |key, replacement|
    hash_insert hash, key, replacement
  end
  hash
end

- (Object) process_hash_merge!(hash, args)

Merge values into hash when processing

h.merge! :something => "value"



316
317
318
319
320
321
322
323
324
# File 'lib/brakeman/processors/alias_processor.rb', line 316

def process_hash_merge! hash, args
  hash = hash.deep_clone
  hash_iterate args do |key, replacement|
    hash_insert hash, key, replacement
    match = Sexp.new(:call, hash, :[], Sexp.new(:arglist, key))
    env[match] = replacement
  end
  hash
end

- (Object) process_iasgn(exp)

Instance variable assignment



229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brakeman/processors/alias_processor.rb', line 229

def process_iasgn exp
  exp[2] = process exp[2]
  ivar = Sexp.new(:ivar, exp[1]).line(exp.line)

  if @inside_if and val = env[ivar]
    if val != exp[2]
      env[ivar] = Sexp.new(:or, val, exp[2]).line(exp.line)
    end
  else
    env[ivar] = exp[2]
  end

  exp
end

- (Object) process_if(exp)

Sets @inside_if = true



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/brakeman/processors/alias_processor.rb', line 391

def process_if exp
  @ignore_ifs ||= @tracker && @tracker.options[:ignore_ifs]

  condition = process exp[1]

  if true? condition
    exps = [exp[2]]
  elsif false? condition
    exps = exp[3..-1]
  else
    exps = exp[2..-1]
  end
  
  was_inside = @inside_if
  @inside_if = !@ignore_ifs

  exps.each do |e|
    process e if sexp? e
  end

  @inside_if = was_inside

  exp
end

- (Object) process_lasgn(exp)

Local assignment x = 1



212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/brakeman/processors/alias_processor.rb', line 212

def process_lasgn exp
  exp[2] = process exp[2] if sexp? exp[2]
  local = Sexp.new(:lvar, exp[1]).line(exp.line || -2)

  if @inside_if and val = env[local]
    if val != exp[2] #avoid setting to value it already is (e.g. "1 or 1")
      env[local] = Sexp.new(:or, val, exp[2]).line(exp.line || -2)
    end
  else
    env[local] = exp[2]
  end

  exp
end

- (Object) process_methdef(exp) Also known as: process_defn

Process a method definition.



190
191
192
193
194
195
196
# File 'lib/brakeman/processors/alias_processor.rb', line 190

def process_methdef exp
  env.scope do
    set_env_defaults
    process exp[3]
  end
  exp
end

- (Object) process_op_asgn1(exp)

Assignments like this x ||= 1



339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/brakeman/processors/alias_processor.rb', line 339

def process_op_asgn1 exp
  return process_default(exp) if exp[3] != :||"

  target = exp[1] = process(exp[1])
  index = exp[2][1] = process(exp[2][1])
  value = exp[4] = process(exp[4])
  match = Sexp.new(:call, target, :[], Sexp.new(:arglist, index))

  unless env[match]
    env[match] = value
  end

  exp
end

- (Object) process_op_asgn2(exp)

Assignments like this x.y ||= 1



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/brakeman/processors/alias_processor.rb', line 356

def process_op_asgn2 exp
  return process_default(exp) if exp[3] != :||"

  target = exp[1] = process(exp[1])
  value = exp[4] = process(exp[4])
  method = exp[2]

  match = Sexp.new(:call, target, method.to_s[0..-2].to_sym, Sexp.new(:arglist))

  unless env[match]
    env[match] = value
  end

  exp
end

- (Object) process_safely(src, set_env = nil)

This method processes the given Sexp, but copies it first so the original argument will not be modified.

set_env should be an instance of SexpProcessor::Environment. If provided, it will be used as the starting environment.

This method returns a new Sexp with variables replaced with their values, where possible.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/brakeman/processors/alias_processor.rb', line 42

def process_safely src, set_env = nil
  @env = Marshal.load(Marshal.dump(set_env)) if set_env
  @result = src.deep_clone
  process @result

  #Process again to propogate replaced variables and process more.
  #For example,
  #  x = [1,2]
  #  y = [3,4]
  #  z = x + y
  #
  #After first pass:
  #
  #  z = [1,2] + [3,4]
  #
  #After second pass:
  #
  #  z = [1,2,3,4]
  if set_env
    @env = set_env
  else
    @env = SexpProcessor::Environment.new
  end

  process @result

  @result
end

- (Object) process_scope(exp)

Process a new scope.



175
176
177
178
179
180
# File 'lib/brakeman/processors/alias_processor.rb', line 175

def process_scope exp
  env.scope do
    process exp[1]
  end
  exp
end

- (Object) process_selfdef(exp) Also known as: process_defs

Process a method definition on self.



199
200
201
202
203
204
205
# File 'lib/brakeman/processors/alias_processor.rb', line 199

def process_selfdef exp
  env.scope do
    set_env_defaults
    process exp[4]
  end
  exp
end

- (Object) set_line(exp, line_number)

Set line nunber for exp and every Sexp it contains. Used when replacing expressions, so warnings indicate the correct line.



470
471
472
473
474
475
476
477
478
479
480
# File 'lib/brakeman/processors/alias_processor.rb', line 470

def set_line exp, line_number
  if sexp? exp
    exp.original_line(exp.original_line || exp.line)
    exp.line line_number
    exp.each do |e|
      set_line e, line_number
    end
  end

  exp
end