brakeman-lib 4.5.1 → 4.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +158 -109
  3. data/README.md +1 -2
  4. data/lib/brakeman/call_index.rb +54 -15
  5. data/lib/brakeman/checks/base_check.rb +50 -47
  6. data/lib/brakeman/checks/check_cookie_serialization.rb +22 -0
  7. data/lib/brakeman/checks/check_cross_site_scripting.rb +4 -4
  8. data/lib/brakeman/checks/check_deserialize.rb +3 -6
  9. data/lib/brakeman/checks/check_execute.rb +26 -1
  10. data/lib/brakeman/checks/check_file_access.rb +7 -1
  11. data/lib/brakeman/checks/check_header_dos.rb +2 -2
  12. data/lib/brakeman/checks/check_i18n_xss.rb +2 -2
  13. data/lib/brakeman/checks/check_jruby_xml.rb +2 -2
  14. data/lib/brakeman/checks/check_json_parsing.rb +2 -2
  15. data/lib/brakeman/checks/check_mass_assignment.rb +1 -1
  16. data/lib/brakeman/checks/check_mime_type_dos.rb +2 -2
  17. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +1 -1
  18. data/lib/brakeman/checks/check_reverse_tabnabbing.rb +58 -0
  19. data/lib/brakeman/checks/check_sanitize_methods.rb +2 -2
  20. data/lib/brakeman/checks/check_session_settings.rb +5 -2
  21. data/lib/brakeman/checks/check_sql.rb +24 -22
  22. data/lib/brakeman/checks/check_xml_dos.rb +2 -2
  23. data/lib/brakeman/checks/check_yaml_parsing.rb +10 -18
  24. data/lib/brakeman/differ.rb +16 -28
  25. data/lib/brakeman/file_parser.rb +4 -8
  26. data/lib/brakeman/file_path.rb +14 -0
  27. data/lib/brakeman/parsers/haml_embedded.rb +1 -1
  28. data/lib/brakeman/parsers/template_parser.rb +3 -1
  29. data/lib/brakeman/processor.rb +2 -2
  30. data/lib/brakeman/processors/alias_processor.rb +15 -1
  31. data/lib/brakeman/processors/base_processor.rb +2 -0
  32. data/lib/brakeman/processors/controller_processor.rb +4 -4
  33. data/lib/brakeman/processors/gem_processor.rb +10 -2
  34. data/lib/brakeman/processors/haml_template_processor.rb +87 -123
  35. data/lib/brakeman/processors/lib/call_conversion_helper.rb +5 -4
  36. data/lib/brakeman/processors/lib/find_all_calls.rb +27 -4
  37. data/lib/brakeman/processors/lib/find_call.rb +3 -64
  38. data/lib/brakeman/processors/lib/rails2_config_processor.rb +1 -1
  39. data/lib/brakeman/processors/template_alias_processor.rb +28 -0
  40. data/lib/brakeman/processors/template_processor.rb +10 -6
  41. data/lib/brakeman/report/report_text.rb +4 -5
  42. data/lib/brakeman/rescanner.rb +4 -0
  43. data/lib/brakeman/tracker.rb +26 -2
  44. data/lib/brakeman/tracker/config.rb +38 -73
  45. data/lib/brakeman/tracker/constants.rb +2 -1
  46. data/lib/brakeman/util.rb +5 -3
  47. data/lib/brakeman/version.rb +1 -1
  48. data/lib/brakeman/warning.rb +4 -0
  49. data/lib/brakeman/warning_codes.rb +3 -0
  50. data/lib/ruby_parser/bm_sexp.rb +7 -2
  51. metadata +18 -17
@@ -1,6 +1,6 @@
1
1
  module Brakeman
2
2
  module FakeHamlFilter
3
- # Copied from Haml - force delayed compilation
3
+ # Copied from Haml 4 - force delayed compilation
4
4
  def compile(compiler, text)
5
5
  filter = self
6
6
  compiler.instance_eval do
@@ -79,7 +79,9 @@ module Brakeman
79
79
 
80
80
  Haml::Engine.new(text,
81
81
  :filename => path,
82
- :escape_html => tracker.config.escape_html?).precompiled.gsub(/([^\\])\\n/, '\1')
82
+ :escape_html => tracker.config.escape_html?,
83
+ :escape_filter_interpolations => tracker.config.escape_filter_interpolations?
84
+ ).precompiled.gsub(/([^\\])\\n/, '\1')
83
85
  rescue Haml::Error => e
84
86
  tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
85
87
  nil
@@ -53,7 +53,7 @@ module Brakeman
53
53
  #Process a model source
54
54
  def process_model src, file_name
55
55
  result = ModelProcessor.new(@tracker).process_model src, file_name
56
- AliasProcessor.new(@tracker).process result if result
56
+ AliasProcessor.new(@tracker, file_name).process result if result
57
57
  end
58
58
 
59
59
  #Process either an ERB or HAML template
@@ -90,7 +90,7 @@ module Brakeman
90
90
  def process_initializer file_name, src
91
91
  res = BaseProcessor.new(@tracker).process_file src, file_name
92
92
  res = AliasProcessor.new(@tracker).process_safely res, nil, file_name
93
- @tracker.initializers[Pathname.new(file_name).basename.to_s] = res
93
+ @tracker.initializers[file_name] = res
94
94
  end
95
95
 
96
96
  #Process source for a library file
@@ -249,6 +249,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
249
249
  end
250
250
  env[target_var] = target
251
251
  return first_arg
252
+ elsif new_string? target
253
+ env[target_var] = first_arg
254
+ return first_arg
252
255
  elsif array? target
253
256
  target << first_arg
254
257
  env[target_var] = target
@@ -265,6 +268,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
265
268
  unless target.nil?
266
269
  exp = target
267
270
  end
271
+ when :dup
272
+ unless target.nil?
273
+ exp = target
274
+ end
268
275
  when :join
269
276
  if array? target and target.length > 2 and (string? first_arg or first_arg.nil?)
270
277
  exp = process_array_join(target, first_arg)
@@ -602,7 +609,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
602
609
  if node_type? exp, :hash
603
610
  if exp.any? { |e| node_type? e, :kwsplat and node_type? e.value, :hash }
604
611
  kwsplats, rest = exp.partition { |e| node_type? e, :kwsplat and node_type? e.value, :hash }
605
- exp = Sexp.new.concat(rest).line(exp)
612
+ exp = Sexp.new.concat(rest).line(exp.line)
606
613
 
607
614
  kwsplats.each do |e|
608
615
  exp = process_hash_merge! exp, e.value
@@ -1194,6 +1201,13 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
1194
1201
  call? exp and exp.method == :raise
1195
1202
  end
1196
1203
 
1204
+ STRING_NEW = s(:call, s(:const, :String), :new)
1205
+
1206
+ # String.new ?
1207
+ def new_string? exp
1208
+ exp == STRING_NEW
1209
+ end
1210
+
1197
1211
  #Set variable to given value.
1198
1212
  #Creates "branched" versions of values when appropriate.
1199
1213
  #Avoids creating multiple branched versions inside same
@@ -114,6 +114,8 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
114
114
  exp.unshift :rlist
115
115
  end
116
116
 
117
+ alias process_rlist process_block
118
+
117
119
  #Processes the inside of an interpolated String.
118
120
  def process_evstr exp
119
121
  exp = exp.dup
@@ -192,8 +192,8 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
192
192
 
193
193
  filter_name = ("fake_filter" + rand.to_s[/\d+$/]).to_sym
194
194
  args = exp.block_call.arglist
195
- args.insert(1, Sexp.new(:lit, filter_name))
196
- before_filter_call = make_call(nil, :before_filter, args)
195
+ args.insert(1, Sexp.new(:lit, filter_name).line(exp.line))
196
+ before_filter_call = make_call(nil, :before_filter, args).line(exp.line)
197
197
 
198
198
  if exp.block_args.length > 1
199
199
  block_variable = exp.block_args[1]
@@ -210,9 +210,9 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
210
210
  #Build Sexp for filter method
211
211
  body = Sexp.new(:lasgn,
212
212
  block_variable,
213
- Sexp.new(:call, Sexp.new(:const, @current_class.name), :new))
213
+ Sexp.new(:call, Sexp.new(:const, @current_class.name).line(exp.line), :new).line(exp.line)).line(exp.line)
214
214
 
215
- filter_method = Sexp.new(:defn, filter_name, Sexp.new(:args), body).concat(block_inner).line(exp.line)
215
+ filter_method = Sexp.new(:defn, filter_name, Sexp.new(:args).line(exp.line), body).concat(block_inner).line(exp.line)
216
216
 
217
217
  vis = @visibility
218
218
  @visibility = :private
@@ -29,6 +29,14 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
29
29
  @tracker.config.set_rails_version
30
30
  end
31
31
 
32
+ # Known issue: Brakeman does not yet support `gem` calls with multiple
33
+ # "version requirements". Consider the following example from the ruby docs:
34
+ #
35
+ # gem 'rake', '>= 1.1.a', '< 2'
36
+ #
37
+ # We are assuming that `second_arg` (eg. '>= 1.1.a') is the only requirement.
38
+ # Perhaps we should instantiate an array of `::Gem::Requirement`s or even a
39
+ # `::Gem::Dependency` and pass that to `Tracker::Config#add_gem`?
32
40
  def process_call exp
33
41
  if exp.target == nil
34
42
  if exp.method == :gem
@@ -51,8 +59,8 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
51
59
  end
52
60
  end
53
61
  elsif @inside_gemspec and exp.method == :add_dependency
54
- if string? exp.first_arg and string? exp.last_arg
55
- @tracker.config.add_gem exp.first_arg.value, exp.last_arg.value, @gemspec, exp.line
62
+ if string? exp.first_arg and string? exp.second_arg
63
+ @tracker.config.add_gem exp.first_arg.value, exp.second_arg.value, @gemspec, exp.line
56
64
  end
57
65
  end
58
66
 
@@ -2,8 +2,10 @@ require 'brakeman/processors/template_processor'
2
2
 
3
3
  #Processes HAML templates.
4
4
  class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
5
- HAML_FORMAT_METHOD = /format_script_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)/
5
+ HAMLOUT = s(:call, nil, :_hamlout)
6
+ HAML_BUFFER = s(:call, HAMLOUT, :buffer)
6
7
  HAML_HELPERS = s(:colon2, s(:const, :Haml), :Helpers)
8
+ HAML_HELPERS2 = s(:colon2, s(:colon3, :Haml), :Helpers)
7
9
  JAVASCRIPT_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Javascript)
8
10
  COFFEE_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Coffee)
9
11
 
@@ -14,130 +16,46 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
14
16
 
15
17
  #Processes call, looking for template output
16
18
  def process_call exp
17
- target = exp.target
18
- if sexp? target
19
- target = process target
19
+ exp = process_default exp
20
+
21
+ if buffer_append? exp
22
+ output = normalize_output(exp.first_arg)
23
+ res = get_pushed_value(output)
20
24
  end
21
25
 
22
- method = exp.method
23
-
24
- if (call? target and target.method == :_hamlout)
25
- res = case method
26
- when :adjust_tabs, :rstrip!, :attributes #Check attributes, maybe?
27
- ignore
28
- when :options, :buffer
29
- exp
30
- when :open_tag
31
- process_call_args exp
32
- else
33
- arg = exp.first_arg
34
-
35
- if arg
36
- @inside_concat = true
37
- exp.first_arg = process(arg)
38
- out = normalize_output(exp.first_arg)
39
- @inside_concat = false
40
- else
41
- raise "Empty _hamlout.#{method}()?"
42
- end
43
-
44
- if string? out
45
- ignore
46
- else
47
- r = case method.to_s
48
- when "push_text"
49
- build_output_from_push_text(out)
50
- when HAML_FORMAT_METHOD
51
- if $4 == "true"
52
- if string_interp? out
53
- build_output_from_push_text(out, :escaped_output)
54
- else
55
- Sexp.new :format_escaped, out
56
- end
57
- else
58
- if string_interp? out
59
- build_output_from_push_text(out)
60
- else
61
- Sexp.new :format, out
62
- end
63
- end
64
-
65
- else
66
- raise "Unrecognized action on _hamlout: #{method}"
67
- end
68
-
69
- @javascript = false
70
- r
71
- end
72
- end
73
-
74
- res.line(exp.line)
75
- res
76
-
77
- #_hamlout.buffer <<
78
- #This seems to be used rarely, but directly appends args to output buffer.
79
- #Has something to do with values of blocks?
80
- elsif sexp? target and method == :<< and is_buffer_target? target
81
- @inside_concat = true
82
- exp.first_arg = process(exp.first_arg)
83
- out = normalize_output(exp.first_arg)
84
- @inside_concat = false
85
-
86
- if out.node_type == :str #ignore plain strings
87
- ignore
88
- else
89
- add_output out
90
- end
91
- elsif target == nil and method == :render
92
- #Process call to render()
93
- exp.arglist = process exp.arglist
94
- make_render_in_view exp
95
- elsif target == nil and method == :find_and_preserve and exp.first_arg
96
- process exp.first_arg
97
- elsif method == :render_with_options
98
- if target == JAVASCRIPT_FILTER or target == COFFEE_FILTER
99
- @javascript = true
100
- end
26
+ res or exp
27
+ end
101
28
 
102
- process exp.first_arg
103
- else
104
- exp.target = target
105
- exp.arglist = process exp.arglist
106
- exp
107
- end
29
+ # _haml_out.buffer << ...
30
+ def buffer_append? exp
31
+ call? exp and
32
+ exp.target == HAML_BUFFER and
33
+ exp.method == :<<
34
+ end
35
+
36
+ PRESERVE_METHODS = [:find_and_preserve, :preserve]
37
+
38
+ def find_and_preserve? exp
39
+ call? exp and
40
+ PRESERVE_METHODS.include?(exp.method) and
41
+ exp.first_arg
108
42
  end
109
43
 
110
44
  #If inside an output stream, only return the final expression
111
45
  def process_block exp
112
46
  exp = exp.dup
113
47
  exp.shift
114
- if @inside_concat
115
- @inside_concat = false
116
- exp[0..-2].each do |e|
117
- process e
118
- end
119
- @inside_concat = true
120
- process exp[-1]
121
- else
122
- exp.map! do |e|
123
- res = process e
124
- if res.empty?
125
- nil
126
- else
127
- res
128
- end
48
+
49
+ exp.map! do |e|
50
+ res = process e
51
+ if res.empty?
52
+ nil
53
+ else
54
+ res
129
55
  end
130
- Sexp.new(:rlist).concat(exp).compact
131
56
  end
132
- end
133
57
 
134
- #Checks if the buffer is the target in a method call Sexp.
135
- #TODO: Test this
136
- def is_buffer_target? exp
137
- exp.node_type == :call and
138
- node_type? exp.target, :lvar and
139
- exp.target.value == :_hamlout and
140
- exp.method == :buffer
58
+ Sexp.new(:rlist).concat(exp).compact
141
59
  end
142
60
 
143
61
  #HAML likes to put interpolated values into _hamlout.push_text
@@ -158,7 +76,6 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
158
76
  end
159
77
  end
160
78
 
161
- #Gets outputs from values interpolated into _hamlout.push_text
162
79
  def get_pushed_value exp, default = :output
163
80
  return exp unless sexp? exp
164
81
 
@@ -173,24 +90,71 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
173
90
  exp
174
91
  when :str, :ignore, :output, :escaped_output
175
92
  exp
176
- when :block, :rlist, :dstr
177
- exp.map! { |e| get_pushed_value e }
93
+ when :block, :rlist
94
+ exp.map! { |e| get_pushed_value(e, default) }
95
+ when :dstr
96
+ build_output_from_push_text(exp, default)
178
97
  when :if
179
- clauses = [get_pushed_value(exp.then_clause), get_pushed_value(exp.else_clause)].compact
98
+ clauses = [get_pushed_value(exp.then_clause, default), get_pushed_value(exp.else_clause, default)].compact
180
99
 
181
100
  if clauses.length > 1
182
- s(:or, *clauses)
101
+ s(:or, *clauses).line(exp.line)
183
102
  else
184
103
  clauses.first
185
104
  end
186
- else
187
- if call? exp and exp.target == HAML_HELPERS and exp.method == :html_escape
188
- add_escaped_output exp.first_arg
189
- elsif @javascript and call? exp and (exp.method == :j or exp.method == :escape_javascript)
190
- add_escaped_output exp.first_arg
105
+ when :call
106
+ if exp.method == :to_s or exp.method == :strip
107
+ get_pushed_value(exp.target, default)
108
+ elsif haml_helpers? exp.target and exp.method == :html_escape
109
+ get_pushed_value(exp.first_arg, :escaped_output)
110
+ elsif @javascript and (exp.method == :j or exp.method == :escape_javascript) # TODO: Remove - this is not safe
111
+ get_pushed_value(exp.first_arg, :escaped_output)
112
+ elsif find_and_preserve? exp or fix_textareas? exp
113
+ get_pushed_value(exp.first_arg, default)
114
+ elsif raw? exp
115
+ get_pushed_value(exp.first_arg, :output)
116
+ elsif hamlout_attributes? exp
117
+ ignore # ignore _hamlout.attributes calls
118
+ elsif exp.target.nil? and exp.method == :render
119
+ #Process call to render()
120
+ exp.arglist = process exp.arglist
121
+ make_render_in_view exp
122
+ elsif exp.method == :render_with_options
123
+ if exp.target == JAVASCRIPT_FILTER or exp.target == COFFEE_FILTER
124
+ @javascript = true
125
+ end
126
+
127
+ get_pushed_value(exp.first_arg, default)
128
+ @javascript = false
191
129
  else
192
130
  add_output exp, default
193
131
  end
132
+ else
133
+ add_output exp, default
194
134
  end
195
135
  end
136
+
137
+ def haml_helpers? exp
138
+ # Sometimes its Haml::Helpers and
139
+ # sometimes its ::Haml::Helpers
140
+ exp == HAML_HELPERS or
141
+ exp == HAML_HELPERS2
142
+ end
143
+
144
+ def hamlout_attributes? exp
145
+ call? exp and
146
+ exp.target == HAMLOUT and
147
+ exp.method == :attributes
148
+ end
149
+
150
+ def fix_textareas? exp
151
+ call? exp and
152
+ exp.target == HAMLOUT and
153
+ exp.method == :fix_textareas!
154
+ end
155
+
156
+ def raw? exp
157
+ call? exp and
158
+ exp.method == :raw
159
+ end
196
160
  end
@@ -19,16 +19,17 @@ module Brakeman
19
19
  end
20
20
  end
21
21
 
22
+ STRING_LENGTH_LIMIT = 50
23
+
22
24
  # Join two string literals into one.
23
25
  def join_strings lhs, rhs, original_exp = nil
24
26
  if string? lhs and string? rhs
25
- result = Sexp.new(:str).line(lhs.line)
26
- result.value = lhs.value + rhs.value
27
-
28
- if result.value.length > 50
27
+ if (lhs.value.length + rhs.value.length > STRING_LENGTH_LIMIT)
29
28
  # Avoid gigantic strings
30
29
  lhs
31
30
  else
31
+ result = Sexp.new(:str).line(lhs.line)
32
+ result.value = lhs.value + rhs.value
32
33
  result
33
34
  end
34
35
  elsif call? lhs and lhs.method == :+ and string? lhs.first_arg and string? rhs
@@ -5,9 +5,9 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
5
5
 
6
6
  def initialize tracker
7
7
  super
8
- @current_class = nil
9
- @current_method = nil
8
+
10
9
  @in_target = false
10
+ @processing_class = false
11
11
  @calls = []
12
12
  @cache = {}
13
13
  end
@@ -23,10 +23,33 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
23
23
  process exp
24
24
  end
25
25
 
26
+ #For whatever reason, originally the indexing of calls
27
+ #was performed on individual method bodies (see process_defn).
28
+ #This method explicitly indexes all calls everywhere given any
29
+ #source.
30
+ def process_all_source exp, opts
31
+ @processing_class = true
32
+ process_source exp, opts
33
+ ensure
34
+ @processing_class = false
35
+ end
36
+
26
37
  #Process body of method
27
38
  def process_defn exp
28
- return exp unless @current_method
29
- process_all exp.body
39
+ return exp unless @current_method or @processing_class
40
+
41
+ # 'Normal' processing assumes the method name was given
42
+ # as an option to `process_source` but for `process_all_source`
43
+ # we don't want to do that.
44
+ if @current_method.nil?
45
+ @current_method = exp.method_name
46
+ process_all exp.body
47
+ @current_method = nil
48
+ else
49
+ process_all exp.body
50
+ end
51
+
52
+ exp
30
53
  end
31
54
 
32
55
  alias process_defs process_defn