Module: Brakeman::Util

Overview

This is a mixin containing utility methods.

Constant Summary collapse

QUERY_PARAMETERS =
Sexp.new(:call, Sexp.new(:call, nil, :request), :query_parameters)
PATH_PARAMETERS =
Sexp.new(:call, Sexp.new(:call, nil, :request), :path_parameters)
REQUEST_REQUEST_PARAMETERS =
Sexp.new(:call, Sexp.new(:call, nil, :request), :request_parameters)
REQUEST_PARAMETERS =
Sexp.new(:call, Sexp.new(:call, nil, :request), :parameters)
REQUEST_PARAMS =
Sexp.new(:call, Sexp.new(:call, nil, :request), :params)
REQUEST_ENV =
Sexp.new(:call, Sexp.new(:call, nil, :request), :env)
PARAMETERS =
Sexp.new(:call, nil, :params)
COOKIES =
Sexp.new(:call, nil, :cookies)
REQUEST_COOKIES =
s(:call, s(:call, nil, :request), :cookies)
SESSION =
Sexp.new(:call, nil, :session)
ALL_PARAMETERS =
Set[PARAMETERS, QUERY_PARAMETERS, PATH_PARAMETERS, REQUEST_REQUEST_PARAMETERS, REQUEST_PARAMETERS, REQUEST_PARAMS]
ALL_COOKIES =
Set[COOKIES, REQUEST_COOKIES]
SAFE_LITERAL =
s(:lit, :BRAKEMAN_SAFE_LITERAL)
PARAMS_SEXP =

These are never modified

Sexp.new(:params)
SESSION_SEXP =
Sexp.new(:session)
COOKIES_SEXP =
Sexp.new(:cookies)
SIMPLE_LITERALS =
[:lit, :false, :str, :true]
LITERALS =
[*SIMPLE_LITERALS, :array, :hash]
DIR_CONST =
s(:const, :Dir)

Instance Method Summary collapse

Instance Method Details

#all_literals?(exp, expected_type = :array) ⇒ Boolean

Returns:

  • (Boolean)


326
327
328
329
330
# File 'lib/brakeman/util.rb', line 326

def all_literals? exp, expected_type = :array
  node_type? exp, expected_type and
    exp.length > 1 and
    exp.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
end

#array?(exp) ⇒ Boolean

Check if exp represents an array: s(:array, […])

Returns:

  • (Boolean)


178
179
180
# File 'lib/brakeman/util.rb', line 178

def array? exp
  exp.is_a? Sexp and exp.node_type == :array
end

#block?(exp) ⇒ Boolean

Check if exp represents a block of code

Returns:

  • (Boolean)


236
237
238
239
# File 'lib/brakeman/util.rb', line 236

def block? exp
  exp.is_a? Sexp and (exp.node_type == :block or
                      exp.node_type == :rlist)
end

#call?(exp) ⇒ Boolean

Check if exp represents a method call: s(:call, …)

Returns:

  • (Boolean)


197
198
199
200
# File 'lib/brakeman/util.rb', line 197

def call? exp
  exp.is_a? Sexp and
    (exp.node_type == :call or exp.node_type == :safe_call)
end

#camelize(lower_case_and_underscored_word) ⇒ Object

Convert a string from “something_like_this” to “SomethingLikeThis”

Taken from ActiveSupport.



36
37
38
# File 'lib/brakeman/util.rb', line 36

def camelize lower_case_and_underscored_word
  lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
end

#class_name(exp) ⇒ Object

Returns a class name as a Symbol. If class name cannot be determined, returns exp.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/brakeman/util.rb', line 62

def class_name exp
  case exp
  when Sexp
    case exp.node_type
    when :const, :colon3
      exp.value
    when :lvar
      exp.value.to_sym
    when :colon2
      "#{class_name(exp.lhs)}::#{exp.rhs}".to_sym
    when :self
      @current_class || @current_module || nil
    else
      exp
    end
  when Symbol
    exp
  when nil
    nil
  else
    exp
  end
end

#constant?(exp) ⇒ Boolean

Returns:

  • (Boolean)


293
294
295
# File 'lib/brakeman/util.rb', line 293

def constant? exp
  node_type? exp, :const, :colon2, :colon3
end

#contains_class?(exp) ⇒ Boolean

Returns true if the given exp contains a :class node.

Useful for checking if a module is just a module or if it is a namespace.

Returns:

  • (Boolean)


345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/brakeman/util.rb', line 345

def contains_class? exp
  todo = [exp]

  until todo.empty?
    current = todo.shift

    if node_type? current, :class
      return true
    elsif sexp? current
      todo = current.sexp_body.concat todo
    end
  end

  false
end

#cookies?(exp) ⇒ Boolean

Returns:

  • (Boolean)


246
247
248
# File 'lib/brakeman/util.rb', line 246

def cookies? exp
  recurse_check?(exp) { |child| child.node_type == :cookies or ALL_COOKIES.include? child }
end

#dir_glob?(exp) ⇒ Boolean

Dir.glob(…).whatever

Returns:

  • (Boolean)


335
336
337
338
339
340
# File 'lib/brakeman/util.rb', line 335

def dir_glob? exp
  exp = exp.block_call if node_type? exp, :iter
  return unless call? exp

  (exp.target == DIR_CONST and exp.method == :glob) or dir_glob? exp.target
end

#false?(exp) ⇒ Boolean

Check if exp represents a :false or :nil node

Returns:

  • (Boolean)


230
231
232
233
# File 'lib/brakeman/util.rb', line 230

def false? exp
  exp.is_a? Sexp and (exp.node_type == :false or
                      exp.node_type == :nil)
end

#hash?(exp) ⇒ Boolean

Check if exp represents a hash: s(:hash, …) This also includes pseudo hashes params, session, and cookies.

Returns:

  • (Boolean)


170
171
172
173
174
175
# File 'lib/brakeman/util.rb', line 170

def hash? exp
  exp.is_a? Sexp and (exp.node_type == :hash or
                      exp.node_type == :params or
                      exp.node_type == :session or
                      exp.node_type == :cookies)
end

#hash_access(hash, key) ⇒ Object

Get value from hash using key.

If key is a Symbol, it will be converted to a Sexp(:lit, key).



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/brakeman/util.rb', line 135

def hash_access hash, key
  if key.is_a? Symbol
    key = Sexp.new(:lit, key)
  end

  if index = hash.find_index(key) and index > 0
    return hash[index + 1]
  end

  nil
end

#hash_insert(hash, key, value) ⇒ Object

Insert value into Hash Sexp



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/brakeman/util.rb', line 117

def hash_insert hash, key, value
  index = 1
  hash_iterate hash.dup do |k,v|
    if k == key
      hash[index + 1] = value
      return hash
    end
    index += 2
  end

  hash << key << value

  hash
end

#hash_iterate(hash) ⇒ Object

Takes an Sexp like (:hash, (:lit, :key), (:str, “value”)) and yields the key and value pairs to the given block.

For example:

h = Sexp.new(:hash, (:lit, :name), (:str, “bob”), (:lit, :name), (:str, “jane”)) names = [] hash_iterate(h) do |key, value|

if symbol? key and key[1] == :name
  names << value[1]
end

end names #[“bob”]



100
101
102
103
104
105
106
# File 'lib/brakeman/util.rb', line 100

def hash_iterate hash
  hash = remove_kwsplat(hash)

  1.step(hash.length - 1, 2) do |i|
    yield hash[i], hash[i + 1]
  end
end

#hash_values(hash) ⇒ Object



147
148
149
150
151
152
153
# File 'lib/brakeman/util.rb', line 147

def hash_values hash
  values = hash.each_sexp.each_slice(2).map do |_, value|
    value
  end

  Sexp.new(:array).concat(values).line(hash.line)
end

#integer?(exp) ⇒ Boolean

Check if exp represents an Integer: s(:lit, …)

Returns:

  • (Boolean)


208
209
210
# File 'lib/brakeman/util.rb', line 208

def integer? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Integer
end

#kwsplat?(exp) ⇒ Boolean

Returns:

  • (Boolean)


297
298
299
300
301
302
# File 'lib/brakeman/util.rb', line 297

def kwsplat? exp
  exp.is_a? Sexp and
    exp.node_type == :hash and
    exp[1].is_a? Sexp and
    exp[1].node_type == :kwsplat
end

#literal?(exp) ⇒ Boolean

Returns:

  • (Boolean)


322
323
324
# File 'lib/brakeman/util.rb', line 322

def literal? exp
  exp.is_a? Sexp and LITERALS.include? exp.node_type
end

#make_call(target, method, *args) ⇒ Object



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/brakeman/util.rb', line 361

def make_call target, method, *args
  call = Sexp.new(:call, target, method)

  if args.empty? or args.first.empty?
    #nothing to do
  elsif node_type? args.first, :arglist
    call.concat args.first.sexp_body
  elsif args.first.node_type.is_a? Sexp #just a list of args
    call.concat args.first
  else
    call.concat args
  end

  call
end

#node_type?(exp, *types) ⇒ Boolean

Check if exp is a Sexp and the node type matches one of the given types.

Returns:

  • (Boolean)


310
311
312
# File 'lib/brakeman/util.rb', line 310

def node_type? exp, *types
  exp.is_a? Sexp and types.include? exp.node_type
end

#number?(exp) ⇒ Boolean

Check if exp represents a number: s(:lit, …)

Returns:

  • (Boolean)


213
214
215
# File 'lib/brakeman/util.rb', line 213

def number? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Numeric
end

#params?(exp) ⇒ Boolean

Check if exp is a params hash

Returns:

  • (Boolean)


242
243
244
# File 'lib/brakeman/util.rb', line 242

def params? exp
  recurse_check?(exp) { |child| child.node_type == :params or ALL_PARAMETERS.include? child }
end

#pluralize(word) ⇒ Object

stupid simple, used to delegate to ActiveSupport



52
53
54
55
56
57
58
# File 'lib/brakeman/util.rb', line 52

def pluralize word
  if word.end_with? 's'
    word + 'es'
  else
    word + 's'
  end
end

#rails_versionObject



393
394
395
# File 'lib/brakeman/util.rb', line 393

def rails_version
  @tracker.config.rails_version
end

#recurse_check?(exp, &check) ⇒ Boolean

Returns:

  • (Boolean)


250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/brakeman/util.rb', line 250

def recurse_check? exp, &check
  if exp.is_a? Sexp
    return true if yield(exp)

    if call? exp
      if recurse_check? exp[1], &check
        return true
      elsif exp[2] == :[]
        return recurse_check? exp[1], &check
      end
    end
  end

  false
end

#regexp?(exp) ⇒ Boolean

Check if exp represents a Regexp: s(:lit, /…/)

Returns:

  • (Boolean)


203
204
205
# File 'lib/brakeman/util.rb', line 203

def regexp? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Regexp
end

#remove_kwsplat(exp) ⇒ Object



108
109
110
111
112
113
114
# File 'lib/brakeman/util.rb', line 108

def remove_kwsplat exp
  if exp.any? { |e| node_type? e, :kwsplat }
    exp.reject { |e| node_type? e, :kwsplat }
  else
    exp
  end
end

#request_headers?(exp) ⇒ Boolean

Only return true when accessing request headers via request.env

Returns:

  • (Boolean)


267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/brakeman/util.rb', line 267

def request_headers? exp
  return unless sexp? exp

  if exp[1] == REQUEST_ENV
    if exp.method == :[]
      if string? exp.first_arg
        # Only care about HTTP headers, which are prefixed by 'HTTP_'
        exp.first_arg.value.start_with?('HTTP_'.freeze)
      else
        true # request.env[something]
      end
    else
      false # request.env.something
    end
  else
    false
  end
end

#request_value?(exp) ⇒ Boolean

Check if exp is params, cookies, or request_headers

Returns:

  • (Boolean)


287
288
289
290
291
# File 'lib/brakeman/util.rb', line 287

def request_value? exp
  params? exp or
  cookies? exp or
  request_headers? exp
end

#result?(exp) ⇒ Boolean

Check if exp represents a result: s(:result, …)

Returns:

  • (Boolean)


218
219
220
# File 'lib/brakeman/util.rb', line 218

def result? exp
  exp.is_a? Sexp and exp.node_type == :result
end

#safe_literal(line = nil) ⇒ Object



377
378
379
# File 'lib/brakeman/util.rb', line 377

def safe_literal line = nil
  s(:lit, :BRAKEMAN_SAFE_LITERAL).line(line || 0)
end

#safe_literal?(exp) ⇒ Boolean

Returns:

  • (Boolean)


381
382
383
# File 'lib/brakeman/util.rb', line 381

def safe_literal? exp
  exp == SAFE_LITERAL
end

#safe_literal_target?(exp) ⇒ Boolean

Returns:

  • (Boolean)


385
386
387
388
389
390
391
# File 'lib/brakeman/util.rb', line 385

def safe_literal_target? exp
  if call? exp
    safe_literal_target? exp.target
  else
    safe_literal? exp
  end
end

#set_env_defaultsObject

Adds params, session, and cookies to environment so they can be replaced by their respective Sexps.



162
163
164
165
166
# File 'lib/brakeman/util.rb', line 162

def set_env_defaults
  @env[PARAMETERS] = PARAMS_SEXP
  @env[SESSION] = SESSION_SEXP
  @env[COOKIES] = COOKIES_SEXP
end

#sexp?(exp) ⇒ Boolean

Check if exp is a Sexp.

Returns:

  • (Boolean)


305
306
307
# File 'lib/brakeman/util.rb', line 305

def sexp? exp
  exp.is_a? Sexp
end

#simple_literal?(exp) ⇒ Boolean

Returns:

  • (Boolean)


316
317
318
# File 'lib/brakeman/util.rb', line 316

def simple_literal? exp
  exp.is_a? Sexp and SIMPLE_LITERALS.include? exp.node_type
end

#string?(exp) ⇒ Boolean

Check if exp represents a String: s(:str, “…”)

Returns:

  • (Boolean)


183
184
185
# File 'lib/brakeman/util.rb', line 183

def string? exp
  exp.is_a? Sexp and exp.node_type == :str
end

#string_interp?(exp) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/brakeman/util.rb', line 187

def string_interp? exp
  exp.is_a? Sexp and exp.node_type == :dstr
end

#symbol?(exp) ⇒ Boolean

Check if exp represents a Symbol: s(:lit, :…)

Returns:

  • (Boolean)


192
193
194
# File 'lib/brakeman/util.rb', line 192

def symbol? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Symbol
end

#template_path_to_name(path) ⇒ Object

Convert path/filename to view name

views/test/something.html.erb -> test/something



400
401
402
403
404
405
406
407
408
409
# File 'lib/brakeman/util.rb', line 400

def template_path_to_name path
  names = path.relative.split('/')
  names.last.gsub!(/(\.(html|js)\..*|\.(rhtml|haml|erb|slim))$/, '')

  if names.include? 'views'
    names[(names.index('views') + 1)..-1]
  else
    names
  end.join('/').to_sym
end

#true?(exp) ⇒ Boolean

Check if exp represents a :true, :lit, or :string node

Returns:

  • (Boolean)


223
224
225
226
227
# File 'lib/brakeman/util.rb', line 223

def true? exp
  exp.is_a? Sexp and (exp.node_type == :true or
                      exp.node_type == :lit or
                      exp.node_type == :string)
end

#underscore(camel_cased_word) ⇒ Object

Convert a string from “Something::LikeThis” to “something/like_this”

Taken from ActiveSupport.



43
44
45
46
47
48
49
# File 'lib/brakeman/util.rb', line 43

def underscore camel_cased_word
  camel_cased_word.to_s.gsub(/::/, '/').
    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
    gsub(/([a-z\d])([A-Z])/,'\1_\2').
    tr("-", "_").
    downcase
end