brakeman 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/FEATURES +16 -0
- data/README.md +112 -0
- data/WARNING_TYPES +69 -0
- data/bin/brakeman +266 -0
- data/lib/checks.rb +67 -0
- data/lib/checks/base_check.rb +338 -0
- data/lib/checks/check_cross_site_scripting.rb +216 -0
- data/lib/checks/check_default_routes.rb +29 -0
- data/lib/checks/check_evaluation.rb +29 -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 +25 -0
- data/lib/checks/check_mass_assignment.rb +72 -0
- data/lib/checks/check_model_attributes.rb +36 -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 +235 -0
- data/lib/processors/config_processor.rb +146 -0
- data/lib/processors/controller_alias_processor.rb +222 -0
- data/lib/processors/controller_processor.rb +175 -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 +115 -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 +118 -0
- data/lib/processors/library_processor.rb +117 -0
- data/lib/processors/model_processor.rb +125 -0
- data/lib/processors/output_processor.rb +204 -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 +628 -0
- data/lib/scanner.rb +232 -0
- data/lib/tracker.rb +144 -0
- data/lib/util.rb +141 -0
- data/lib/warning.rb +97 -0
- metadata +191 -0
@@ -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,115 @@
|
|
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
|
+
#Processes call, looking for template output
|
8
|
+
def process_call exp
|
9
|
+
target = exp[1]
|
10
|
+
if sexp? target
|
11
|
+
target = process target
|
12
|
+
end
|
13
|
+
|
14
|
+
method = exp[2]
|
15
|
+
|
16
|
+
if (sexp? target and target[2] == :_hamlout) or target == :_hamlout
|
17
|
+
res = case method
|
18
|
+
when :adjust_tabs, :rstrip!
|
19
|
+
ignore
|
20
|
+
when :options
|
21
|
+
Sexp.new :call, :_hamlout, :options, exp[3]
|
22
|
+
when :buffer
|
23
|
+
Sexp.new :call, :_hamlout, :buffer, exp[3]
|
24
|
+
when :open_tag
|
25
|
+
Sexp.new(:tag, process(exp[3]))
|
26
|
+
else
|
27
|
+
arg = exp[3][1]
|
28
|
+
|
29
|
+
if arg
|
30
|
+
@inside_concat = true
|
31
|
+
out = exp[3][1] = process(arg)
|
32
|
+
@inside_concat = false
|
33
|
+
else
|
34
|
+
raise Exception.new("Empty _hamlout.#{method}()?")
|
35
|
+
end
|
36
|
+
|
37
|
+
if string? out
|
38
|
+
ignore
|
39
|
+
else
|
40
|
+
case method.to_s
|
41
|
+
when "push_text"
|
42
|
+
s = Sexp.new(:output, out)
|
43
|
+
@current_template[:outputs] << s
|
44
|
+
s
|
45
|
+
when HAML_FORMAT_METHOD
|
46
|
+
if $4 == "true"
|
47
|
+
Sexp.new :format_escaped, out
|
48
|
+
else
|
49
|
+
Sexp.new :format, out
|
50
|
+
end
|
51
|
+
else
|
52
|
+
raise Exception.new("Unrecognized action on _hamlout: #{method}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
res.line(exp.line)
|
59
|
+
res
|
60
|
+
|
61
|
+
#_hamlout.buffer <<
|
62
|
+
#This seems to be used rarely, but directly appends args to output buffer
|
63
|
+
elsif sexp? target and method == :<< and is_buffer_target? target
|
64
|
+
@inside_concat = true
|
65
|
+
out = exp[3][1] = process(exp[3][1])
|
66
|
+
@inside_concat = false
|
67
|
+
|
68
|
+
if out.node_type == :str #ignore plain strings
|
69
|
+
ignore
|
70
|
+
else
|
71
|
+
s = Sexp.new(:output, out)
|
72
|
+
@current_template[:outputs] << s
|
73
|
+
s.line(exp.line)
|
74
|
+
s
|
75
|
+
end
|
76
|
+
elsif target == nil and method == :render
|
77
|
+
#Process call to render()
|
78
|
+
exp[3] = process exp[3]
|
79
|
+
make_render exp
|
80
|
+
else
|
81
|
+
args = process exp[3]
|
82
|
+
call = Sexp.new :call, target, method, args
|
83
|
+
call.line(exp.line)
|
84
|
+
call
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#If inside an output stream, only return the final expression
|
89
|
+
def process_block exp
|
90
|
+
exp.shift
|
91
|
+
if @inside_concat
|
92
|
+
@inside_concat = false
|
93
|
+
exp[0..-2].each do |e|
|
94
|
+
process e
|
95
|
+
end
|
96
|
+
@inside_concat = true
|
97
|
+
process exp[-1]
|
98
|
+
else
|
99
|
+
exp.map! do |e|
|
100
|
+
res = process e
|
101
|
+
if res.empty?
|
102
|
+
nil
|
103
|
+
else
|
104
|
+
res
|
105
|
+
end
|
106
|
+
end
|
107
|
+
Sexp.new(:rlist).concat(exp).compact
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#Checks if the buffer is the target in a method call Sexp.
|
112
|
+
def is_buffer_target? exp
|
113
|
+
exp.node_type == :call and exp[1] == :_hamlout and exp[2] == :buffer
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'processors/base_processor'
|
2
|
+
|
3
|
+
#Finds method calls matching the given target(s).
|
4
|
+
#
|
5
|
+
#Targets/methods can be:
|
6
|
+
#
|
7
|
+
# - nil: matches anything, including nothing
|
8
|
+
# - Empty array: matches nothing
|
9
|
+
# - Symbol: matches single target/method exactly
|
10
|
+
# - Array of symbols: matches against any of the symbols
|
11
|
+
# - Regular expression: matches the expression
|
12
|
+
# - Array of regular expressions: matches any of the expressions
|
13
|
+
#
|
14
|
+
#If a target is also the name of a class, methods called on instances
|
15
|
+
#of that class will also be matched, in a very limited way.
|
16
|
+
#(Any methods called on Klass.new, basically. More useful when used
|
17
|
+
#in conjunction with AliasProcessor.)
|
18
|
+
#
|
19
|
+
#Examples:
|
20
|
+
#
|
21
|
+
# #To find any uses of this class:
|
22
|
+
# FindCall.new :FindCall, nil
|
23
|
+
#
|
24
|
+
# #Find system calls without a target
|
25
|
+
# FindCall.new [], [:system, :exec, :syscall]
|
26
|
+
#
|
27
|
+
# #Find all calls to length(), no matter the target
|
28
|
+
# FindCall.new nil, :length
|
29
|
+
#
|
30
|
+
# #Find all calls to sub, sub!, gsub, or gsub!
|
31
|
+
# FindCall.new nil, /^g?sub!?$/
|
32
|
+
class FindCall < BaseProcessor
|
33
|
+
|
34
|
+
def initialize targets, methods
|
35
|
+
super(nil)
|
36
|
+
@calls = []
|
37
|
+
@find_targets = targets
|
38
|
+
@find_methods = methods
|
39
|
+
@current_class = nil
|
40
|
+
@current_method = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
#Returns a list of results.
|
44
|
+
#
|
45
|
+
#A result looks like:
|
46
|
+
#
|
47
|
+
# s(:result, :ClassName, :method_name, s(:call, ...))
|
48
|
+
#
|
49
|
+
#or
|
50
|
+
#
|
51
|
+
# s(:result, :template_name, s(:call, ...))
|
52
|
+
def matches
|
53
|
+
@calls
|
54
|
+
end
|
55
|
+
|
56
|
+
#Process the given source. Provide either class and method being searched
|
57
|
+
#or the template. These names are used when reporting results.
|
58
|
+
#
|
59
|
+
#Use FindCall#matches to retrieve results.
|
60
|
+
def process_source exp, klass = nil, method = nil, template = nil
|
61
|
+
@current_class = klass
|
62
|
+
@current_method = method
|
63
|
+
@current_template = template
|
64
|
+
process exp
|
65
|
+
end
|
66
|
+
|
67
|
+
#Process body of method
|
68
|
+
def process_methdef exp
|
69
|
+
process exp[3]
|
70
|
+
end
|
71
|
+
|
72
|
+
#Process body of method
|
73
|
+
def process_selfdef exp
|
74
|
+
process exp[4]
|
75
|
+
end
|
76
|
+
|
77
|
+
#Process body of block
|
78
|
+
def process_rlist exp
|
79
|
+
exp[1..-1].each do |e|
|
80
|
+
process e
|
81
|
+
end
|
82
|
+
|
83
|
+
exp
|
84
|
+
end
|
85
|
+
|
86
|
+
#Look for matching calls and add them to results
|
87
|
+
def process_call exp
|
88
|
+
target = get_target exp[1]
|
89
|
+
method = exp[2]
|
90
|
+
|
91
|
+
process exp[3]
|
92
|
+
|
93
|
+
if match(@find_targets, target) and match(@find_methods, method)
|
94
|
+
|
95
|
+
if @current_template
|
96
|
+
@calls << Sexp.new(:result, @current_template, exp).line(exp.line)
|
97
|
+
else
|
98
|
+
@calls << Sexp.new(:result, @current_class, @current_method, exp).line(exp.line)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
exp
|
103
|
+
end
|
104
|
+
|
105
|
+
#Process an assignment like a call
|
106
|
+
def process_attrasgn exp
|
107
|
+
process_call exp
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
#Gets the target of a call as a Symbol
|
113
|
+
#if possible
|
114
|
+
def get_target exp
|
115
|
+
if sexp? exp
|
116
|
+
case exp.node_type
|
117
|
+
when :ivar, :lvar, :const
|
118
|
+
exp[1]
|
119
|
+
when :true, :false
|
120
|
+
exp[0]
|
121
|
+
when :lit
|
122
|
+
exp[1]
|
123
|
+
when :colon2
|
124
|
+
class_name exp
|
125
|
+
else
|
126
|
+
exp
|
127
|
+
end
|
128
|
+
else
|
129
|
+
exp
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
#Checks if the search terms match the given item
|
134
|
+
def match search_terms, item
|
135
|
+
case search_terms
|
136
|
+
when Symbol
|
137
|
+
if search_terms == item
|
138
|
+
true
|
139
|
+
elsif sexp? item
|
140
|
+
is_instance_of? item, search_terms
|
141
|
+
else
|
142
|
+
false
|
143
|
+
end
|
144
|
+
when Enumerable
|
145
|
+
if search_terms.empty?
|
146
|
+
item == nil
|
147
|
+
else
|
148
|
+
search_terms.each do|term|
|
149
|
+
if match(term, item)
|
150
|
+
return true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
false
|
154
|
+
end
|
155
|
+
when Regexp
|
156
|
+
search_terms.match item.to_s
|
157
|
+
when nil
|
158
|
+
true
|
159
|
+
else
|
160
|
+
raise "Cannot match #{search_terms} and #{item}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
#Checks if +item+ is an instance of +klass+ by looking for Klass.new
|
165
|
+
def is_instance_of? item, klass
|
166
|
+
if call? item
|
167
|
+
if sexp? item[1]
|
168
|
+
item[2] == :new and item[1].node_type == :const and item[1][1] == klass
|
169
|
+
else
|
170
|
+
item[2] == :new and item[1] == klass
|
171
|
+
end
|
172
|
+
else
|
173
|
+
false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'processors/lib/find_call'
|
2
|
+
|
3
|
+
#This processor specifically looks for calls like
|
4
|
+
# User.active.human.find(:all, :conditions => ...)
|
5
|
+
class FindModelCall < FindCall
|
6
|
+
|
7
|
+
#Passes +targets+ to FindCall
|
8
|
+
def initialize targets
|
9
|
+
super(targets, /^(find.*|first|last|all)$/)
|
10
|
+
end
|
11
|
+
|
12
|
+
#Matches entire method chain as a target. This differs from
|
13
|
+
#FindCall#get_target, which only matches the first expression in the chain.
|
14
|
+
def get_target exp
|
15
|
+
if sexp? exp
|
16
|
+
case exp.node_type
|
17
|
+
when :ivar, :lvar, :const
|
18
|
+
exp[1]
|
19
|
+
when :true, :false
|
20
|
+
exp[0]
|
21
|
+
when :lit
|
22
|
+
exp[1]
|
23
|
+
when :colon2
|
24
|
+
class_name exp
|
25
|
+
when :call
|
26
|
+
t = get_target(exp[1])
|
27
|
+
if t and match(@find_targets, t)
|
28
|
+
t
|
29
|
+
else
|
30
|
+
process exp
|
31
|
+
end
|
32
|
+
else
|
33
|
+
process exp
|
34
|
+
end
|
35
|
+
else
|
36
|
+
exp
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|