brakeman 0.0.2
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/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
|