Class: Brakeman::ControllerAliasProcessor

Inherits:
AliasProcessor show all
Includes:
RenderHelper
Defined in:
lib/brakeman/processors/controller_alias_processor.rb

Overview

Processes aliasing in controllers, but includes following renders in routes and putting variables into templates

Constant Summary

Constants included from Util

Util::ALL_PARAMETERS, Util::COOKIES, Util::COOKIES_SEXP, Util::PARAMETERS, Util::PARAMS_SEXP, Util::PATH_PARAMETERS, Util::QUERY_PARAMETERS, Util::REQUEST_ENV, Util::REQUEST_PARAMETERS, Util::REQUEST_PARAMS, Util::SESSION, Util::SESSION_SEXP

Constants inherited from SexpProcessor

SexpProcessor::VERSION

Instance Attribute Summary

Attributes inherited from AliasProcessor

#result

Attributes inherited from SexpProcessor

#context, #env, #expected

Instance Method Summary collapse

Methods included from RenderHelper

#get_class_target, #get_options, #process_action, #process_layout, #process_partial, #process_render

Methods inherited from AliasProcessor

#assign_args, #collapse_send_call, #duplicate?, #find_push_target, #get_call_value, #join_arrays, #join_strings, #merge_if_branch, #meth_env, #only_ivars, #only_request_vars, #process_array_access, #process_attrasgn, #process_block, #process_cdecl, #process_cvdecl, #process_default, #process_gasgn, #process_hash_access, #process_hash_merge, #process_hash_merge!, #process_helper_method, #process_iasgn, #process_if, #process_if_branch, #process_lasgn, #process_masgn, #process_op_asgn1, #process_op_asgn2, #process_or_simple_operation, #process_or_target, #process_safely, #process_scope, #process_selfdef, #process_svalue, #same_value?, #self_assign?, #self_assign_target?, #self_assign_var?, #set_value, #too_deep?, #top_target, #value_from_if

Methods included from Util

#array?, #block?, #call?, #camelize, #contains_class?, #context_for, #cookies?, #false?, #file_by_name, #file_for, #github_url, #hash?, #hash_access, #hash_insert, #hash_iterate, #integer?, #make_call, #node_type?, #number?, #params?, #pluralize, #regexp?, #relative_path, #request_env?, #request_value?, #result?, #set_env_defaults, #sexp?, #string?, #symbol?, #table_to_csv, #template_path_to_name, #true?, #truncate_table, #underscore

Methods included from ProcessorHelper

#class_name, #process_all, #process_all!, #process_call_args, #process_module

Methods inherited from SexpProcessor

#error_handler, #in_context, #process, #process_dummy, #scope

Constructor Details

#initialize(app_tree, tracker, only_method = nil) ⇒ ControllerAliasProcessor

If only_method is specified, only that method will be processed, other methods will be skipped. This is for rescanning just a single action.


13
14
15
16
17
18
19
20
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 13

def initialize app_tree, tracker, only_method = nil
  super tracker
  @app_tree = app_tree
  @only_method = only_method
  @rendered = false
  @current_class = @current_module = @current_method = nil
  @method_cache = {} #Cache method lookups
end

Instance Method Details

#before_filter_list(method, klass) ⇒ Object

Get list of filters, including those that are inherited


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

def before_filter_list method, klass
  controller = @tracker.controllers[klass]
  filters = []

  while controller
    filters = get_before_filters(method, controller) + filters

    controller = @tracker.controllers[controller[:parent]] ||
                 @tracker.libs[controller[:parent]]
  end

  remove_skipped_filters filters, method, klass
end

#before_filter_to_hash(args) ⇒ Object

Returns a before filter as a hash table


297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 297

def before_filter_to_hash args
  filter = {}

  #Process args for the uncommon but possible situation
  #in which some variables are used in the filter.
  args.each do |a|
    if sexp? a
      a = process_default a
    end
  end

  filter[:methods] = [args[0][1]]

  args[1..-1].each do |a|
    filter[:methods] << a[1] if a.node_type == :lit
  end

  if args[-1].node_type == :hash
    option = args[-1][1][1]
    value = args[-1][2]
    case value.node_type
    when :array
      filter[option] = value[1..-1].map {|v| v[1] }
    when :lit, :str
      filter[option] = value[1]
    else
      Brakeman.debug "[Notice] Unknown before_filter value: #{option} => #{value}"
    end
  else
    filter[:all] = true
  end

  filter
end

#find_method(method_name, klass) ⇒ Object

Finds a method in the given class or a parent class

Returns nil if the method could not be found.

If found, returns hash table with controller name and method sexp.


337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 337

def find_method method_name, klass
  return nil if sexp? method_name
  method_name = method_name.to_sym

  if method = @method_cache[method_name]
    return method
  end

  controller = @tracker.controllers[klass]
  controller ||= @tracker.libs[klass]

  if klass and controller
    method = controller[:public][method_name]
    method ||= controller[:private][method_name]
    method ||= controller[:protected][method_name]

    if method.nil?
      controller[:includes].each do |included|
        method = find_method method_name, included
        if method
          @method_cache[method_name] = method
          return method
        end
     end

      @method_cache[method_name] = find_method method_name, controller[:parent]
    else
      @method_cache[method_name] = { :controller => controller[:name], :method => method[:src] }
    end
  else
    nil
  end
end

#get_before_filters(method, controller) ⇒ Object

Returns an array of filter names


267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 267

def get_before_filters method, controller
  return [] unless controller[:options] and controller[:options][:before_filters]

  filters = []

  if controller[:before_filter_cache].nil?
    filter_cache = []

    controller[:options][:before_filters].each do |filter|
      filter_cache << before_filter_to_hash(filter.args)
    end

    controller[:before_filter_cache] = filter_cache
  end

  controller[:before_filter_cache].each do |f|
    if f[:all] or
      (f[:only] == method) or
      (f[:only].is_a? Array and f[:only].include? method) or
      (f[:except].is_a? Symbol and f[:except] != method) or
      (f[:except].is_a? Array and not f[:except].include? method)

      filters.concat f[:methods]
    end
  end

  filters
end

#get_skipped_filters(method, controller) ⇒ Object


241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 241

def get_skipped_filters method, controller
  return [] unless controller[:options] and controller[:options][:skip_filters]

  filters = []

  if controller[:skip_filter_cache].nil?
    controller[:skip_filter_cache] = controller[:options][:skip_filters].map do |filter|
      before_filter_to_hash(filter.args)
    end
  end

  controller[:skip_filter_cache].each do |f|
    if f[:all] or
      (f[:only] == method) or
      (f[:only].is_a? Array and f[:only].include? method) or
      (f[:except].is_a? Symbol and f[:except] != method) or
      (f[:except].is_a? Array and not f[:except].include? method)

      filters.concat f[:methods]
    end
  end

  filters
end

#layout_nameObject

Determines default layout name


190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 190

def layout_name
  controller = @tracker.controllers[@current_class]

  return controller[:layout] if controller[:layout]
  return false if controller[:layout] == false

  app_controller = @tracker.controllers[:ApplicationController]

  return app_controller[:layout] if app_controller and app_controller[:layout]

  nil
end

#process_before_filter(name) ⇒ Object

Processes a call to a before filter. Basically, adds any instance variable assignments to the environment. TODO: method arguments?


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
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 137

def process_before_filter name
  filter = find_method name, @current_class

  if filter.nil?
    Brakeman.debug "[Notice] Could not find filter #{name}"
    return
  end

  method = filter[:method]

  if ivars = @tracker.filter_cache[[filter[:controller], name]]
    ivars.each do |variable, value|
      env[variable] = value
    end
  else
    processor = Brakeman::AliasProcessor.new @tracker
    processor.process_safely(method.body_list, only_ivars(:include_request_vars))

    ivars = processor.only_ivars(:include_request_vars).all

    @tracker.filter_cache[[filter[:controller], name]] = ivars

    ivars.each do |variable, value|
      env[variable] = value
    end
  end
end

#process_call(exp) ⇒ Object

Look for calls to head()


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 106

def process_call exp
  exp = super
  return exp unless call? exp

  method = exp.method

  if method == :head
    @rendered = true
  elsif @tracker.options[:interprocedural] and
    @current_method and (exp.target.nil? or exp.target.node_type == :self)

    exp = get_call_value(exp)
  end

  exp
end

#process_call_with_block(exp) ⇒ Object

Check for respond_to


124
125
126
127
128
129
130
131
132
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 124

def process_call_with_block exp
  super

  if call? exp.block_call and exp.block_call.method == :respond_to
    @rendered = true
  end

  exp
end

#process_class(exp) ⇒ Object

Skip it, must be an inner class


67
68
69
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 67

def process_class exp
  exp
end

#process_controller(name, src) ⇒ Object


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

def process_controller name, src
  if not node_type? src, :class
    Brakeman.debug "#{name} is not a class, it's a #{src.node_type}"
    return
  else
    @current_class = name

    process_default src

    process_mixins
  end
end

#process_default_render(exp) ⇒ Object

Processes the default template for the current action


166
167
168
169
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 166

def process_default_render exp
  process_layout
  process_template template_name, nil
end

#process_methdef(exp) ⇒ Object

Processes a method definition, which may include processing any rendered templates.


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
98
99
100
101
102
103
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 73

def process_methdef exp
  meth_name = exp.method_name

  Brakeman.debug "Processing #{@current_class}##{meth_name}"

  #Skip if instructed to only process a specific method
  #(but don't skip if this method was called from elsewhere)
  return exp if @current_method.nil? and @only_method and @only_method != meth_name

  is_route = route? meth_name
  other_method = @current_method
  @current_method = meth_name
  @rendered = false if is_route

  meth_env do
    if is_route
      before_filter_list(@current_method, @current_class).each do |f|
        process_before_filter f
      end
    end

    process_all exp.body

    if is_route and not @rendered
      process_default_render exp
    end
  end

  @current_method = other_method
  exp
end

#process_mixinsObject

Process modules mixed into the controller, in case they contain actions.


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 36

def process_mixins
  controller = @tracker.controllers[@current_class]

  controller[:includes].each do |i|
    mixin = @tracker.libs[i]

    next unless mixin

    #Process methods in alphabetical order for consistency
    methods = mixin[:public].keys.map { |n| n.to_s }.sort.map { |n| n.to_sym }

    methods.each do |name|
      #Need to process the method like it was in a controller in order
      #to get the renders set
      processor = Brakeman::ControllerProcessor.new(@app_tree, @tracker)
      method = mixin[:public][name][:src].deep_clone

      if node_type? method, :methdef
        method = processor.process_defn method
      else
        #Should be a methdef, but this will catch other cases
        method = processor.process method
      end

      #Then process it like any other method in the controller
      process method
    end
  end
end

#process_template(name, args) ⇒ Object

Process template and add the current class and method name as called_from info


172
173
174
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 172

def process_template name, args
  super name, args, ["#@current_class##@current_method"]
end

#remove_skipped_filters(filters, method, klass) ⇒ Object


228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 228

def remove_skipped_filters filters, method, klass
  controller = @tracker.controllers[klass]

  while controller
    filters = filters - get_skipped_filters(method, controller)

    controller = @tracker.controllers[controller[:parent]] ||
                 @tracker.libs[controller[:parent]]
  end

  filters
end

#route?(method) ⇒ Boolean

Returns true if the given method name is also a route

Returns:

  • (Boolean)

204
205
206
207
208
209
210
211
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 204

def route? method
  if @tracker.routes[:allow_all_actions] or @tracker.options[:assume_all_routes]
    true
  else
    routes = @tracker.routes[@current_class]
    routes and (routes.include? :allow_all_actions or routes.include? method)
  end
end

#template_name(name = nil) ⇒ Object

Turns a method name into a template name


177
178
179
180
181
182
183
184
185
186
187
# File 'lib/brakeman/processors/controller_alias_processor.rb', line 177

def template_name name = nil
  name ||= @current_method
  name = name.to_s
  if name.include? "/"
    name
  else
    controller = @current_class.to_s.gsub("Controller", "")
    controller.gsub!("::", "/")
    underscore(controller + "/" + name.to_s)
  end
end