Class: Brakeman::ControllerProcessor

Inherits:
BaseProcessor show all
Includes:
ModuleHelper
Defined in:
lib/brakeman/processors/controller_processor.rb

Overview

Processes controller. Results are put in tracker.controllers

Constant Summary collapse

FORMAT_HTML =
Sexp.new(:call, Sexp.new(:lvar, :format), :html)

Constants inherited from BaseProcessor

BaseProcessor::IGNORE

Constants included from Util

Util::ALL_COOKIES, Util::ALL_PARAMETERS, Util::COOKIES, Util::COOKIES_SEXP, Util::DIR_CONST, Util::LITERALS, Util::PARAMETERS, Util::PARAMS_SEXP, Util::PATH_PARAMETERS, Util::QUERY_PARAMETERS, Util::REQUEST_COOKIES, Util::REQUEST_ENV, Util::REQUEST_PARAMETERS, Util::REQUEST_PARAMS, Util::REQUEST_REQUEST_PARAMETERS, Util::SAFE_LITERAL, Util::SESSION, Util::SESSION_SEXP, Util::SIMPLE_LITERALS

Constants inherited from SexpProcessor

SexpProcessor::VERSION

Instance Attribute Summary

Attributes inherited from SexpProcessor

#context, #env, #expected

Instance Method Summary collapse

Methods included from ModuleHelper

#handle_class, #handle_module, #make_defs, #process_defn, #process_defs, #process_sclass

Methods inherited from BaseProcessor

#find_render_type, #ignore, #make_inline_render, #make_render, #make_render_in_view, #process_arglist, #process_attrasgn, #process_block, #process_cdecl, #process_default, #process_dstr, #process_evstr, #process_file, #process_hash, #process_if, #process_ignore, #process_lasgn, #process_scope

Methods included from Util

#all_literals?, #array?, #block?, #call?, #camelize, #class_name, #constant?, #contains_class?, #cookies?, #dir_glob?, #false?, #hash?, #hash_access, #hash_insert, #hash_iterate, #hash_values, #integer?, #kwsplat?, #literal?, #make_call, #node_type?, #number?, #params?, #pluralize, #rails_version, #recurse_check?, #regexp?, #remove_kwsplat, #request_headers?, #request_value?, #result?, #safe_literal, #safe_literal?, #safe_literal_target?, #set_env_defaults, #sexp?, #simple_literal?, #string?, #string_interp?, #symbol?, #template_path_to_name, #true?, #underscore

Methods included from ProcessorHelper

#current_file, #process_all, #process_all!, #process_call_args, #process_call_defn?

Methods inherited from SexpProcessor

#in_context, #process, processors, #scope

Constructor Details

#initialize(tracker, current_file = nil) ⇒ ControllerProcessor

Returns a new instance of ControllerProcessor.



11
12
13
14
15
16
# File 'lib/brakeman/processors/controller_processor.rb', line 11

def initialize tracker, current_file = nil
  super(tracker)
  @visibility = :public
  @current_file = current_file
  @concerns = Set.new
end

Instance Method Details

#add_fake_filter(exp) ⇒ Object

This is to handle before_filter do |controller| … end

We build a new method and process that the same way as usual methods and filters.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/brakeman/processors/controller_processor.rb', line 187

def add_fake_filter exp
  unless @current_class
    Brakeman.debug "Skipping before_filter outside controller: #{exp}"
    return exp
  end

  filter_name = ("fake_filter" + rand.to_s[/\d+$/]).to_sym
  args = exp.block_call.arglist
  args.insert(1, Sexp.new(:lit, filter_name).line(exp.line))
  before_filter_call = make_call(nil, :before_filter, args).line(exp.line)

  if exp.block_args.length > 1
    block_variable = exp.block_args[1]
  else
    block_variable = :temp
  end

  if node_type? exp.block, :block
    block_inner = exp.block.sexp_body
  else
    block_inner = [exp.block]
  end

  #Build Sexp for filter method
  body = Sexp.new(:lasgn,
                  block_variable,
                  Sexp.new(:call, Sexp.new(:const, @current_class.name).line(exp.line), :new).line(exp.line)).line(exp.line)

  filter_method = Sexp.new(:defn, filter_name, Sexp.new(:args).line(exp.line), body).concat(block_inner).line(exp.line)

  vis = @visibility
  @visibility = :private
  process_defn filter_method
  @visibility = vis
  process before_filter_call
  exp
end

#add_lambda_filter(exp) ⇒ Object



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/brakeman/processors/controller_processor.rb', line 225

def add_lambda_filter exp
  # Convert into regular block call
  e = exp.dup
  lambda_node = e.delete_at(3)
  result = Sexp.new(:iter, e).line(e.line)

  # Add block arguments
  if node_type? lambda_node[2], :args
    result << lambda_node[2].last
  else
    result << s(:args)
  end

  # Add block contents
  if sexp? lambda_node[3]
    result << lambda_node[3]
  end

  add_fake_filter result
end

#process_call(exp) ⇒ Object

Look for specific calls inside the controller



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

def process_call exp
  return exp if process_call_defn? exp

  target = exp.target
  if sexp? target
    target = process target
  end

  method = exp.method
  first_arg = exp.first_arg
  last_arg = exp.last_arg

  #Methods called inside class definition
  #like attr_* and other settings
  if @current_method.nil? and target.nil? and @current_class
    if first_arg.nil? #No args
      case method
      when :private, :protected, :public
        @visibility = method
      when :protect_from_forgery
        @current_class.options[:protect_from_forgery] = true
      else
        #??
      end
    else
      case method
      when :include
        if @current_class
          concern = class_name(first_arg)
          @current_class.add_include concern
          process_concern concern
        end
      when :before_filter, :append_before_filter, :before_action, :append_before_action
        if node_type? exp.first_arg, :iter
          add_lambda_filter exp
        else
          @current_class.add_before_filter exp
        end
      when :prepend_before_filter, :prepend_before_action
        if node_type? exp.first_arg, :iter
          add_lambda_filter exp
        else
          @current_class.prepend_before_filter exp
        end
      when :skip_before_filter, :skip_filter, :skip_before_action, :skip_action_callback
        @current_class.skip_filter exp
      when :layout
        if string? last_arg
          #layout "some_layout"

          name = last_arg.value.to_s
          if @app_tree.layout_exists?(name)
            @current_class.layout = "layouts/#{name}"
          else
            Brakeman.debug "[Notice] Layout not found: #{name}"
          end
        elsif node_type? last_arg, :nil, :false
          #layout :false or layout nil
          @current_class.layout = false
        end
      else
        @current_class.add_option method, exp
      end
    end

    exp
  elsif target == nil and method == :render
    make_render exp
  elsif exp == FORMAT_HTML and context[1] != :iter
    #This is an empty call to
    # format.html
    #Which renders the default template if no arguments
    #Need to make more generic, though.
    call = Sexp.new :render, :default, @current_method
    call.line(exp.line)
    call
  else
    call = make_call target, method, process_all!(exp.args)
    call.line(exp.line)
    call
  end
end

#process_class(exp) ⇒ Object

s(:class, NAME, PARENT, s(:scope …))



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/brakeman/processors/controller_processor.rb', line 25

def process_class exp
  name = class_name(exp.class_name)
  parent = class_name(exp.parent_name)

  #If inside a real controller, treat any other classes as libraries.
  #But if not inside a controller already, then the class may include
  #a real controller, so we can't take this shortcut.
  if @current_class and @current_class.name.to_s.end_with? "Controller"
    Brakeman.debug "[Notice] Treating inner class as library: #{name}"
    Brakeman::LibraryProcessor.new(@tracker).process_library exp, @current_file
    return exp
  end

  if not name.to_s.end_with? "Controller"
    Brakeman.debug "[Notice] Adding noncontroller as library: #{name}"
    #Set the class to be a module in order to get the right namespacing.
    #Add class to libraries, in case it is needed later (e.g. it's used
    #as a parent class for a controller.)
    #However, still want to process it in this class, so have to set
    #@current_class to this not-really-a-controller thing.
    process_module exp, parent

    return exp
  end

  handle_class(exp, @tracker.controllers, Brakeman::Controller) do
    set_layout_name
  end

  exp
end

#process_concern(concern_name) ⇒ Object



61
62
63
64
65
66
67
68
69
70
# File 'lib/brakeman/processors/controller_processor.rb', line 61

def process_concern concern_name
  return unless @current_class

  if mod = @tracker.find_class(concern_name)
    if mod.options[:included] and not @concerns.include? concern_name
      @concerns << concern_name
      process mod.options[:included].deep_clone
    end
  end
end

#process_controller(src, current_file = @current_file) ⇒ Object

Use this method to process a Controller



19
20
21
22
# File 'lib/brakeman/processors/controller_processor.rb', line 19

def process_controller src, current_file = @current_file
  @current_file = current_file
  process src
end

#process_iter(exp) ⇒ Object

Look for before_filters and add fake ones if necessary



157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/brakeman/processors/controller_processor.rb', line 157

def process_iter exp
  if @current_method.nil? and call? exp.block_call
    block_call_name = exp.block_call.method

    if block_call_name == :before_filter  or block_call_name == :before_action
      add_fake_filter exp
    else
      super
    end
  else
    super
  end
end

#process_module(exp, parent = nil) ⇒ Object



57
58
59
# File 'lib/brakeman/processors/controller_processor.rb', line 57

def process_module exp, parent = nil
  handle_module exp, Brakeman::Controller, parent
end

#set_layout_nameObject

Sets default layout for renders inside Controller



172
173
174
175
176
177
178
179
180
181
# File 'lib/brakeman/processors/controller_processor.rb', line 172

def set_layout_name
  return if @current_class.layout

  name = underscore(@current_class.name.to_s.split("::")[-1].gsub("Controller", ''))

  #There is a layout for this Controller
  if @app_tree.layout_exists?(name)
    @current_class.layout = "layouts/#{name}"
  end
end