brakeman-min 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/FEATURES +16 -0
- data/README.md +118 -0
- data/WARNING_TYPES +69 -0
- data/bin/brakeman +269 -0
- data/lib/checks.rb +67 -0
- data/lib/checks/base_check.rb +353 -0
- data/lib/checks/check_cross_site_scripting.rb +324 -0
- data/lib/checks/check_default_routes.rb +29 -0
- data/lib/checks/check_evaluation.rb +27 -0
- data/lib/checks/check_execute.rb +110 -0
- data/lib/checks/check_file_access.rb +46 -0
- data/lib/checks/check_forgery_setting.rb +42 -0
- data/lib/checks/check_mail_to.rb +48 -0
- data/lib/checks/check_mass_assignment.rb +72 -0
- data/lib/checks/check_model_attributes.rb +36 -0
- data/lib/checks/check_nested_attributes.rb +34 -0
- data/lib/checks/check_redirect.rb +98 -0
- data/lib/checks/check_render.rb +65 -0
- data/lib/checks/check_send_file.rb +15 -0
- data/lib/checks/check_session_settings.rb +36 -0
- data/lib/checks/check_sql.rb +124 -0
- data/lib/checks/check_validation_regex.rb +60 -0
- data/lib/format/style.css +105 -0
- data/lib/processor.rb +83 -0
- data/lib/processors/alias_processor.rb +384 -0
- data/lib/processors/base_processor.rb +237 -0
- data/lib/processors/config_processor.rb +146 -0
- data/lib/processors/controller_alias_processor.rb +237 -0
- data/lib/processors/controller_processor.rb +202 -0
- data/lib/processors/erb_template_processor.rb +84 -0
- data/lib/processors/erubis_template_processor.rb +62 -0
- data/lib/processors/haml_template_processor.rb +131 -0
- data/lib/processors/lib/find_call.rb +176 -0
- data/lib/processors/lib/find_model_call.rb +39 -0
- data/lib/processors/lib/processor_helper.rb +36 -0
- data/lib/processors/lib/render_helper.rb +137 -0
- data/lib/processors/library_processor.rb +118 -0
- data/lib/processors/model_processor.rb +125 -0
- data/lib/processors/output_processor.rb +233 -0
- data/lib/processors/params_processor.rb +77 -0
- data/lib/processors/route_processor.rb +338 -0
- data/lib/processors/template_alias_processor.rb +86 -0
- data/lib/processors/template_processor.rb +55 -0
- data/lib/report.rb +651 -0
- data/lib/scanner.rb +215 -0
- data/lib/scanner_erubis.rb +43 -0
- data/lib/tracker.rb +144 -0
- data/lib/util.rb +141 -0
- data/lib/version.rb +1 -0
- data/lib/warning.rb +97 -0
- metadata +141 -0
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'ruby_parser'
|
2
|
+
require 'processors/base_processor'
|
3
|
+
|
4
|
+
#Processes controller. Results are put in tracker.controllers
|
5
|
+
class ControllerProcessor < BaseProcessor
|
6
|
+
FORMAT_HTML = Sexp.new(:call, Sexp.new(:lvar, :format), :html, Sexp.new(:arglist))
|
7
|
+
|
8
|
+
def initialize tracker
|
9
|
+
super
|
10
|
+
@controller = nil
|
11
|
+
@current_method = nil
|
12
|
+
@current_module = nil
|
13
|
+
@visibility = :public
|
14
|
+
@file_name = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
#Use this method to process a Controller
|
18
|
+
def process_controller src, file_name = nil
|
19
|
+
@file_name = file_name
|
20
|
+
process src
|
21
|
+
end
|
22
|
+
|
23
|
+
#s(:class, NAME, PARENT, s(:scope ...))
|
24
|
+
def process_class exp
|
25
|
+
if @controller
|
26
|
+
warn "[Notice] Skipping inner class: #{class_name exp[1]}" if OPTIONS[:debug]
|
27
|
+
return ignore
|
28
|
+
end
|
29
|
+
|
30
|
+
name = class_name(exp[1])
|
31
|
+
if @current_module
|
32
|
+
name = (@current_module + "::" + name.to_s).to_sym
|
33
|
+
end
|
34
|
+
@controller = { :name => name,
|
35
|
+
:parent => class_name(exp[2]),
|
36
|
+
:includes => [],
|
37
|
+
:public => {},
|
38
|
+
:private => {},
|
39
|
+
:protected => {},
|
40
|
+
:options => {},
|
41
|
+
:src => exp,
|
42
|
+
:file => @file_name }
|
43
|
+
@tracker.controllers[@controller[:name]] = @controller
|
44
|
+
exp[3] = process exp[3]
|
45
|
+
set_layout_name
|
46
|
+
@controller = nil
|
47
|
+
exp
|
48
|
+
end
|
49
|
+
|
50
|
+
#Look for specific calls inside the controller
|
51
|
+
def process_call exp
|
52
|
+
target = exp[1]
|
53
|
+
if sexp? target
|
54
|
+
target = process target
|
55
|
+
end
|
56
|
+
|
57
|
+
method = exp[2]
|
58
|
+
args = exp[3]
|
59
|
+
|
60
|
+
#Methods called inside class definition
|
61
|
+
#like attr_* and other settings
|
62
|
+
if @current_method.nil? and target.nil?
|
63
|
+
if args.length == 1 #actually, empty
|
64
|
+
case method
|
65
|
+
when :private, :protected, :public
|
66
|
+
@visibility = method
|
67
|
+
when :protect_from_forgery
|
68
|
+
@controller[:options][:protect_from_forgery] = true
|
69
|
+
else
|
70
|
+
#??
|
71
|
+
end
|
72
|
+
else
|
73
|
+
case method
|
74
|
+
when :include
|
75
|
+
@controller[:includes] << class_name(args[1]) if @controller
|
76
|
+
when :before_filter
|
77
|
+
@controller[:options][:before_filters] ||= []
|
78
|
+
@controller[:options][:before_filters] << args[1..-1]
|
79
|
+
when :layout
|
80
|
+
if string? args[-1]
|
81
|
+
#layout "some_layout"
|
82
|
+
|
83
|
+
name = args[-1][1].to_s
|
84
|
+
unless Dir.glob("#{OPTIONS[:app_path]}/app/views/layouts/#{name}.html.{erb,haml}").empty?
|
85
|
+
@controller[:layout] = "layouts/#{name}"
|
86
|
+
else
|
87
|
+
warn "[Notice] Layout not found: #{name}" if OPTIONS[:debug]
|
88
|
+
end
|
89
|
+
elsif sexp? args[-1] and (args[-1][0] == :nil or args[-1][0] == :false)
|
90
|
+
#layout :false or layout nil
|
91
|
+
@controller[:layout] = false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
ignore
|
96
|
+
elsif target == nil and method == :render
|
97
|
+
make_render exp
|
98
|
+
elsif exp == FORMAT_HTML and context[1] != :iter
|
99
|
+
#This is an empty call to
|
100
|
+
# format.html
|
101
|
+
#Which renders the default template if no arguments
|
102
|
+
#Need to make more generic, though.
|
103
|
+
call = Sexp.new :render, :default, @current_method
|
104
|
+
call.line(exp.line)
|
105
|
+
call
|
106
|
+
else
|
107
|
+
call = Sexp.new :call, target, method, process(args)
|
108
|
+
call.line(exp.line)
|
109
|
+
call
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#Process method definition and store in Tracker
|
114
|
+
def process_defn exp
|
115
|
+
name = exp[1]
|
116
|
+
@current_method = name
|
117
|
+
res = Sexp.new :methdef, name, process(exp[2]), process(exp[3][1])
|
118
|
+
res.line(exp.line)
|
119
|
+
@current_method = nil
|
120
|
+
@controller[@visibility][name] = res unless @controller.nil?
|
121
|
+
|
122
|
+
res
|
123
|
+
end
|
124
|
+
|
125
|
+
#Process self.method definition and store in Tracker
|
126
|
+
def process_defs exp
|
127
|
+
name = exp[2]
|
128
|
+
|
129
|
+
if exp[1].node_type == :self
|
130
|
+
target = @controller[:name]
|
131
|
+
else
|
132
|
+
target = class_name exp[1]
|
133
|
+
end
|
134
|
+
|
135
|
+
@current_method = name
|
136
|
+
res = Sexp.new :selfdef, target, name, process(exp[3]), process(exp[4][1])
|
137
|
+
res.line(exp.line)
|
138
|
+
@current_method = nil
|
139
|
+
@controller[@visibility][name] = res unless @controller.nil?
|
140
|
+
|
141
|
+
res
|
142
|
+
end
|
143
|
+
|
144
|
+
#Look for before_filters and add fake ones if necessary
|
145
|
+
def process_iter exp
|
146
|
+
if exp[1][2] == :before_filter
|
147
|
+
add_fake_filter exp
|
148
|
+
else
|
149
|
+
super
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
#Sets default layout for renders inside Controller
|
154
|
+
def set_layout_name
|
155
|
+
return if @controller[:layout]
|
156
|
+
|
157
|
+
name = underscore(@controller[:name].to_s.split("::")[-1].gsub("Controller", ''))
|
158
|
+
|
159
|
+
#There is a layout for this Controller
|
160
|
+
unless Dir.glob("#{OPTIONS[:app_path]}/app/views/layouts/#{name}.html.{erb,haml}").empty?
|
161
|
+
@controller[:layout] = "layouts/#{name}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
#This is to handle before_filter do |controller| ... end
|
166
|
+
#
|
167
|
+
#We build a new method and process that the same way as usual
|
168
|
+
#methods and filters.
|
169
|
+
def add_fake_filter exp
|
170
|
+
filter_name = ("fake_filter" + rand.to_s[/\d+$/]).to_sym
|
171
|
+
args = exp[1][3]
|
172
|
+
args.insert(1, Sexp.new(:lit, filter_name))
|
173
|
+
before_filter_call = Sexp.new(:call, nil, :before_filter, args)
|
174
|
+
|
175
|
+
if exp[2]
|
176
|
+
block_variable = exp[2][1]
|
177
|
+
else
|
178
|
+
block_variable = :temp
|
179
|
+
end
|
180
|
+
|
181
|
+
if sexp? exp[3] and exp[3].node_type == :block
|
182
|
+
block_inner = exp[3][1..-1]
|
183
|
+
else
|
184
|
+
block_inner = [exp[3]]
|
185
|
+
end
|
186
|
+
|
187
|
+
#Build Sexp for filter method
|
188
|
+
body = Sexp.new(:scope,
|
189
|
+
Sexp.new(:block,
|
190
|
+
Sexp.new(:lasgn, block_variable,
|
191
|
+
Sexp.new(:call, Sexp.new(:const, @controller[:name]), :new, Sexp.new(:arglist)))).concat(block_inner))
|
192
|
+
|
193
|
+
filter_method = Sexp.new(:defn, filter_name, Sexp.new(:args), body).line(exp.line)
|
194
|
+
|
195
|
+
vis = @visibility
|
196
|
+
@visibility = :private
|
197
|
+
process_defn filter_method
|
198
|
+
@visibility = vis
|
199
|
+
process before_filter_call
|
200
|
+
exp
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'processors/template_processor'
|
2
|
+
|
3
|
+
#Processes ERB templates
|
4
|
+
#(those ending in .html.erb or .rthml).
|
5
|
+
class ErbTemplateProcessor < TemplateProcessor
|
6
|
+
|
7
|
+
#s(:call, TARGET, :method, s(:arglist))
|
8
|
+
def process_call exp
|
9
|
+
target = exp[1]
|
10
|
+
if sexp? target
|
11
|
+
target = process target
|
12
|
+
end
|
13
|
+
method = exp[2]
|
14
|
+
|
15
|
+
#_erbout is the default output variable for erb
|
16
|
+
if target and target[1] == :_erbout
|
17
|
+
if method == :concat
|
18
|
+
@inside_concat = true
|
19
|
+
args = exp[3] = process(exp[3])
|
20
|
+
@inside_concat = false
|
21
|
+
|
22
|
+
if args.length > 2
|
23
|
+
raise Exception.new("Did not expect more than a single argument to _erbout.concat")
|
24
|
+
end
|
25
|
+
|
26
|
+
args = args[1]
|
27
|
+
|
28
|
+
if args.node_type == :call and args[2] == :to_s #erb always calls to_s on output
|
29
|
+
args = args[1]
|
30
|
+
end
|
31
|
+
|
32
|
+
if args.node_type == :str #ignore plain strings
|
33
|
+
ignore
|
34
|
+
else
|
35
|
+
s = Sexp.new :output, args
|
36
|
+
s.line(exp.line)
|
37
|
+
@current_template[:outputs] << s
|
38
|
+
s
|
39
|
+
end
|
40
|
+
elsif method == :force_encoding
|
41
|
+
ignore
|
42
|
+
else
|
43
|
+
abort "Unrecognized action on _erbout: #{method}"
|
44
|
+
end
|
45
|
+
elsif target == nil and method == :render
|
46
|
+
exp[3] = process(exp[3])
|
47
|
+
make_render exp
|
48
|
+
else
|
49
|
+
args = exp[3] = process(exp[3])
|
50
|
+
call = Sexp.new :call, target, method, args
|
51
|
+
call.line(exp.line)
|
52
|
+
call
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#Process block, removing irrelevant expressions
|
57
|
+
def process_block exp
|
58
|
+
exp.shift
|
59
|
+
if @inside_concat
|
60
|
+
@inside_concat = false
|
61
|
+
exp[0..-2].each do |e|
|
62
|
+
process e
|
63
|
+
end
|
64
|
+
@inside_concat = true
|
65
|
+
process exp[-1]
|
66
|
+
else
|
67
|
+
exp.map! do |e|
|
68
|
+
res = process e
|
69
|
+
if res.empty? or res == ignore
|
70
|
+
nil
|
71
|
+
elsif sexp? res and res.node_type == :lvar and res[1] == :_erbout
|
72
|
+
nil
|
73
|
+
|
74
|
+
else
|
75
|
+
res
|
76
|
+
end
|
77
|
+
end
|
78
|
+
block = Sexp.new(:rlist).concat(exp).compact
|
79
|
+
block.line(exp.line)
|
80
|
+
block
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'processors/template_processor'
|
2
|
+
|
3
|
+
#Processes ERB templates using Erubis instead of erb.
|
4
|
+
class ErubisTemplateProcessor < TemplateProcessor
|
5
|
+
|
6
|
+
#s(:call, TARGET, :method, s(:arglist))
|
7
|
+
def process_call exp
|
8
|
+
target = exp[1]
|
9
|
+
if sexp? target
|
10
|
+
target = process target
|
11
|
+
end
|
12
|
+
method = exp[2]
|
13
|
+
|
14
|
+
#_buf is the default output variable for Erubis
|
15
|
+
if target and target[1] == :_buf
|
16
|
+
if method == :<<
|
17
|
+
args = exp[3][1] = process(exp[3][1])
|
18
|
+
|
19
|
+
if args.node_type == :call and args[2] == :to_s #just calling to_s on inner code
|
20
|
+
args = args[1]
|
21
|
+
end
|
22
|
+
|
23
|
+
if args.node_type == :str #ignore plain strings
|
24
|
+
ignore
|
25
|
+
else
|
26
|
+
s = Sexp.new :output, args
|
27
|
+
s.line(exp.line)
|
28
|
+
@current_template[:outputs] << s
|
29
|
+
s
|
30
|
+
end
|
31
|
+
elsif method == :to_s
|
32
|
+
ignore
|
33
|
+
else
|
34
|
+
abort "Unrecognized action on _buf: #{method}"
|
35
|
+
end
|
36
|
+
elsif target == nil and method == :render
|
37
|
+
exp[3] = process exp[3]
|
38
|
+
make_render exp
|
39
|
+
else
|
40
|
+
args = exp[3] = process(exp[3])
|
41
|
+
call = Sexp.new :call, target, method, args
|
42
|
+
call.line(exp.line)
|
43
|
+
call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
#Process blocks, ignoring :ignore exps
|
48
|
+
def process_block exp
|
49
|
+
exp.shift
|
50
|
+
exp.map! do |e|
|
51
|
+
res = process e
|
52
|
+
if res.empty? or res == ignore
|
53
|
+
nil
|
54
|
+
else
|
55
|
+
res
|
56
|
+
end
|
57
|
+
end
|
58
|
+
block = Sexp.new(:rlist).concat(exp).compact
|
59
|
+
block.line(exp.line)
|
60
|
+
block
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'processors/template_processor'
|
2
|
+
|
3
|
+
#Processes HAML templates.
|
4
|
+
class HamlTemplateProcessor < TemplateProcessor
|
5
|
+
HAML_FORMAT_METHOD = /format_script_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)/
|
6
|
+
|
7
|
+
def initialize *args
|
8
|
+
super
|
9
|
+
|
10
|
+
@tracker.libs.each do |name, lib|
|
11
|
+
if name.to_s =~ /^Haml::Filters/
|
12
|
+
begin
|
13
|
+
require lib[:file]
|
14
|
+
rescue Exception => e
|
15
|
+
if OPTIONS[:debug]
|
16
|
+
raise e
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#Processes call, looking for template output
|
24
|
+
def process_call exp
|
25
|
+
target = exp[1]
|
26
|
+
if sexp? target
|
27
|
+
target = process target
|
28
|
+
end
|
29
|
+
|
30
|
+
method = exp[2]
|
31
|
+
|
32
|
+
if (sexp? target and target[2] == :_hamlout) or target == :_hamlout
|
33
|
+
res = case method
|
34
|
+
when :adjust_tabs, :rstrip!
|
35
|
+
ignore
|
36
|
+
when :options
|
37
|
+
Sexp.new :call, :_hamlout, :options, exp[3]
|
38
|
+
when :buffer
|
39
|
+
Sexp.new :call, :_hamlout, :buffer, exp[3]
|
40
|
+
when :open_tag
|
41
|
+
Sexp.new(:tag, process(exp[3]))
|
42
|
+
else
|
43
|
+
arg = exp[3][1]
|
44
|
+
|
45
|
+
if arg
|
46
|
+
@inside_concat = true
|
47
|
+
out = exp[3][1] = process(arg)
|
48
|
+
@inside_concat = false
|
49
|
+
else
|
50
|
+
raise Exception.new("Empty _hamlout.#{method}()?")
|
51
|
+
end
|
52
|
+
|
53
|
+
if string? out
|
54
|
+
ignore
|
55
|
+
else
|
56
|
+
case method.to_s
|
57
|
+
when "push_text"
|
58
|
+
s = Sexp.new(:output, out)
|
59
|
+
@current_template[:outputs] << s
|
60
|
+
s
|
61
|
+
when HAML_FORMAT_METHOD
|
62
|
+
if $4 == "true"
|
63
|
+
Sexp.new :format_escaped, out
|
64
|
+
else
|
65
|
+
Sexp.new :format, out
|
66
|
+
end
|
67
|
+
else
|
68
|
+
raise Exception.new("Unrecognized action on _hamlout: #{method}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
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
|
+
elsif sexp? target and method == :<< and is_buffer_target? target
|
80
|
+
@inside_concat = true
|
81
|
+
out = exp[3][1] = process(exp[3][1])
|
82
|
+
@inside_concat = false
|
83
|
+
|
84
|
+
if out.node_type == :str #ignore plain strings
|
85
|
+
ignore
|
86
|
+
else
|
87
|
+
s = Sexp.new(:output, out)
|
88
|
+
@current_template[:outputs] << s
|
89
|
+
s.line(exp.line)
|
90
|
+
s
|
91
|
+
end
|
92
|
+
elsif target == nil and method == :render
|
93
|
+
#Process call to render()
|
94
|
+
exp[3] = process exp[3]
|
95
|
+
make_render exp
|
96
|
+
else
|
97
|
+
args = process exp[3]
|
98
|
+
call = Sexp.new :call, target, method, args
|
99
|
+
call.line(exp.line)
|
100
|
+
call
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#If inside an output stream, only return the final expression
|
105
|
+
def process_block exp
|
106
|
+
exp.shift
|
107
|
+
if @inside_concat
|
108
|
+
@inside_concat = false
|
109
|
+
exp[0..-2].each do |e|
|
110
|
+
process e
|
111
|
+
end
|
112
|
+
@inside_concat = true
|
113
|
+
process exp[-1]
|
114
|
+
else
|
115
|
+
exp.map! do |e|
|
116
|
+
res = process e
|
117
|
+
if res.empty?
|
118
|
+
nil
|
119
|
+
else
|
120
|
+
res
|
121
|
+
end
|
122
|
+
end
|
123
|
+
Sexp.new(:rlist).concat(exp).compact
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
#Checks if the buffer is the target in a method call Sexp.
|
128
|
+
def is_buffer_target? exp
|
129
|
+
exp.node_type == :call and exp[1] == :_hamlout and exp[2] == :buffer
|
130
|
+
end
|
131
|
+
end
|