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 +4 -4
- data/CHANGES.md +11 -0
- data/lib/brakeman/checks/base_check.rb +23 -1
- data/lib/brakeman/checks/check_cookie_serialization.rb +1 -1
- data/lib/brakeman/checks/check_cross_site_scripting.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +26 -1
- data/lib/brakeman/differ.rb +16 -28
- data/lib/brakeman/parsers/haml_embedded.rb +1 -1
- data/lib/brakeman/parsers/template_parser.rb +3 -1
- data/lib/brakeman/processors/alias_processor.rb +10 -0
- data/lib/brakeman/processors/base_processor.rb +2 -0
- data/lib/brakeman/processors/haml_template_processor.rb +86 -122
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +1 -1
- data/lib/brakeman/processors/template_alias_processor.rb +28 -0
- data/lib/brakeman/tracker/config.rb +33 -92
- data/lib/brakeman/version.rb +1 -1
- metadata +6 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e29d6575b47438b336589ce753da72098ad1b7465a4bb78625d5c666d8c1d0a3
|
|
4
|
+
data.tar.gz: 073d249eb7c1915c3c3be01345c25d050127eec929ff2dd77e276433f94606c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
-
|
|
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)
|
data/lib/brakeman/differ.rb
CHANGED
|
@@ -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
|
-
|
|
30
|
-
|
|
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
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
|
|
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
|
|
@@ -2,8 +2,10 @@ require 'brakeman/processors/template_processor'
|
|
|
2
2
|
|
|
3
3
|
#Processes HAML templates.
|
|
4
4
|
class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
|
|
5
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
exp.
|
|
106
|
-
exp
|
|
107
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
|
177
|
-
exp.map! { |e| get_pushed_value
|
|
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
|
-
|
|
187
|
-
if
|
|
188
|
-
|
|
189
|
-
elsif
|
|
190
|
-
|
|
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.
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
data/lib/brakeman/version.rb
CHANGED
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.
|
|
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-
|
|
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.
|
|
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.
|
|
181
|
+
version: '5.1'
|
|
188
182
|
- !ruby/object:Gem::Dependency
|
|
189
183
|
name: slim
|
|
190
184
|
requirement: !ruby/object:Gem::Requirement
|