brakeman 0.3.2 → 0.4.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.
- data/bin/brakeman +4 -0
- data/lib/checks/check_cross_site_scripting.rb +14 -2
- data/lib/processor.rb +2 -0
- data/lib/processors/erubis_template_processor.rb +26 -3
- data/lib/processors/output_processor.rb +17 -0
- data/lib/scanner.rb +38 -11
- data/lib/version.rb +1 -1
- metadata +4 -4
data/bin/brakeman
CHANGED
@@ -38,6 +38,10 @@ OptionParser.new do |opts|
|
|
38
38
|
options[:ignore_model_output] = true
|
39
39
|
end
|
40
40
|
|
41
|
+
opts.on "-e", "--escape-html", "Escape HTML by default" do
|
42
|
+
options[:escape_html] = true
|
43
|
+
end
|
44
|
+
|
41
45
|
opts.on "-r", "--report-direct", "Only report direct use of untrusted data" do |option|
|
42
46
|
options[:check_arguments] = !option
|
43
47
|
end
|
@@ -30,6 +30,8 @@ class CheckCrossSiteScripting < BaseCheck
|
|
30
30
|
|
31
31
|
HAML_HELPERS = Sexp.new(:colon2, Sexp.new(:const, :Haml), :Helpers)
|
32
32
|
|
33
|
+
XML_HELPER = Sexp.new(:colon2, Sexp.new(:const, :Erubis), :XmlHelper)
|
34
|
+
|
33
35
|
URI = Sexp.new(:const, :URI)
|
34
36
|
|
35
37
|
CGI = Sexp.new(:const, :CGI)
|
@@ -48,7 +50,7 @@ class CheckCrossSiteScripting < BaseCheck
|
|
48
50
|
@current_template = template
|
49
51
|
|
50
52
|
template[:outputs].each do |out|
|
51
|
-
type, match = has_immediate_user_input?(out[1])
|
53
|
+
type, match = (out[0] == :output and has_immediate_user_input?(out[1]))
|
52
54
|
if type and not duplicate? out
|
53
55
|
add_result out
|
54
56
|
case type
|
@@ -102,6 +104,16 @@ class CheckCrossSiteScripting < BaseCheck
|
|
102
104
|
process exp[1].dup
|
103
105
|
end
|
104
106
|
|
107
|
+
#Look for calls to raw()
|
108
|
+
#Otherwise, ignore
|
109
|
+
def process_escaped_output exp
|
110
|
+
if exp[1].node_type == :call and exp[1][2] == :raw
|
111
|
+
process_output exp
|
112
|
+
else
|
113
|
+
exp
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
105
117
|
#Check a call for user input
|
106
118
|
#
|
107
119
|
#
|
@@ -154,6 +166,7 @@ class CheckCrossSiteScripting < BaseCheck
|
|
154
166
|
(@matched == :model and IGNORE_MODEL_METHODS.include? method) or
|
155
167
|
(target == HAML_HELPERS and method == :html_escape) or
|
156
168
|
((target == URI or target == CGI) and method == :escape) or
|
169
|
+
(target == XML_HELPER and method == :escape_xml) or
|
157
170
|
(target == FORM_BUILDER and IGNORE_METHODS.include? method) or
|
158
171
|
(method.to_s[-1,1] == "?")
|
159
172
|
|
@@ -163,7 +176,6 @@ class CheckCrossSiteScripting < BaseCheck
|
|
163
176
|
|
164
177
|
@matched = :model
|
165
178
|
elsif @inspect_arguments and (ALL_PARAMETERS.include?(exp) or params? exp)
|
166
|
-
|
167
179
|
@matched = :params
|
168
180
|
elsif @inspect_arguments
|
169
181
|
process args
|
data/lib/processor.rb
CHANGED
@@ -50,6 +50,8 @@ class Processor
|
|
50
50
|
result = ErbTemplateProcessor.new(@tracker, name, called_from, file_name).process src
|
51
51
|
when :haml
|
52
52
|
result = HamlTemplateProcessor.new(@tracker, name, called_from, file_name).process src
|
53
|
+
when :erubis
|
54
|
+
result = ErubisTemplateProcessor.new(@tracker, name, called_from, file_name).process src
|
53
55
|
else
|
54
56
|
abort "Unknown template type: #{type} (#{name})"
|
55
57
|
end
|
@@ -2,6 +2,7 @@ require 'processors/template_processor'
|
|
2
2
|
|
3
3
|
#Processes ERB templates using Erubis instead of erb.
|
4
4
|
class ErubisTemplateProcessor < TemplateProcessor
|
5
|
+
|
5
6
|
|
6
7
|
#s(:call, TARGET, :method, s(:arglist))
|
7
8
|
def process_call exp
|
@@ -10,10 +11,10 @@ class ErubisTemplateProcessor < TemplateProcessor
|
|
10
11
|
target = process target
|
11
12
|
end
|
12
13
|
method = exp[2]
|
13
|
-
|
14
|
+
|
14
15
|
#_buf is the default output variable for Erubis
|
15
|
-
if target and target[1] == :_buf
|
16
|
-
if method == :<<
|
16
|
+
if target and (target[1] == :_buf or target[1] == :output_buffer)
|
17
|
+
if method == :<< or method == :safe_concat
|
17
18
|
args = exp[3][1] = process(exp[3][1])
|
18
19
|
|
19
20
|
if args.node_type == :call and args[2] == :to_s #just calling to_s on inner code
|
@@ -59,4 +60,26 @@ class ErubisTemplateProcessor < TemplateProcessor
|
|
59
60
|
block.line(exp.line)
|
60
61
|
block
|
61
62
|
end
|
63
|
+
|
64
|
+
#Look for assignments to output buffer
|
65
|
+
def process_attrasgn exp
|
66
|
+
if exp[1].node_type == :ivar and exp[1][1] == :@output_buffer
|
67
|
+
if exp[2] == :append= or exp[2] == :safe_append=
|
68
|
+
args = exp[3][1] = process(exp[3][1])
|
69
|
+
|
70
|
+
if args.node_type == :str
|
71
|
+
ignore
|
72
|
+
else
|
73
|
+
s = Sexp.new :escaped_output, args
|
74
|
+
s.line(exp.line)
|
75
|
+
@current_template[:outputs] << s
|
76
|
+
s
|
77
|
+
end
|
78
|
+
else
|
79
|
+
ignore
|
80
|
+
end
|
81
|
+
else
|
82
|
+
super
|
83
|
+
end
|
84
|
+
end
|
62
85
|
end
|
@@ -153,6 +153,23 @@ class OutputProcessor < Ruby2Ruby
|
|
153
153
|
out
|
154
154
|
end
|
155
155
|
|
156
|
+
def process_escaped_output exp
|
157
|
+
out = if exp[0].node_type == :str
|
158
|
+
""
|
159
|
+
else
|
160
|
+
res = process exp[0]
|
161
|
+
|
162
|
+
if res == ""
|
163
|
+
""
|
164
|
+
else
|
165
|
+
"[Escaped Output] #{res}"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
exp.clear
|
169
|
+
out
|
170
|
+
end
|
171
|
+
|
172
|
+
|
156
173
|
def process_format exp
|
157
174
|
out = if exp[0].node_type == :str or exp[0].node_type == :ignore
|
158
175
|
""
|
data/lib/scanner.rb
CHANGED
@@ -17,6 +17,10 @@ class ScannerErubis < Erubis::Eruby
|
|
17
17
|
include Erubis::NoTextEnhancer
|
18
18
|
end
|
19
19
|
|
20
|
+
class ErubisEscape < ScannerErubis
|
21
|
+
include Erubis::EscapeEnhancer
|
22
|
+
end
|
23
|
+
|
20
24
|
#Scans the Rails application.
|
21
25
|
class Scanner
|
22
26
|
|
@@ -61,7 +65,9 @@ class Scanner
|
|
61
65
|
@processor.process_config(RubyParser.new.parse(File.read("#@path/config/gems.rb")))
|
62
66
|
end
|
63
67
|
|
64
|
-
if File.exists? "#@path/vendor/plugins/rails_xss" or
|
68
|
+
if File.exists? "#@path/vendor/plugins/rails_xss" or
|
69
|
+
OPTIONS[:rails3] or OPTIONS[:escape_html] or
|
70
|
+
(File.exists? "#@path/Gemfile" and File.read("#@path/Gemfile").include? "rails_xss")
|
65
71
|
tracker.config[:escape_html] = true
|
66
72
|
warn "[Notice] Escaping HTML by default"
|
67
73
|
end
|
@@ -143,8 +149,14 @@ class Scanner
|
|
143
149
|
begin
|
144
150
|
if type == :erb
|
145
151
|
if tracker.config[:escape_html]
|
146
|
-
|
152
|
+
type = :erubis
|
153
|
+
if OPTIONS[:rails3]
|
154
|
+
src = RailsXSSErubis.new(File.read(f)).src
|
155
|
+
else
|
156
|
+
src = ErubisEscape.new(File.read(f)).src
|
157
|
+
end
|
147
158
|
elsif tracker.config[:erubis]
|
159
|
+
type = :erubis
|
148
160
|
src = ScannerErubis.new(File.read(f)).src
|
149
161
|
else
|
150
162
|
src = ERB.new(File.read(f), nil, "-").src
|
@@ -200,34 +212,49 @@ class Scanner
|
|
200
212
|
end
|
201
213
|
end
|
202
214
|
|
203
|
-
#This is from the
|
204
|
-
#except we don't care about plain text.
|
215
|
+
#This is from Rails 3 version of the Erubis handler
|
205
216
|
class RailsXSSErubis < ::Erubis::Eruby
|
206
217
|
include Erubis::NoTextEnhancer
|
207
218
|
|
208
219
|
#Initializes output buffer.
|
209
220
|
def add_preamble(src)
|
210
|
-
src << "
|
221
|
+
# src << "_buf = ActionView::SafeBuffer.new;\n"
|
211
222
|
end
|
212
223
|
|
213
224
|
#This does nothing.
|
214
225
|
def add_text(src, text)
|
215
|
-
#
|
226
|
+
# src << "@output_buffer << ('" << escape_text(text) << "'.html_safe!);"
|
216
227
|
end
|
217
228
|
|
218
|
-
|
229
|
+
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
230
|
+
|
219
231
|
def add_expr_literal(src, code)
|
220
|
-
|
232
|
+
if code =~ BLOCK_EXPR
|
233
|
+
src << '@output_buffer.append= ' << code
|
234
|
+
else
|
235
|
+
src << '@output_buffer.append= (' << code << ');'
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def add_stmt(src, code)
|
240
|
+
if code =~ BLOCK_EXPR
|
241
|
+
src << '@output_buffer.append_if_string= ' << code
|
242
|
+
else
|
243
|
+
super
|
244
|
+
end
|
221
245
|
end
|
222
246
|
|
223
|
-
#Add an expression to the output buffer after escaping it.
|
224
247
|
def add_expr_escaped(src, code)
|
225
|
-
|
248
|
+
if code =~ BLOCK_EXPR
|
249
|
+
src << "@output_buffer.safe_append= " << code
|
250
|
+
else
|
251
|
+
src << "@output_buffer.safe_concat(" << code << ");"
|
252
|
+
end
|
226
253
|
end
|
227
254
|
|
228
255
|
#Add code to output buffer.
|
229
256
|
def add_postamble(src)
|
230
|
-
src << '
|
257
|
+
# src << '_buf.to_s'
|
231
258
|
end
|
232
259
|
end
|
233
260
|
|
data/lib/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Version = "0.
|
1
|
+
Version = "0.4.0"
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Justin Collins
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-05-
|
17
|
+
date: 2011-05-18 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|