haml_template_processor.rb 4.3 KB
Newer Older
J
Justin Collins 已提交
1
require 'brakeman/processors/template_processor'
J
Justin 已提交
2 3

#Processes HAML templates.
J
Justin Collins 已提交
4
class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
J
Justin 已提交
5
  HAML_FORMAT_METHOD = /format_script_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)/
J
Justin Collins 已提交
6
  HAML_HELPERS = s(:colon2, s(:const, :Haml), :Helpers)
7
  
J
Justin 已提交
8 9
  #Processes call, looking for template output
  def process_call exp
J
Justin Collins 已提交
10
    target = exp.target
J
Justin 已提交
11 12 13 14
    if sexp? target
      target = process target
    end

J
Justin Collins 已提交
15
    method = exp.method
J
Justin 已提交
16

17
    if (call? target and target.method == :_hamlout)
J
Justin 已提交
18
      res = case method
J
Justin Collins 已提交
19
            when :adjust_tabs, :rstrip!, :attributes #Check attributes, maybe?
J
Justin 已提交
20
              ignore
21 22
            when :options, :buffer
              exp
J
Justin 已提交
23
            when :open_tag
24
              process_call_args exp
J
Justin 已提交
25
            else
26
              arg = exp.first_arg
J
Justin 已提交
27 28 29

              if arg
                @inside_concat = true
30
                out = exp.first_arg = process(arg)
J
Justin 已提交
31 32
                @inside_concat = false
              else
33
                raise "Empty _hamlout.#{method}()?"
J
Justin 已提交
34 35 36 37 38 39 40
              end

              if string? out
                ignore
              else
                case method.to_s
                when "push_text"
J
Justin Collins 已提交
41
                  build_output_from_push_text(out)
J
Justin 已提交
42 43 44 45 46 47 48
                when HAML_FORMAT_METHOD
                  if $4 == "true"
                    Sexp.new :format_escaped, out
                  else
                    Sexp.new :format, out
                  end
                else
49
                  raise "Unrecognized action on _hamlout: #{method}"
J
Justin 已提交
50 51 52 53 54 55 56 57 58
                end
              end

            end

      res.line(exp.line)
      res

      #_hamlout.buffer <<
59 60
      #This seems to be used rarely, but directly appends args to output buffer.
      #Has something to do with values of blocks?
J
Justin 已提交
61 62
    elsif sexp? target and method == :<< and is_buffer_target? target
      @inside_concat = true
63
      out = exp.first_arg = process(exp.first_arg)
J
Justin 已提交
64 65 66 67 68 69 70 71 72 73 74 75
      @inside_concat = false

      if out.node_type == :str #ignore plain strings
        ignore
      else
        s = Sexp.new(:output, out)
        @current_template[:outputs] << s
        s.line(exp.line)
        s
      end
    elsif target == nil and method == :render
      #Process call to render()
J
Justin Collins 已提交
76
      exp.arglist = process exp.arglist
J
Justin Collins 已提交
77
      make_render_in_view exp
J
Justin 已提交
78
    else
79 80 81
      exp.target = target
      exp.arglist = process exp.arglist
      exp
J
Justin 已提交
82 83 84 85 86
    end
  end

  #If inside an output stream, only return the final expression
  def process_block exp
87
    exp = exp.dup
J
Justin 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    exp.shift
    if @inside_concat
      @inside_concat = false
      exp[0..-2].each do |e|
        process e
      end
      @inside_concat = true
      process exp[-1]
    else
      exp.map! do |e|
        res = process e
        if res.empty?
          nil
        else
          res
        end
      end
      Sexp.new(:rlist).concat(exp).compact
    end
  end

  #Checks if the buffer is the target in a method call Sexp.
110
  #TODO: Test this
J
Justin 已提交
111
  def is_buffer_target? exp
112 113 114 115
    exp.node_type == :call and
    node_type? exp.target, :lvar and
    exp.target.value == :_hamlout and
    exp.method == :buffer
J
Justin 已提交
116
  end
J
Justin Collins 已提交
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

  #HAML likes to put interpolated values into _hamlout.push_text
  #but we want to handle those individually
  def build_output_from_push_text exp
    if node_type? exp, :string_interp, :dstr
      exp.map! do |e|
        if sexp? e
          if node_type? e, :string_eval, :evstr
            e = e.value
          end

          get_pushed_value e
        else
          e
        end
      end
    end
  end

  #Gets outputs from values interpolated into _hamlout.push_text
  def get_pushed_value exp
    return exp unless sexp? exp
    
    case exp.node_type
    when :format
      exp.node_type = :output
      @current_template[:outputs] << exp
      exp
    when :format_escaped
      exp.node_type = :escaped_output
      @current_template[:outputs] << exp
      exp
    when :str, :ignore, :output, :escaped_output
      exp
    when :block, :rlist, :string_interp, :dstr
      exp.map! { |e| get_pushed_value e }
    else
      if call? exp and exp.target == HAML_HELPERS and exp.method == :html_escape
        s = Sexp.new(:escaped_output, exp.first_arg)
      else
        s = Sexp.new(:output, exp)
      end

      s.line(exp.line)
      @current_template[:outputs] << s
      s
    end
  end
J
Justin 已提交
165
end