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.
Files changed (47) hide show
  1. data/FEATURES +16 -0
  2. data/README.md +112 -0
  3. data/WARNING_TYPES +69 -0
  4. data/bin/brakeman +266 -0
  5. data/lib/checks.rb +67 -0
  6. data/lib/checks/base_check.rb +338 -0
  7. data/lib/checks/check_cross_site_scripting.rb +216 -0
  8. data/lib/checks/check_default_routes.rb +29 -0
  9. data/lib/checks/check_evaluation.rb +29 -0
  10. data/lib/checks/check_execute.rb +110 -0
  11. data/lib/checks/check_file_access.rb +46 -0
  12. data/lib/checks/check_forgery_setting.rb +25 -0
  13. data/lib/checks/check_mass_assignment.rb +72 -0
  14. data/lib/checks/check_model_attributes.rb +36 -0
  15. data/lib/checks/check_redirect.rb +98 -0
  16. data/lib/checks/check_render.rb +65 -0
  17. data/lib/checks/check_send_file.rb +15 -0
  18. data/lib/checks/check_session_settings.rb +36 -0
  19. data/lib/checks/check_sql.rb +124 -0
  20. data/lib/checks/check_validation_regex.rb +60 -0
  21. data/lib/format/style.css +105 -0
  22. data/lib/processor.rb +83 -0
  23. data/lib/processors/alias_processor.rb +384 -0
  24. data/lib/processors/base_processor.rb +235 -0
  25. data/lib/processors/config_processor.rb +146 -0
  26. data/lib/processors/controller_alias_processor.rb +222 -0
  27. data/lib/processors/controller_processor.rb +175 -0
  28. data/lib/processors/erb_template_processor.rb +84 -0
  29. data/lib/processors/erubis_template_processor.rb +62 -0
  30. data/lib/processors/haml_template_processor.rb +115 -0
  31. data/lib/processors/lib/find_call.rb +176 -0
  32. data/lib/processors/lib/find_model_call.rb +39 -0
  33. data/lib/processors/lib/processor_helper.rb +36 -0
  34. data/lib/processors/lib/render_helper.rb +118 -0
  35. data/lib/processors/library_processor.rb +117 -0
  36. data/lib/processors/model_processor.rb +125 -0
  37. data/lib/processors/output_processor.rb +204 -0
  38. data/lib/processors/params_processor.rb +77 -0
  39. data/lib/processors/route_processor.rb +338 -0
  40. data/lib/processors/template_alias_processor.rb +86 -0
  41. data/lib/processors/template_processor.rb +55 -0
  42. data/lib/report.rb +628 -0
  43. data/lib/scanner.rb +232 -0
  44. data/lib/tracker.rb +144 -0
  45. data/lib/util.rb +141 -0
  46. data/lib/warning.rb +97 -0
  47. metadata +191 -0
@@ -0,0 +1,146 @@
1
+ require 'processors/base_processor'
2
+ require 'processors/alias_processor'
3
+
4
+ #Replace block variable in
5
+ #
6
+ # Rails::Initializer.run |config|
7
+ #
8
+ #with this value so we can keep track of it.
9
+ RAILS_CONFIG = Sexp.new(:const, :"!BRAKEMAN_RAILS_CONFIG")
10
+
11
+ #Processes configuration. Results are put in tracker.config.
12
+ #
13
+ #Configuration of Rails via Rails::Initializer are stored in tracker.config[:rails].
14
+ #For example:
15
+ #
16
+ # Rails::Initializer.run |config|
17
+ # config.action_controller.session_store = :cookie_store
18
+ # end
19
+ #
20
+ #will be stored in
21
+ #
22
+ # tracker.config[:rails][:action_controller][:session_store]
23
+ #
24
+ #Values for tracker.config[:rails] will still be Sexps.
25
+ class ConfigProcessor < BaseProcessor
26
+ def initialize *args
27
+ super
28
+ @tracker.config[:rails] ||= {}
29
+ end
30
+
31
+ #Use this method to process configuration file
32
+ def process_config src
33
+ res = ConfigAliasProcessor.new.process_safely(src)
34
+ process res
35
+ end
36
+
37
+ #Check if config is set to use Erubis
38
+ def process_call exp
39
+ target = exp[1]
40
+ target = process target if sexp? target
41
+
42
+ if exp[2] == :gem and exp[3][1][1] == "erubis"
43
+ warn "[Notice] Using Erubis for ERB templates"
44
+ @tracker.config[:erubis] = true
45
+ end
46
+
47
+ exp
48
+ end
49
+
50
+ #Look for configuration settings
51
+ def process_attrasgn exp
52
+ if exp[1] == RAILS_CONFIG
53
+ #Get rid of '=' at end
54
+ attribute = exp[2].to_s[0..-2].to_sym
55
+ if exp[3].length > 2
56
+ #Multiple arguments?...not sure if this will ever happen
57
+ @tracker.config[:rails][exp[2]] = exp[3][1..-1]
58
+ else
59
+ @tracker.config[:rails][exp[2]] = exp[3][1]
60
+ end
61
+ elsif include_rails_config? exp
62
+ options = get_rails_config exp
63
+ level = @tracker.config[:rails]
64
+ options[0..-2].each do |o|
65
+ level[o] ||= {}
66
+ level = level[o]
67
+ end
68
+
69
+ level[options.last] = exp[3][1]
70
+ end
71
+
72
+ exp
73
+ end
74
+
75
+ #Check for Rails version
76
+ def process_cdecl exp
77
+ #Set Rails version required
78
+ if exp[1] == :RAILS_GEM_VERSION
79
+ @tracker.config[:rails_version] = exp[2][1]
80
+ end
81
+
82
+ exp
83
+ end
84
+
85
+ #Check if an expression includes a call to set Rails config
86
+ def include_rails_config? exp
87
+ target = exp[1]
88
+ if call? target
89
+ if target[1] == RAILS_CONFIG
90
+ true
91
+ else
92
+ include_rails_config? target
93
+ end
94
+ elsif target == RAILS_CONFIG
95
+ true
96
+ else
97
+ false
98
+ end
99
+ end
100
+
101
+ #Returns an array of symbols for each 'level' in the config
102
+ #
103
+ # config.action_controller.session_store = :cookie
104
+ #
105
+ #becomes
106
+ #
107
+ # [:action_controller, :session_store]
108
+ def get_rails_config exp
109
+ if sexp? exp and exp.node_type == :attrasgn
110
+ attribute = exp[2].to_s[0..-2].to_sym
111
+ get_rails_config(exp[1]) << attribute
112
+ elsif call? exp
113
+ if exp[1] == RAILS_CONFIG
114
+ [exp[2]]
115
+ else
116
+ get_rails_config(exp[1]) << exp[2]
117
+ end
118
+ else
119
+ raise "WHAT"
120
+ end
121
+ end
122
+ end
123
+
124
+ #This is necessary to replace block variable so we can track config settings
125
+ class ConfigAliasProcessor < AliasProcessor
126
+
127
+ RAILS_INIT = Sexp.new(:colon2, Sexp.new(:const, :Rails), :Initializer)
128
+
129
+ #Look for a call to
130
+ #
131
+ # Rails::Initializer.run do |config|
132
+ # ...
133
+ # end
134
+ #
135
+ #and replace config with RAILS_CONFIG
136
+ def process_iter exp
137
+ target = exp[1][1]
138
+ method = exp[1][2]
139
+
140
+ if sexp? target and target == RAILS_INIT and method == :run
141
+ exp[2][2] = RAILS_CONFIG
142
+ end
143
+
144
+ process_default exp
145
+ end
146
+ end
@@ -0,0 +1,222 @@
1
+ require 'processors/alias_processor'
2
+ require 'processors/lib/render_helper'
3
+
4
+ #Processes aliasing in controllers, but includes following
5
+ #renders in routes and putting variables into templates
6
+ class ControllerAliasProcessor < AliasProcessor
7
+ include RenderHelper
8
+
9
+ def initialize tracker
10
+ super()
11
+ @tracker = tracker
12
+ @rendered = false
13
+ @current_class = @current_module = @current_method = nil
14
+ end
15
+
16
+ #Processes a class which is probably a controller.
17
+ def process_class exp
18
+ @current_class = class_name(exp[1])
19
+ if @current_module
20
+ @current_class = (@current_module + "::" + @current_class.to_s).to_sym
21
+ end
22
+
23
+ process_default exp
24
+ end
25
+
26
+ #Processes a method definition, which may include
27
+ #processing any rendered templates.
28
+ def process_methdef exp
29
+ set_env_defaults
30
+ is_route = route? exp[1]
31
+ other_method = @current_method
32
+ @current_method = exp[1]
33
+ @rendered = false if is_route
34
+
35
+ env.scope do
36
+
37
+ if is_route
38
+ before_filter_list(@current_method, @current_class).each do |f|
39
+ process_before_filter f
40
+ end
41
+ end
42
+
43
+ process exp[3]
44
+
45
+ if is_route and not @rendered
46
+ process_default_render exp
47
+ end
48
+ end
49
+
50
+ @current_method = other_method
51
+ exp
52
+ end
53
+
54
+ #Look for calls to head()
55
+ def process_call exp
56
+ exp = super
57
+
58
+ if exp[2] == :head
59
+ @rendered = true
60
+ end
61
+ exp
62
+ end
63
+
64
+ #Check for +respond_to+
65
+ def process_call_with_block exp
66
+ process_default exp
67
+
68
+ if exp[1][2] == :respond_to
69
+ @rendered = true
70
+ end
71
+
72
+ exp
73
+ end
74
+
75
+ #Processes a call to a before filter.
76
+ #Basically, adds any instance variable assignments to the environment.
77
+ #TODO: method arguments?
78
+ def process_before_filter name
79
+ method = find_method name, @current_class
80
+
81
+ if method.nil?
82
+ warn "[Notice] Could not find filter #{name}" if OPTIONS[:debug]
83
+ return
84
+ end
85
+
86
+ processor = AliasProcessor.new
87
+ processor.process_safely(method[3])
88
+
89
+ processor.only_ivars.all.each do |variable, value|
90
+ env[variable] = value
91
+ end
92
+ end
93
+
94
+ #Processes the default template for the current action
95
+ def process_default_render exp
96
+ process_template template_name, nil
97
+ end
98
+
99
+ #Process template and add the current class and method name as called_from info
100
+ def process_template name, args
101
+ super name, args, "#@current_class##@current_method"
102
+ end
103
+
104
+ #Turns a method name into a template name
105
+ def template_name name = nil
106
+ name ||= @current_method
107
+ name = name.to_s
108
+ if name.include? "/"
109
+ name
110
+ else
111
+ controller = @current_class.to_s.gsub("Controller", "")
112
+ controller.gsub!("::", "/")
113
+ underscore(controller + "/" + name.to_s)
114
+ end
115
+ end
116
+
117
+ #Returns true if the given method name is also a route
118
+ def route? method
119
+ return true if @tracker.routes[:allow_all_actions]
120
+ routes = @tracker.routes[@current_class]
121
+ routes and (routes == :allow_all_actions or routes.include? method)
122
+ end
123
+
124
+ #Get list of filters, including those that are inherited
125
+ def before_filter_list method, klass
126
+ controller = @tracker.controllers[klass]
127
+ filters = []
128
+
129
+ while controller
130
+ filters = get_before_filters(method, controller) + filters
131
+
132
+ controller = @tracker.controllers[controller[:parent]]
133
+ end
134
+
135
+ filters
136
+ end
137
+
138
+ #Returns an array of filter names
139
+ def get_before_filters method, controller
140
+ filters = []
141
+ return filters unless controller[:options]
142
+ filter_list = controller[:options][:before_filters]
143
+ return filters unless filter_list
144
+
145
+ filter_list.each do |filter|
146
+ f = before_filter_to_hash filter
147
+ if f[:all] or
148
+ (f[:only] == method) or
149
+ (f[:only].is_a? Array and f[:only].include? method) or
150
+ (f[:except] == method) or
151
+ (f[:except].is_a? Array and not f[:except].include? method)
152
+
153
+ filters.concat f[:methods]
154
+ end
155
+ end
156
+
157
+ filters
158
+ end
159
+
160
+ #Returns a before filter as a hash table
161
+ def before_filter_to_hash args
162
+ filter = {}
163
+
164
+ #Process args for the uncommon but possible situation
165
+ #in which some variables are used in the filter.
166
+ args.each do |a|
167
+ if sexp? a
168
+ a = process_default a
169
+ end
170
+ end
171
+
172
+ filter[:methods] = [args[0][1]]
173
+
174
+ args[1..-1].each do |a|
175
+ filter[:methods] << a[1] unless a.node_type == :hash
176
+ end
177
+
178
+ if args[-1].node_type == :hash
179
+ option = args[-1][1][1]
180
+ value = args[-1][2]
181
+ case value.node_type
182
+ when :array
183
+ filter[option] = value[1..-1].map {|v| v[1] }
184
+ when :lit, :str
185
+ filter[option] = value[1]
186
+ else
187
+ warn "[Notice] Unknown before_filter value: #{option} => #{value}" if OPTIONS[:debug]
188
+ end
189
+ else
190
+ filter[:all] = true
191
+ end
192
+
193
+ filter
194
+ end
195
+
196
+ #Finds a method in the given class or a parent class
197
+ def find_method method_name, klass
198
+ return nil if sexp? method_name
199
+ method_name = method_name.to_sym
200
+ controller = @tracker.controllers[klass]
201
+ controller ||= @tracker.libs[klass]
202
+
203
+ if klass and controller
204
+ method = controller[:public][method_name]
205
+ method ||= controller[:private][method_name]
206
+ method ||= controller[:protected][method_name]
207
+
208
+ if method.nil?
209
+ controller[:includes].each do |included|
210
+ method = find_method method_name, included
211
+ return method if method
212
+ end
213
+
214
+ find_method method_name, controller[:parent]
215
+ else
216
+ method
217
+ end
218
+ else
219
+ nil
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,175 @@
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
+ @controller = nil
46
+ exp
47
+ end
48
+
49
+ #Look for specific calls inside the controller
50
+ def process_call exp
51
+ target = exp[1]
52
+ if sexp? target
53
+ target = process target
54
+ end
55
+
56
+ method = exp[2]
57
+ args = exp[3]
58
+
59
+ #Methods called inside class definition
60
+ #like attr_* and other settings
61
+ if @current_method.nil? and target.nil?
62
+ if args.length == 1 #actually, empty
63
+ case method
64
+ when :private, :protected, :public
65
+ @visibility = method
66
+ when :protect_from_forgery
67
+ @controller[:options][:protect_from_forgery] = true
68
+ else
69
+ #??
70
+ end
71
+ else
72
+ case method
73
+ when :include
74
+ @controller[:includes] << class_name(args[1]) if @controller
75
+ when :before_filter
76
+ @controller[:options][:before_filters] ||= []
77
+ @controller[:options][:before_filters] << args[1..-1]
78
+ end
79
+ end
80
+ ignore
81
+ elsif target == nil and method == :render
82
+ make_render exp
83
+ elsif exp == FORMAT_HTML and context[1] != :iter
84
+ #This is an empty call to
85
+ # format.html
86
+ #Which renders the default template if no arguments
87
+ #Need to make more generic, though.
88
+ call = Sexp.new :render, :default, @current_method
89
+ call.line(exp.line)
90
+ call
91
+ else
92
+ call = Sexp.new :call, target, method, process(args)
93
+ call.line(exp.line)
94
+ call
95
+ end
96
+ end
97
+
98
+ #Process method definition and store in Tracker
99
+ def process_defn exp
100
+ name = exp[1]
101
+ @current_method = name
102
+ res = Sexp.new :methdef, name, process(exp[2]), process(exp[3][1])
103
+ res.line(exp.line)
104
+ @current_method = nil
105
+ @controller[@visibility][name] = res unless @controller.nil?
106
+
107
+ res
108
+ end
109
+
110
+ #Process self.method definition and store in Tracker
111
+ def process_defs exp
112
+ name = exp[2]
113
+
114
+ if exp[1].node_type == :self
115
+ target = @controller[:name]
116
+ else
117
+ target = class_name exp[1]
118
+ end
119
+
120
+ @current_method = name
121
+ res = Sexp.new :selfdef, target, name, process(exp[3]), process(exp[4][1])
122
+ res.line(exp.line)
123
+ @current_method = nil
124
+ @controller[@visibility][name] = res unless @controller.nil?
125
+
126
+ res
127
+ end
128
+
129
+ #Look for before_filters and add fake ones if necessary
130
+ def process_iter exp
131
+ if exp[1][2] == :before_filter
132
+ add_fake_filter exp
133
+ else
134
+ super
135
+ end
136
+ end
137
+
138
+ #This is to handle before_filter do |controller| ... end
139
+ #
140
+ #We build a new method and process that the same way as usual
141
+ #methods and filters.
142
+ def add_fake_filter exp
143
+ filter_name = ("fake_filter" + rand.to_s[/\d+$/]).to_sym
144
+ args = exp[1][3]
145
+ args.insert(1, Sexp.new(:lit, filter_name))
146
+ before_filter_call = Sexp.new(:call, nil, :before_filter, args)
147
+
148
+ if exp[2]
149
+ block_variable = exp[2][1]
150
+ else
151
+ block_variable = :temp
152
+ end
153
+
154
+ if sexp? exp[3] and exp[3].node_type == :block
155
+ block_inner = exp[3][1..-1]
156
+ else
157
+ block_inner = [exp[3]]
158
+ end
159
+
160
+ #Build Sexp for filter method
161
+ body = Sexp.new(:scope,
162
+ Sexp.new(:block,
163
+ Sexp.new(:lasgn, block_variable,
164
+ Sexp.new(:call, Sexp.new(:const, @controller[:name]), :new, Sexp.new(:arglist)))).concat(block_inner))
165
+
166
+ filter_method = Sexp.new(:defn, filter_name, Sexp.new(:args), body).line(exp.line)
167
+
168
+ vis = @visibility
169
+ @visibility = :private
170
+ process_defn filter_method
171
+ @visibility = vis
172
+ process before_filter_call
173
+ exp
174
+ end
175
+ end