brakeman-lib 4.6.1 → 4.7.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6eaaa0c21996b84e6ae73284cb743a1801428f643a757e09c25d1f907c2f6c66
4
- data.tar.gz: e0ee33be554dcc58b89926d709625afd4df0834f20a374ec210630992108e615
3
+ metadata.gz: e29d6575b47438b336589ce753da72098ad1b7465a4bb78625d5c666d8c1d0a3
4
+ data.tar.gz: 073d249eb7c1915c3c3be01345c25d050127eec929ff2dd77e276433f94606c1
5
5
  SHA512:
6
- metadata.gz: c3cef641ef58cb656a1f11706fd7da8e36ebaff5cc8e0473c901368c46eec1333775a58eac4a5f3c5b0b8f38786e698aae38e9d50685d34a34953a0bdf2558e7
7
- data.tar.gz: 68f49be644f4aa8de6b7cef78c8b9948984f73f265052b12d861a06231d5aa3620b4c227e7129606dba71a6498a0da4cafd37d0212875b37a9c1fbe03d9c9f5d
6
+ metadata.gz: 87488f856d5b69554147a900ea4a5e0f8826b8a4a1ca95090c472f279d3014a80d28acfa36e131b07c2a9d78a80ec807fa20b45f7e2c133575da164b8ccf3506
7
+ data.tar.gz: b73fe208fc26c26428b4883aa36e2b0c310f7b271fe9db0111cd5fc23d3fc040b8f90c0875f4736724685515ee6c3a6d20faf4cb1246042fab0fe66a6de41c61
data/CHANGES.md CHANGED
@@ -1,3 +1,14 @@
1
+ # 4.7.0
2
+
3
+ * Refactor `Brakeman::Differ#second_pass` (Benoit Côté-Jodoin)
4
+ * Ignore interpolation in `%W[]`
5
+ * Fix `version_between?` (Andrey Glushkov)
6
+ * Add support for `ruby_parser` 3.14.0
7
+ * Ignore `form_for` for XSS check
8
+ * Update Haml support to Haml 5.x
9
+ * Catch shell injection from `-c` shell commands (Jacob Evelyn)
10
+ * Correctly handle non-symbols in `CheckCookieSerialization` (Phil Turnbull)
11
+
1
12
  # 4.6.1
2
13
 
3
14
  * Fix Reverse Tabnabbing warning message (Steffen Schildknecht / Jörg Schiller)
@@ -39,6 +39,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
39
39
  @active_record_models = nil
40
40
  @mass_assign_disabled = nil
41
41
  @has_user_input = nil
42
+ @in_array = false
42
43
  @safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id]
43
44
  @comparison_ops = Set[:==, :!=, :>, :<, :>=, :<=]
44
45
  end
@@ -109,9 +110,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
109
110
  exp
110
111
  end
111
112
 
113
+ def process_array exp
114
+ @in_array = true
115
+ process_default exp
116
+ ensure
117
+ @in_array = false
118
+ end
119
+
112
120
  #Does not actually process string interpolation, but notes that it occurred.
113
121
  def process_dstr exp
114
- unless @string_interp # don't overwrite existing value
122
+ unless array_interp? exp or @string_interp # don't overwrite existing value
115
123
  @string_interp = Match.new(:interp, exp)
116
124
  end
117
125
 
@@ -120,6 +128,20 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
120
128
 
121
129
  private
122
130
 
131
+ # Checking for
132
+ #
133
+ # %W[#{a}]
134
+ #
135
+ # which will be parsed as
136
+ #
137
+ # s(:array, s(:dstr, "", s(:evstr, s(:call, nil, :a))))
138
+ def array_interp? exp
139
+ @in_array and
140
+ string_interp? exp and
141
+ exp[1] == "".freeze and
142
+ exp.length == 3 # only one interpolated value
143
+ end
144
+
123
145
  def always_safe_method? meth
124
146
  @safe_input_attributes.include? meth or
125
147
  @comparison_ops.include? meth
@@ -9,7 +9,7 @@ class Brakeman::CheckCookieSerialization < Brakeman::BaseCheck
9
9
  tracker.find_call(target: :'Rails.application.config.action_dispatch', method: :cookies_serializer=).each do |result|
10
10
  setting = result[:call].first_arg
11
11
 
12
- if symbol? setting and setting.value == :marshal or setting.value == :hybrid
12
+ if symbol? setting and [:marshal, :hybrid].include? setting.value
13
13
  warn :result => result,
14
14
  :warning_type => "Remote Code Execution",
15
15
  :warning_code => :unsafe_cookie_serialization,
@@ -287,7 +287,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
287
287
 
288
288
  def setup
289
289
  @ignore_methods = Set[:==, :!=, :button_to, :check_box, :content_tag, :escapeHTML, :escape_once,
290
- :field_field, :fields_for, :h, :hidden_field,
290
+ :field_field, :fields_for, :form_for, :h, :hidden_field,
291
291
  :hidden_field, :hidden_field_tag, :image_tag, :label,
292
292
  :link_to, :mail_to, :radio_button, :select,
293
293
  :submit_tag, :text_area, :text_field,
@@ -21,6 +21,10 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
21
21
  SHELL_ESCAPE_MODULE_METHODS = Set[:escape, :join, :shellescape, :shelljoin]
22
22
  SHELL_ESCAPE_MIXIN_METHODS = Set[:shellescape, :shelljoin]
23
23
 
24
+ # These are common shells that are known to allow the execution of commands
25
+ # via a -c flag. See dash_c_shell_command? for more info.
26
+ KNOWN_SHELL_COMMANDS = Set["sh", "bash", "ksh", "csh", "tcsh", "zsh"]
27
+
24
28
  SHELLWORDS = s(:const, :Shellwords)
25
29
 
26
30
  #Check models, controllers, and views for command injection.
@@ -42,6 +46,8 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
42
46
  end
43
47
  end
44
48
 
49
+ private
50
+
45
51
  #Processes results from Tracker#find_call.
46
52
  def process_result result
47
53
  call = result[:call]
@@ -54,7 +60,17 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
54
60
  failure = include_user_input?(args) || dangerous_interp?(args)
55
61
  end
56
62
  when :system, :exec
57
- failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
63
+ # Normally, if we're in a `system` or `exec` call, we only are worried
64
+ # about shell injection when there's a single argument, because comma-
65
+ # separated arguments are always escaped by Ruby. However, an exception is
66
+ # when the first two arguments are something like "bash -c" because then
67
+ # the third argument is effectively the command being run and might be
68
+ # a malicious executable if it comes (partially or fully) from user input.
69
+ if dash_c_shell_command?(first_arg, call.second_arg)
70
+ failure = include_user_input?(args[3]) || dangerous_interp?(args[3])
71
+ else
72
+ failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
73
+ end
58
74
  else
59
75
  failure = include_user_input?(args) || dangerous_interp?(args)
60
76
  end
@@ -77,6 +93,15 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
77
93
  end
78
94
  end
79
95
 
96
+ # @return [Boolean] true iff the command given by `first_arg`, `second_arg`
97
+ # invokes a new shell process via `<shell_command> -c` (like `bash -c`)
98
+ def dash_c_shell_command?(first_arg, second_arg)
99
+ string?(first_arg) &&
100
+ KNOWN_SHELL_COMMANDS.include?(first_arg.value) &&
101
+ string?(second_arg) &&
102
+ second_arg.value == "-c"
103
+ end
104
+
80
105
  def check_open_calls
81
106
  tracker.find_call(:targets => [nil, :Kernel], :method => :open).each do |result|
82
107
  if match = dangerous_open_arg?(result[:call].first_arg)
@@ -24,43 +24,31 @@ class Brakeman::Differ
24
24
  # second pass to cleanup any vulns which have changed in line number only.
25
25
  # Given a list of new warnings, delete pairs of new/fixed vulns that differ
26
26
  # only by line number.
27
- # Horrible O(n^2) performance. Keep n small :-/
28
27
  def second_pass(warnings)
29
- # keep track of the number of elements deleted because the index numbers
30
- # won't update as the list is modified
31
- elements_deleted_offset = 0
28
+ new_fingerprints = Set.new(warnings[:new].map(&method(:fingerprint)))
29
+ fixed_fingerprints = Set.new(warnings[:fixed].map(&method(:fingerprint)))
32
30
 
33
- # dup this list since we will be deleting from it and the iterator gets confused.
34
- # use _with_index for fast deletion as opposed to .reject!{|obj| obj == *_warning}
35
- warnings[:new].dup.each_with_index do |new_warning, new_warning_id|
36
- warnings[:fixed].each_with_index do |fixed_warning, fixed_warning_id|
37
- if eql_except_line_number new_warning, fixed_warning
38
- warnings[:new].delete_at(new_warning_id - elements_deleted_offset)
39
- elements_deleted_offset += 1
40
- warnings[:fixed].delete_at(fixed_warning_id)
41
- break
42
- end
31
+ # Remove warnings which fingerprints are both in :new and :fixed
32
+ shared_fingerprints = new_fingerprints.intersection(fixed_fingerprints)
33
+
34
+ unless shared_fingerprints.empty?
35
+ warnings[:new].delete_if do |warning|
36
+ shared_fingerprints.include?(fingerprint(warning))
37
+ end
38
+
39
+ warnings[:fixed].delete_if do |warning|
40
+ shared_fingerprints.include?(fingerprint(warning))
43
41
  end
44
42
  end
45
43
 
46
44
  warnings
47
45
  end
48
46
 
49
- def eql_except_line_number new_warning, fixed_warning
50
- # can't do this ahead of time, as callers may be expecting a Brakeman::Warning
51
- if new_warning.is_a? Brakeman::Warning
52
- new_warning = new_warning.to_hash
53
- fixed_warning = fixed_warning.to_hash
54
- end
55
-
56
- if new_warning[:fingerprint] and fixed_warning[:fingerprint]
57
- new_warning[:fingerprint] == fixed_warning[:fingerprint]
47
+ def fingerprint(warning)
48
+ if warning.is_a?(Brakeman::Warning)
49
+ warning.fingerprint
58
50
  else
59
- OLD_WARNING_KEYS.each do |attr|
60
- return false if new_warning[attr] != fixed_warning[attr]
61
- end
62
-
63
- true
51
+ warning[:fingerprint]
64
52
  end
65
53
  end
66
54
  end
@@ -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
@@ -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
@@ -1198,6 +1201,13 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
1198
1201
  call? exp and exp.method == :raise
1199
1202
  end
1200
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
+
1201
1211
  #Set variable to given value.
1202
1212
  #Creates "branched" versions of values when appropriate.
1203
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
@@ -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
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
@@ -75,7 +75,7 @@ class Brakeman::Rails2ConfigProcessor < Brakeman::BasicProcessor
75
75
  def process_cdecl exp
76
76
  #Set Rails version required
77
77
  if exp.lhs == :RAILS_GEM_VERSION
78
- @tracker.config.rails_version = exp.rhs.value
78
+ @tracker.config.set_rails_version exp.rhs.value
79
79
  end
80
80
 
81
81
  exp
@@ -32,6 +32,34 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
32
32
  end
33
33
  end
34
34
 
35
+ def process_lasgn exp
36
+ if exp.lhs == :haml_temp or haml_capture? exp.rhs
37
+ exp.rhs = process exp.rhs
38
+
39
+ # Avoid propagating contents of block
40
+ if node_type? exp.rhs, :iter
41
+ new_exp = exp.dup
42
+ new_exp.rhs = exp.rhs.block_call
43
+
44
+ super new_exp
45
+
46
+ exp # Still save the original, though
47
+ else
48
+ super exp
49
+ end
50
+ else
51
+ super exp
52
+ end
53
+ end
54
+
55
+ HAML_CAPTURE = [:capture, :capture_haml]
56
+
57
+ def haml_capture? exp
58
+ node_type? exp, :iter and
59
+ call? exp.block_call and
60
+ HAML_CAPTURE.include? exp.block_call.method
61
+ end
62
+
35
63
  #Determine template name
36
64
  def template_name name
37
65
  if !name.to_s.include?('/') && @template.name.to_s.include?('/')
@@ -4,10 +4,8 @@ module Brakeman
4
4
  class Config
5
5
  include Util
6
6
 
7
- attr_reader :rails, :tracker
8
- attr_accessor :rails_version, :ruby_version
7
+ attr_reader :gems, :rails, :ruby_version, :tracker
9
8
  attr_writer :erubis, :escape_html
10
- attr_reader :gems
11
9
 
12
10
  def initialize tracker
13
11
  @tracker = tracker
@@ -20,7 +18,7 @@ module Brakeman
20
18
  end
21
19
 
22
20
  def default_protect_from_forgery?
23
- if version_between? "5.2.0", "9.9.9"
21
+ if version_between? "5.2.0.beta1", "9.9.9"
24
22
  if @rails.dig(:action_controller, :default_protect_from_forgery) == Sexp.new(:false)
25
23
  return false
26
24
  else
@@ -44,12 +42,18 @@ module Brakeman
44
42
  true? @rails.dig(:active_support, :escape_html_entities_in_json)
45
43
  end
46
44
 
45
+ def escape_filter_interpolations?
46
+ # TODO see if app is actually turning this off itself
47
+ has_gem?(:haml) and
48
+ version_between? "5.0.0", "5.99", gem_version(:haml)
49
+ end
50
+
47
51
  def whitelist_attributes?
48
52
  @rails.dig(:active_record, :whitelist_attributes) == Sexp.new(:true)
49
53
  end
50
54
 
51
55
  def gem_version name
52
- @gems.dig(name, :version)
56
+ extract_version @gems.dig(name, :version)
53
57
  end
54
58
 
55
59
  def add_gem name, version, file, line
@@ -69,11 +73,16 @@ module Brakeman
69
73
  @gems[name]
70
74
  end
71
75
 
72
- def set_rails_version
73
- # Ignore ~>, etc. when using values from Gemfile
74
- version = gem_version(:rails) || gem_version(:railties)
75
- if version and version.match(/(\d+\.\d+(\.\d+.*)?)/)
76
- @rails_version = $1
76
+ def set_rails_version version = nil
77
+ version = if version
78
+ # Only used by Rails2ConfigProcessor right now
79
+ extract_version(version)
80
+ else
81
+ gem_version(:rails) || gem_version(:railties)
82
+ end
83
+
84
+ if version
85
+ @rails_version = version
77
86
 
78
87
  if tracker.options[:rails3].nil? and tracker.options[:rails4].nil?
79
88
  if @rails_version.start_with? "3"
@@ -102,16 +111,22 @@ module Brakeman
102
111
  @escape_html = true
103
112
  Brakeman.notify "[Notice] Escaping HTML by default"
104
113
  end
114
+ end
105
115
 
106
- check_haml_version
116
+ def rails_version
117
+ # This needs to be here because Util#rails_version calls Tracker::Config#rails_version
118
+ # but Tracker::Config includes Util...
119
+ @rails_version
107
120
  end
108
121
 
109
122
  def set_ruby_version version
123
+ @ruby_version = extract_version(version)
124
+ end
125
+
126
+ def extract_version version
110
127
  return unless version.is_a? String
111
128
 
112
- if version =~ /(\d+\.\d+\.\d+)/
113
- self.ruby_version = $1
114
- end
129
+ version[/\d+\.\d+(\.\d+.*)?/]
115
130
  end
116
131
 
117
132
  #Returns true if low_version <= RAILS_VERSION <= high_version
@@ -121,89 +136,15 @@ module Brakeman
121
136
  current_version ||= rails_version
122
137
  return false unless current_version
123
138
 
124
- version = current_version.split(".").map! { |v| convert_version_number v }
125
- low_version = low_version.split(".").map! { |v| convert_version_number v }
126
- high_version = high_version.split(".").map! { |v| convert_version_number v }
127
-
128
- version.each_with_index do |v, i|
129
- if lower? v, low_version.fetch(i, 0)
130
- return false
131
- elsif higher? v, low_version.fetch(i, 0)
132
- break
133
- end
134
- end
135
-
136
- version.each_with_index do |v, i|
137
- if higher? v, high_version.fetch(i, 0)
138
- return false
139
- elsif lower? v, high_version.fetch(i, 0)
140
- break
141
- end
142
- end
139
+ low = Gem::Version.new(low_version)
140
+ high = Gem::Version.new(high_version)
141
+ current = Gem::Version.new(current_version)
143
142
 
144
- true
143
+ current.between?(low, high)
145
144
  end
146
145
 
147
146
  def session_settings
148
147
  @rails.dig(:action_controller, :session)
149
148
  end
150
-
151
- private
152
-
153
- # Brakeman does not support Haml 5 yet.
154
- # (https://github.com/presidentbeef/brakeman/issues/1370) Fortunately, Haml
155
- # 5 doesn't add much new syntax, and brakeman (which uses the haml 4 parser)
156
- # will parse most Haml 5 files without errors. Therefore, we only print a
157
- # warning here, instead of raising an error.
158
- def check_haml_version
159
- return if supported_haml_version?
160
- Brakeman.notify <<~EOS
161
- Brakeman does not fully support Haml 5 yet. Your Gemfile (or gemspec)
162
- allows Haml 5. Brakeman uses the Haml 4 parser and thus may produce
163
- errors when parsing Haml 5 syntax. If Brakeman encounters such an error,
164
- it will be unable to scan that file.
165
- EOS
166
- end
167
-
168
- def convert_version_number value
169
- if value.match(/\A\d+\z/)
170
- value.to_i
171
- else
172
- value
173
- end
174
- end
175
-
176
- def lower? lhs, rhs
177
- if lhs.class == rhs.class
178
- lhs < rhs
179
- else
180
- false
181
- end
182
- end
183
-
184
- # Brakeman does not support Haml 5 yet. See `check_haml_version`.
185
- #
186
- # This method is messy because all we have is a version `Requirement` (like
187
- # `~> 5.0.3`, or `> 4.99`) The only way I could think of was to loop over
188
- # known Haml 5 versions and test whether the `Requirement` is satisfied. I
189
- # included '5.99.99' in the list so that this method might stand a chance of
190
- # working in the future.
191
- def supported_haml_version?
192
- haml_version = gem_version(:haml)
193
- return true unless haml_version
194
-
195
- requirement = Gem::Requirement.new(haml_version)
196
- ['5.99.99', '5.1.1', '5.1.0', '5.0.4', '5.0.3', '5.0.2', '5.0.1', '5.0.0'].none? do |v|
197
- requirement.satisfied_by?(Gem::Version.new(v))
198
- end
199
- end
200
-
201
- def higher? lhs, rhs
202
- if lhs.class == rhs.class
203
- lhs > rhs
204
- else
205
- false
206
- end
207
- end
208
149
  end
209
150
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "4.6.1"
2
+ Version = "4.7.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.1
4
+ version: 4.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain:
11
11
  - brakeman-public_cert.pem
12
- date: 2019-07-24 00:00:00.000000000 Z
12
+ date: 2019-10-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -169,22 +169,16 @@ dependencies:
169
169
  name: haml
170
170
  requirement: !ruby/object:Gem::Requirement
171
171
  requirements:
172
- - - ">="
173
- - !ruby/object:Gem::Version
174
- version: '3.0'
175
- - - "<"
172
+ - - "~>"
176
173
  - !ruby/object:Gem::Version
177
- version: '5.0'
174
+ version: '5.1'
178
175
  type: :runtime
179
176
  prerelease: false
180
177
  version_requirements: !ruby/object:Gem::Requirement
181
178
  requirements:
182
- - - ">="
183
- - !ruby/object:Gem::Version
184
- version: '3.0'
185
- - - "<"
179
+ - - "~>"
186
180
  - !ruby/object:Gem::Version
187
- version: '5.0'
181
+ version: '5.1'
188
182
  - !ruby/object:Gem::Dependency
189
183
  name: slim
190
184
  requirement: !ruby/object:Gem::Requirement