Module: Brakeman::RenderHelper

Included in:
ControllerAliasProcessor, SlimTemplateProcessor, TemplateAliasProcessor
Defined in:
lib/brakeman/processors/lib/render_helper.rb

Overview

Processes a call to render() in a controller or template

Constant Summary collapse

SINGLE_RECORD =
[:first, :find, :last, :new]
COLLECTION =
[:all, :where]

Instance Method Summary collapse

Instance Method Details

#get_class_target(sexp) ⇒ Object



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

def get_class_target sexp
  if call? sexp
    get_class_target sexp.target
  else
    klass = class_name sexp
    if klass.is_a? Symbol
      klass
    else
      nil
    end
  end
end

#get_options(args) ⇒ Object

Turn options Sexp into hash



200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/brakeman/processors/lib/render_helper.rb', line 200

def get_options args
  options = {}
  return options unless hash? args

  hash_iterate args do |key, value|
    if symbol? key
      options[key.value] = value
    end
  end

  options
end

#process_action(name, args, line) ⇒ Object

Processes a given action



56
57
58
59
60
61
62
# File 'lib/brakeman/processors/lib/render_helper.rb', line 56

def process_action name, args, line
  if name.is_a? String or name.is_a? Symbol
    process_template template_name(name), args, nil, line
  else
    Brakeman.debug "Not processing render #{name.inspect}"
  end
end

#process_layout(name = nil) ⇒ Object

Processes layout



34
35
36
37
38
39
40
41
42
# File 'lib/brakeman/processors/lib/render_helper.rb', line 34

def process_layout name = nil
  if name.nil? and defined? layout_name
    name = layout_name
  end

  return unless name

  process_template name, nil, nil, nil
end

#process_model_action(action, args) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/brakeman/processors/lib/render_helper.rb', line 67

def process_model_action action, args
  return unless call? action

  method = action.method

  klass = get_class_target(action) || Brakeman::Tracker::UNKNOWN_MODEL
  name = Sexp.new(:lit, klass.downcase)

  if SINGLE_RECORD.include? method
    # Set a local variable with name based on class of model
    # and value of the value passed to render
    local_key = Sexp.new(:lit, :locals)
    locals = hash_access(args, local_key) || Sexp.new(:hash)
    hash_insert(locals, name, action)
    hash_insert(args, local_key, locals)

    process_partial name, args, action.line
  elsif COLLECTION.include? method
    collection_key = Sexp.new(:lit, :collection)
    hash_insert(args, collection_key, action)

    process_partial name, args, action.line
  end
end

#process_partial(name, args, line) ⇒ Object

Determines file name for partial and then processes it



45
46
47
48
49
50
51
52
53
# File 'lib/brakeman/processors/lib/render_helper.rb', line 45

def process_partial name, args, line
  if !(string? name or symbol? name) or name.value == ""
    return
  end

  names = name.value.to_s.split("/")
  names[-1] = "_" + names[-1]
  process_template template_name(names.join("/")), args, nil, line
end

#process_render(exp) ⇒ Object

Process s(:render, TYPE, OPTION?, OPTIONS)



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/brakeman/processors/lib/render_helper.rb', line 7

def process_render exp
  process_default exp
  @rendered = true
  case exp.render_type
  when :action, :template, :inline
    action = exp[2]
    args = exp[3]

    if string? action or symbol? action
      process_action action.value, args, exp.line
    else
      process_model_action action, args
    end
  when :default
    begin
      process_template template_name, exp[3], nil, exp.line
    rescue ArgumentError
      Brakeman.debug "Problem processing render: #{exp}"
    end
  when :partial, :layout
    process_partial exp[2], exp[3], exp.line
  when :nothing
  end
  exp
end

#process_template(name, args, called_from = nil, *_) ⇒ Object

Processes a template, adding any instance variables to its environment.



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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/brakeman/processors/lib/render_helper.rb', line 94

def process_template name, args, called_from = nil, *_

  Brakeman.debug "Rendering #{name} (#{called_from})"
  #Get scanned source for this template
  name = name.to_s.gsub(/^\//, "")
  template = @tracker.templates[name.to_sym]
  unless template
    Brakeman.debug "[Notice] No such template: #{name}"
    return
  end

  if called_from
    # Track actual template that was rendered
    called_from.last_template = template
  end

  template_env = only_ivars(:include_request_vars)

  #Hash the environment and the source of the template to avoid
  #pointlessly processing templates, which can become prohibitively
  #expensive in terms of time and memory.
  digest = Digest::SHA1.new.update(template_env.instance_variable_get(:@env).to_a.sort.to_s << name).to_s.to_sym

  if @tracker.template_cache.include? digest
    #Already processed this template with identical environment
    return
  else
    @tracker.template_cache << digest

    options = get_options args

    #Process layout
    if string? options[:layout]
      process_template "layouts/#{options[:layout][1]}", nil, nil, nil
    elsif node_type? options[:layout], :false
      #nothing
    elsif not template.name.to_s.match(/[^\/_][^\/]+$/)
      #Don't do this for partials

      process_layout
    end

    if hash? options[:locals]
      hash_iterate options[:locals] do |key, value|
        if symbol? key
          template_env[Sexp.new(:call, nil, key.value)] = value
        end
      end
    end

    if options[:collection]

      #The collection name is the name of the partial without the leading
      #underscore.
      variable = template.name.to_s.match(/[^\/_][^\/]+$/)[0].to_sym

      #Unless the :as => :variable_name option is used
      if options[:as]
        if string? options[:as] or symbol? options[:as]
          variable = options[:as].value.to_sym
        end
      end

      collection = get_class_target(options[:collection]) || Brakeman::Tracker::UNKNOWN_MODEL

      template_env[Sexp.new(:call, nil, variable)] = Sexp.new(:call, Sexp.new(:const, collection), :new)
    end

    #Set original_line for values so it is clear
    #that values came from another file
    template_env.all.each do |_var, value|
      unless value.original_line
        #TODO: This has been broken for a while now and no one noticed
        #so maybe we can skip it
        value.original_line = value.line
      end
    end

    #Run source through AliasProcessor with instance variables from the
    #current environment.
    #TODO: Add in :locals => { ... } to environment
    src = Brakeman::TemplateAliasProcessor.new(@tracker, template, called_from).process_safely(template.src, template_env)

    digest = Digest::SHA1.new.update(name + src.to_s).to_s.to_sym

    if @tracker.template_cache.include? digest
      return
    else
      @tracker.template_cache << digest
    end

    #Run alias-processed src through the template processor to pull out
    #information and outputs.
    #This information will be stored in tracker.templates, but with a name
    #specifying this particular route. The original source should remain
    #pristine (so it can be processed within other environments).
    @tracker.processor.process_template name, src, template.type, called_from, template.file
  end
end

#template_name(name) ⇒ Object

Override to process name, such as adding the controller name.



195
196
197
# File 'lib/brakeman/processors/lib/render_helper.rb', line 195

def template_name name
  raise "RenderHelper#template_name should be overridden."
end