brakeman-lib 3.3.1
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.
- checksums.yaml +7 -0
- data/CHANGES +872 -0
- data/FEATURES +16 -0
- data/README.md +169 -0
- data/WARNING_TYPES +95 -0
- data/bin/brakeman +89 -0
- data/lib/brakeman.rb +495 -0
- data/lib/brakeman/app_tree.rb +161 -0
- data/lib/brakeman/brakeman.rake +17 -0
- data/lib/brakeman/call_index.rb +219 -0
- data/lib/brakeman/checks.rb +191 -0
- data/lib/brakeman/checks/base_check.rb +518 -0
- data/lib/brakeman/checks/check_basic_auth.rb +88 -0
- data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/brakeman/checks/check_content_tag.rb +160 -0
- data/lib/brakeman/checks/check_create_with.rb +75 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
- data/lib/brakeman/checks/check_default_routes.rb +86 -0
- data/lib/brakeman/checks/check_deserialize.rb +57 -0
- data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
- data/lib/brakeman/checks/check_digest_dos.rb +38 -0
- data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
- data/lib/brakeman/checks/check_escape_function.rb +21 -0
- data/lib/brakeman/checks/check_evaluation.rb +36 -0
- data/lib/brakeman/checks/check_execute.rb +167 -0
- data/lib/brakeman/checks/check_file_access.rb +63 -0
- data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
- data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
- data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
- data/lib/brakeman/checks/check_header_dos.rb +31 -0
- data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
- data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
- data/lib/brakeman/checks/check_json_encoding.rb +47 -0
- data/lib/brakeman/checks/check_json_parsing.rb +107 -0
- data/lib/brakeman/checks/check_link_to.rb +132 -0
- data/lib/brakeman/checks/check_link_to_href.rb +115 -0
- data/lib/brakeman/checks/check_mail_to.rb +49 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
- data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
- data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
- data/lib/brakeman/checks/check_model_attributes.rb +119 -0
- data/lib/brakeman/checks/check_model_serialize.rb +67 -0
- data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
- data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
- data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
- data/lib/brakeman/checks/check_redirect.rb +215 -0
- data/lib/brakeman/checks/check_regex_dos.rb +69 -0
- data/lib/brakeman/checks/check_render.rb +92 -0
- data/lib/brakeman/checks/check_render_dos.rb +37 -0
- data/lib/brakeman/checks/check_render_inline.rb +54 -0
- data/lib/brakeman/checks/check_response_splitting.rb +21 -0
- data/lib/brakeman/checks/check_route_dos.rb +42 -0
- data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
- data/lib/brakeman/checks/check_secrets.rb +40 -0
- data/lib/brakeman/checks/check_select_tag.rb +60 -0
- data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
- data/lib/brakeman/checks/check_send.rb +48 -0
- data/lib/brakeman/checks/check_send_file.rb +19 -0
- data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
- data/lib/brakeman/checks/check_session_settings.rb +170 -0
- data/lib/brakeman/checks/check_simple_format.rb +59 -0
- data/lib/brakeman/checks/check_single_quotes.rb +101 -0
- data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
- data/lib/brakeman/checks/check_sql.rb +660 -0
- data/lib/brakeman/checks/check_sql_cves.rb +101 -0
- data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
- data/lib/brakeman/checks/check_strip_tags.rb +89 -0
- data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
- data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/brakeman/checks/check_translate_bug.rb +45 -0
- data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
- data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
- data/lib/brakeman/checks/check_validation_regex.rb +116 -0
- data/lib/brakeman/checks/check_weak_hash.rb +151 -0
- data/lib/brakeman/checks/check_without_protection.rb +80 -0
- data/lib/brakeman/checks/check_xml_dos.rb +51 -0
- data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
- data/lib/brakeman/differ.rb +66 -0
- data/lib/brakeman/file_parser.rb +50 -0
- data/lib/brakeman/format/style.css +133 -0
- data/lib/brakeman/options.rb +301 -0
- data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
- data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
- data/lib/brakeman/parsers/template_parser.rb +89 -0
- data/lib/brakeman/processor.rb +102 -0
- data/lib/brakeman/processors/alias_processor.rb +1013 -0
- data/lib/brakeman/processors/base_processor.rb +277 -0
- data/lib/brakeman/processors/config_processor.rb +14 -0
- data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
- data/lib/brakeman/processors/controller_processor.rb +326 -0
- data/lib/brakeman/processors/erb_template_processor.rb +80 -0
- data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
- data/lib/brakeman/processors/gem_processor.rb +57 -0
- data/lib/brakeman/processors/haml_template_processor.rb +190 -0
- data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
- data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
- data/lib/brakeman/processors/lib/find_call.rb +183 -0
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/brakeman/processors/lib/render_helper.rb +181 -0
- data/lib/brakeman/processors/lib/render_path.rb +107 -0
- data/lib/brakeman/processors/lib/route_helper.rb +68 -0
- data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
- data/lib/brakeman/processors/library_processor.rb +119 -0
- data/lib/brakeman/processors/model_processor.rb +191 -0
- data/lib/brakeman/processors/output_processor.rb +171 -0
- data/lib/brakeman/processors/route_processor.rb +17 -0
- data/lib/brakeman/processors/slim_template_processor.rb +107 -0
- data/lib/brakeman/processors/template_alias_processor.rb +116 -0
- data/lib/brakeman/processors/template_processor.rb +74 -0
- data/lib/brakeman/report.rb +78 -0
- data/lib/brakeman/report/config/remediation.yml +71 -0
- data/lib/brakeman/report/ignore/config.rb +135 -0
- data/lib/brakeman/report/ignore/interactive.rb +311 -0
- data/lib/brakeman/report/renderer.rb +24 -0
- data/lib/brakeman/report/report_base.rb +286 -0
- data/lib/brakeman/report/report_codeclimate.rb +70 -0
- data/lib/brakeman/report/report_csv.rb +55 -0
- data/lib/brakeman/report/report_hash.rb +23 -0
- data/lib/brakeman/report/report_html.rb +216 -0
- data/lib/brakeman/report/report_json.rb +42 -0
- data/lib/brakeman/report/report_markdown.rb +156 -0
- data/lib/brakeman/report/report_table.rb +107 -0
- data/lib/brakeman/report/report_tabs.rb +17 -0
- data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
- data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
- data/lib/brakeman/report/templates/header.html.erb +58 -0
- data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/overview.html.erb +38 -0
- data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
- data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
- data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
- data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
- data/lib/brakeman/rescanner.rb +483 -0
- data/lib/brakeman/scanner.rb +317 -0
- data/lib/brakeman/tracker.rb +347 -0
- data/lib/brakeman/tracker/collection.rb +93 -0
- data/lib/brakeman/tracker/config.rb +101 -0
- data/lib/brakeman/tracker/constants.rb +101 -0
- data/lib/brakeman/tracker/controller.rb +161 -0
- data/lib/brakeman/tracker/library.rb +17 -0
- data/lib/brakeman/tracker/model.rb +90 -0
- data/lib/brakeman/tracker/template.rb +33 -0
- data/lib/brakeman/util.rb +481 -0
- data/lib/brakeman/version.rb +3 -0
- data/lib/brakeman/warning.rb +255 -0
- data/lib/brakeman/warning_codes.rb +111 -0
- data/lib/ruby_parser/bm_sexp.rb +610 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- metadata +362 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
require 'brakeman/processors/lib/basic_processor'
|
|
2
|
+
|
|
3
|
+
#Processes the Sexp from routes.rb. Stores results in tracker.routes.
|
|
4
|
+
#
|
|
5
|
+
#Note that it is only interested in determining what methods on which
|
|
6
|
+
#controllers are used as routes, not the generated URLs for routes.
|
|
7
|
+
class Brakeman::Rails2RoutesProcessor < Brakeman::BasicProcessor
|
|
8
|
+
include Brakeman::RouteHelper
|
|
9
|
+
|
|
10
|
+
attr_reader :map, :nested, :current_controller
|
|
11
|
+
|
|
12
|
+
def initialize tracker
|
|
13
|
+
super
|
|
14
|
+
@map = Sexp.new(:lvar, :map)
|
|
15
|
+
@nested = nil #used for identifying nested targets
|
|
16
|
+
@prefix = [] #Controller name prefix (a module name, usually)
|
|
17
|
+
@current_controller = nil
|
|
18
|
+
@with_options = nil #For use inside map.with_options
|
|
19
|
+
@file_name = "config/routes.rb"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#Call this with parsed route file information.
|
|
23
|
+
#
|
|
24
|
+
#This method first calls RouteAliasProcessor#process_safely on the +exp+,
|
|
25
|
+
#so it does not modify the +exp+.
|
|
26
|
+
def process_routes exp
|
|
27
|
+
process Brakeman::RouteAliasProcessor.new.process_safely(exp, nil, @file_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#Looking for mapping of routes
|
|
31
|
+
def process_call exp
|
|
32
|
+
target = exp.target
|
|
33
|
+
|
|
34
|
+
if target == map or (not target.nil? and target == nested)
|
|
35
|
+
process_map exp
|
|
36
|
+
else
|
|
37
|
+
process_default exp
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
exp
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
#Process a map.something call
|
|
44
|
+
#based on the method used
|
|
45
|
+
def process_map exp
|
|
46
|
+
args = exp.args
|
|
47
|
+
|
|
48
|
+
case exp.method
|
|
49
|
+
when :resource
|
|
50
|
+
process_resource args
|
|
51
|
+
when :resources
|
|
52
|
+
process_resources args
|
|
53
|
+
when :connect, :root
|
|
54
|
+
process_connect args
|
|
55
|
+
else
|
|
56
|
+
process_named_route args
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
exp
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#Look for map calls that take a block.
|
|
63
|
+
#Otherwise, just do the default processing.
|
|
64
|
+
def process_iter exp
|
|
65
|
+
target = exp.block_call.target
|
|
66
|
+
|
|
67
|
+
if target == map or target == nested
|
|
68
|
+
method = exp.block_call.method
|
|
69
|
+
case method
|
|
70
|
+
when :namespace
|
|
71
|
+
process_namespace exp
|
|
72
|
+
when :resources, :resource
|
|
73
|
+
process_resources exp.block_call.args
|
|
74
|
+
process_default exp.block if exp.block
|
|
75
|
+
when :with_options
|
|
76
|
+
process_with_options exp
|
|
77
|
+
end
|
|
78
|
+
exp
|
|
79
|
+
else
|
|
80
|
+
process_default exp
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#Process
|
|
85
|
+
# map.resources :x, :controller => :y, :member => ...
|
|
86
|
+
#etc.
|
|
87
|
+
def process_resources exp
|
|
88
|
+
controller = check_for_controller_name exp
|
|
89
|
+
if controller
|
|
90
|
+
self.current_controller = controller
|
|
91
|
+
process_resource_options exp[-1]
|
|
92
|
+
else
|
|
93
|
+
exp.each do |argument|
|
|
94
|
+
if node_type? argument, :lit
|
|
95
|
+
self.current_controller = exp.first.value
|
|
96
|
+
add_resources_routes
|
|
97
|
+
process_resource_options exp.last
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#Process all the options that might be in the hash passed to
|
|
104
|
+
#map.resource, et al.
|
|
105
|
+
def process_resource_options exp
|
|
106
|
+
if exp.nil? and @with_options
|
|
107
|
+
exp = @with_options
|
|
108
|
+
elsif @with_options
|
|
109
|
+
exp = exp.concat @with_options[1..-1]
|
|
110
|
+
end
|
|
111
|
+
return unless exp.node_type == :hash
|
|
112
|
+
|
|
113
|
+
hash_iterate(exp) do |option, value|
|
|
114
|
+
case option[1]
|
|
115
|
+
when :controller, :requirements, :singular, :path_prefix, :as,
|
|
116
|
+
:path_names, :shallow, :name_prefix, :member_path, :nested_member_path,
|
|
117
|
+
:belongs_to, :conditions, :active_scaffold
|
|
118
|
+
#should be able to skip
|
|
119
|
+
when :collection, :member, :new
|
|
120
|
+
process_collection value
|
|
121
|
+
when :has_one
|
|
122
|
+
save_controller = current_controller
|
|
123
|
+
process_resource value[1..-1] #Verify this is proper behavior
|
|
124
|
+
self.current_controller = save_controller
|
|
125
|
+
when :has_many
|
|
126
|
+
save_controller = current_controller
|
|
127
|
+
process_resources value[1..-1]
|
|
128
|
+
self.current_controller = save_controller
|
|
129
|
+
when :only
|
|
130
|
+
process_option_only value
|
|
131
|
+
when :except
|
|
132
|
+
process_option_except value
|
|
133
|
+
else
|
|
134
|
+
Brakeman.notify "[Notice] Unhandled resource option, please report: #{option}"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
#Process route option :only => ...
|
|
140
|
+
def process_option_only exp
|
|
141
|
+
routes = @tracker.routes[@current_controller]
|
|
142
|
+
[:index, :new, :create, :show, :edit, :update, :destroy].each do |r|
|
|
143
|
+
routes.delete r
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
if exp.node_type == :array
|
|
147
|
+
exp[1..-1].each do |e|
|
|
148
|
+
routes << e.value
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
#Process route option :except => ...
|
|
154
|
+
def process_option_except exp
|
|
155
|
+
return unless exp.node_type == :array
|
|
156
|
+
routes = @tracker.routes[@current_controller]
|
|
157
|
+
|
|
158
|
+
exp[1..-1].each do |e|
|
|
159
|
+
routes.delete e.value
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# map.resource :x, ..
|
|
164
|
+
def process_resource exp
|
|
165
|
+
controller = check_for_controller_name exp
|
|
166
|
+
if controller
|
|
167
|
+
self.current_controller = controller
|
|
168
|
+
process_resource_options exp.last
|
|
169
|
+
else
|
|
170
|
+
exp.each do |argument|
|
|
171
|
+
if node_type? argument, :lit
|
|
172
|
+
self.current_controller = pluralize(exp.first.value.to_s)
|
|
173
|
+
add_resource_routes
|
|
174
|
+
process_resource_options exp.last
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
#Process
|
|
181
|
+
# map.connect '/something', :controller => 'blah', :action => 'whatever'
|
|
182
|
+
def process_connect exp
|
|
183
|
+
return if exp.empty?
|
|
184
|
+
|
|
185
|
+
controller = check_for_controller_name exp
|
|
186
|
+
self.current_controller = controller if controller
|
|
187
|
+
|
|
188
|
+
#Check for default route
|
|
189
|
+
if string? exp.first
|
|
190
|
+
if exp.first.value == ":controller/:action/:id"
|
|
191
|
+
@tracker.routes[:allow_all_actions] = exp.first
|
|
192
|
+
elsif exp.first.value.include? ":action"
|
|
193
|
+
@tracker.routes[@current_controller] = [:allow_all_actions, exp.line]
|
|
194
|
+
return
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
#This -seems- redundant, but people might connect actions
|
|
199
|
+
#to a controller which already allows them all
|
|
200
|
+
return if @tracker.routes[@current_controller].is_a? Array and @tracker.routes[@current_controller][0] == :allow_all_actions
|
|
201
|
+
|
|
202
|
+
exp.last.each_with_index do |e,i|
|
|
203
|
+
if symbol? e and e.value == :action
|
|
204
|
+
action = exp.last[i + 1]
|
|
205
|
+
|
|
206
|
+
if node_type? action, :lit
|
|
207
|
+
@tracker.routes[@current_controller] << action.value.to_sym
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
return
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# map.with_options :controller => 'something' do |something|
|
|
216
|
+
# something.resources :blah
|
|
217
|
+
# end
|
|
218
|
+
def process_with_options exp
|
|
219
|
+
@with_options = exp.block_call.last_arg
|
|
220
|
+
@nested = Sexp.new(:lvar, exp.block_args.value)
|
|
221
|
+
|
|
222
|
+
self.current_controller = check_for_controller_name exp.block_call.args
|
|
223
|
+
|
|
224
|
+
#process block
|
|
225
|
+
process exp.block
|
|
226
|
+
|
|
227
|
+
@with_options = nil
|
|
228
|
+
@nested = nil
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# map.namespace :something do |something|
|
|
232
|
+
# something.resources :blah
|
|
233
|
+
# end
|
|
234
|
+
def process_namespace exp
|
|
235
|
+
call = exp.block_call
|
|
236
|
+
formal_args = exp.block_args
|
|
237
|
+
block = exp.block
|
|
238
|
+
|
|
239
|
+
@prefix << camelize(call.first_arg.value)
|
|
240
|
+
|
|
241
|
+
if formal_args
|
|
242
|
+
@nested = Sexp.new(:lvar, formal_args.value)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
process block
|
|
246
|
+
|
|
247
|
+
@prefix.pop
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# map.something_abnormal '/blah', :controller => 'something', :action => 'wohoo'
|
|
251
|
+
def process_named_route exp
|
|
252
|
+
process_connect exp
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
#Process collection option
|
|
256
|
+
# :collection => { :some_action => :http_actions }
|
|
257
|
+
def process_collection exp
|
|
258
|
+
return unless exp.node_type == :hash
|
|
259
|
+
routes = @tracker.routes[@current_controller]
|
|
260
|
+
|
|
261
|
+
hash_iterate(exp) do |action, type|
|
|
262
|
+
routes << action.value
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
private
|
|
267
|
+
|
|
268
|
+
#Checks an argument list for a hash that has a key :controller.
|
|
269
|
+
#If it does, returns the value.
|
|
270
|
+
#
|
|
271
|
+
#Otherwise, returns nil.
|
|
272
|
+
def check_for_controller_name args
|
|
273
|
+
args.each do |a|
|
|
274
|
+
if hash? a and value = hash_access(a, :controller)
|
|
275
|
+
return value.value if string? value or symbol? value
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
nil
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
#This is for a really specific case where a hash is used as arguments
|
|
284
|
+
#to one of the map methods.
|
|
285
|
+
class Brakeman::RouteAliasProcessor < Brakeman::AliasProcessor
|
|
286
|
+
|
|
287
|
+
#This replaces
|
|
288
|
+
# { :some => :hash }.keys
|
|
289
|
+
#with
|
|
290
|
+
# [:some]
|
|
291
|
+
def process_call exp
|
|
292
|
+
process_default exp
|
|
293
|
+
|
|
294
|
+
if hash? exp.target and exp.method == :keys
|
|
295
|
+
keys = get_keys exp.target
|
|
296
|
+
exp.clear
|
|
297
|
+
keys.each_with_index do |e,i|
|
|
298
|
+
exp[i] = e
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
exp
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
#Returns an array Sexp containing the keys from the hash
|
|
305
|
+
def get_keys hash
|
|
306
|
+
keys = Sexp.new(:array)
|
|
307
|
+
hash_iterate(hash) do |key, value|
|
|
308
|
+
keys << key
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
keys
|
|
312
|
+
end
|
|
313
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
|
|
2
|
+
require 'brakeman/processors/lib/basic_processor'
|
|
3
|
+
|
|
4
|
+
#Processes configuration. Results are put in tracker.config.
|
|
5
|
+
#
|
|
6
|
+
#Configuration of Rails via Rails::Initializer are stored in tracker.config.rails.
|
|
7
|
+
#For example:
|
|
8
|
+
#
|
|
9
|
+
# MyApp::Application.configure do
|
|
10
|
+
# config.active_record.whitelist_attributes = true
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
#will be stored in
|
|
14
|
+
#
|
|
15
|
+
# tracker.config.rails[:active_record][:whitelist_attributes]
|
|
16
|
+
#
|
|
17
|
+
#Values for tracker.config.rails will still be Sexps.
|
|
18
|
+
class Brakeman::Rails3ConfigProcessor < Brakeman::BasicProcessor
|
|
19
|
+
RAILS_CONFIG = Sexp.new(:call, nil, :config)
|
|
20
|
+
|
|
21
|
+
def initialize *args
|
|
22
|
+
super
|
|
23
|
+
@inside_config = false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#Use this method to process configuration file
|
|
27
|
+
def process_config src, file_name
|
|
28
|
+
@file_name = file_name
|
|
29
|
+
res = Brakeman::AliasProcessor.new(@tracker).process_safely(src, nil, @file_name)
|
|
30
|
+
process res
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#Look for MyApp::Application.configure do ... end
|
|
34
|
+
def process_iter exp
|
|
35
|
+
call = exp.block_call
|
|
36
|
+
|
|
37
|
+
if node_type?(call.target, :colon2) and
|
|
38
|
+
call.target.rhs == :Application and
|
|
39
|
+
call.method == :configure
|
|
40
|
+
|
|
41
|
+
@inside_config = true
|
|
42
|
+
process exp.block if sexp? exp.block
|
|
43
|
+
@inside_config = false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
exp
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#Look for class Application < Rails::Application
|
|
50
|
+
def process_class exp
|
|
51
|
+
if exp.class_name == :Application
|
|
52
|
+
@inside_config = true
|
|
53
|
+
process_all exp.body if sexp? exp.body
|
|
54
|
+
@inside_config = false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
exp
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
#Look for configuration settings
|
|
61
|
+
def process_attrasgn exp
|
|
62
|
+
return exp unless @inside_config
|
|
63
|
+
|
|
64
|
+
if exp.target == RAILS_CONFIG
|
|
65
|
+
#Get rid of '=' at end
|
|
66
|
+
attribute = exp.method.to_s[0..-2].to_sym
|
|
67
|
+
if exp.args.length > 1
|
|
68
|
+
#Multiple arguments?...not sure if this will ever happen
|
|
69
|
+
@tracker.config.rails[attribute] = exp.args
|
|
70
|
+
else
|
|
71
|
+
@tracker.config.rails[attribute] = exp.first_arg
|
|
72
|
+
end
|
|
73
|
+
elsif include_rails_config? exp
|
|
74
|
+
options = get_rails_config exp
|
|
75
|
+
level = @tracker.config.rails
|
|
76
|
+
options[0..-2].each do |o|
|
|
77
|
+
level[o] ||= {}
|
|
78
|
+
|
|
79
|
+
option = level[o]
|
|
80
|
+
|
|
81
|
+
if not option.is_a? Hash
|
|
82
|
+
Brakeman.debug "[Notice] Skipping config setting: #{options.map(&:to_s).join(".")}"
|
|
83
|
+
return exp
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
level = level[o]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
level[options.last] = exp.first_arg
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
exp
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
#Check if an expression includes a call to set Rails config
|
|
96
|
+
def include_rails_config? exp
|
|
97
|
+
target = exp.target
|
|
98
|
+
if call? target
|
|
99
|
+
if target.target == RAILS_CONFIG
|
|
100
|
+
true
|
|
101
|
+
else
|
|
102
|
+
include_rails_config? target
|
|
103
|
+
end
|
|
104
|
+
elsif target == RAILS_CONFIG
|
|
105
|
+
true
|
|
106
|
+
else
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
#Returns an array of symbols for each 'level' in the config
|
|
112
|
+
#
|
|
113
|
+
# config.action_controller.session_store = :cookie
|
|
114
|
+
#
|
|
115
|
+
#becomes
|
|
116
|
+
#
|
|
117
|
+
# [:action_controller, :session_store]
|
|
118
|
+
def get_rails_config exp
|
|
119
|
+
if node_type? exp, :attrasgn
|
|
120
|
+
attribute = exp.method.to_s[0..-2].to_sym
|
|
121
|
+
get_rails_config(exp.target) << attribute
|
|
122
|
+
elsif call? exp
|
|
123
|
+
if exp.target == RAILS_CONFIG
|
|
124
|
+
[exp.method]
|
|
125
|
+
else
|
|
126
|
+
get_rails_config(exp.target) << exp.method
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
raise "WHAT"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
require 'brakeman/processors/lib/basic_processor'
|
|
2
|
+
|
|
3
|
+
#Processes the Sexp from routes.rb. Stores results in tracker.routes.
|
|
4
|
+
#
|
|
5
|
+
#Note that it is only interested in determining what methods on which
|
|
6
|
+
#controllers are used as routes, not the generated URLs for routes.
|
|
7
|
+
class Brakeman::Rails3RoutesProcessor < Brakeman::BasicProcessor
|
|
8
|
+
include Brakeman::RouteHelper
|
|
9
|
+
|
|
10
|
+
attr_reader :map, :nested, :current_controller
|
|
11
|
+
|
|
12
|
+
def initialize tracker
|
|
13
|
+
super
|
|
14
|
+
@map = Sexp.new(:lvar, :map)
|
|
15
|
+
@nested = nil #used for identifying nested targets
|
|
16
|
+
@prefix = [] #Controller name prefix (a module name, usually)
|
|
17
|
+
@current_controller = nil
|
|
18
|
+
@with_options = nil #For use inside map.with_options
|
|
19
|
+
@controller_block = false
|
|
20
|
+
@file_name = "config/routes.rb"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def process_routes exp
|
|
24
|
+
process Brakeman::AliasProcessor.new.process_safely(exp, nil, @file_name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def process_call exp
|
|
28
|
+
case exp.method
|
|
29
|
+
when :resources
|
|
30
|
+
process_resources exp
|
|
31
|
+
when :resource
|
|
32
|
+
process_resource exp
|
|
33
|
+
when :root
|
|
34
|
+
process_root exp
|
|
35
|
+
when :member
|
|
36
|
+
process_default exp
|
|
37
|
+
when :get, :put, :post, :delete
|
|
38
|
+
process_verb exp
|
|
39
|
+
when :match
|
|
40
|
+
process_match exp
|
|
41
|
+
else
|
|
42
|
+
exp
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def process_iter exp
|
|
47
|
+
case exp.block_call.method
|
|
48
|
+
when :namespace
|
|
49
|
+
process_namespace exp
|
|
50
|
+
when :resource
|
|
51
|
+
process_resource_block exp
|
|
52
|
+
when :resources
|
|
53
|
+
process_resources_block exp
|
|
54
|
+
when :scope
|
|
55
|
+
process_scope_block exp
|
|
56
|
+
when :controller
|
|
57
|
+
process_controller_block exp
|
|
58
|
+
else
|
|
59
|
+
process_default exp
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def process_namespace exp
|
|
64
|
+
arg = exp.block_call.first_arg
|
|
65
|
+
return exp unless symbol? arg or string? arg
|
|
66
|
+
|
|
67
|
+
name = arg.value
|
|
68
|
+
block = exp.block
|
|
69
|
+
|
|
70
|
+
@prefix << camelize(name)
|
|
71
|
+
|
|
72
|
+
process block
|
|
73
|
+
|
|
74
|
+
@prefix.pop
|
|
75
|
+
|
|
76
|
+
exp
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
#TODO: Need test for this
|
|
80
|
+
def process_root exp
|
|
81
|
+
if value = hash_access(exp.first_arg, :to)
|
|
82
|
+
if string? value
|
|
83
|
+
add_route_from_string value
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
exp
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def process_match exp
|
|
91
|
+
first_arg = exp.first_arg
|
|
92
|
+
second_arg = exp.second_arg
|
|
93
|
+
last_arg = exp.last_arg
|
|
94
|
+
|
|
95
|
+
if string? first_arg
|
|
96
|
+
|
|
97
|
+
matcher = first_arg.value
|
|
98
|
+
if matcher == ':controller(/:action(/:id(.:format)))' or
|
|
99
|
+
matcher.include? ':controller' and action_route?(matcher) #Default routes
|
|
100
|
+
@tracker.routes[:allow_all_actions] = first_arg
|
|
101
|
+
return exp
|
|
102
|
+
elsif action_route?(first_arg)
|
|
103
|
+
if hash? second_arg and controller_name = hash_access(second_arg, :controller)
|
|
104
|
+
loose_action(controller_name, "matched") #TODO: Parse verbs
|
|
105
|
+
end
|
|
106
|
+
elsif second_arg.nil? and in_controller_block? and not matcher.include? ":"
|
|
107
|
+
add_route matcher
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
if hash? last_arg
|
|
112
|
+
hash_iterate last_arg do |k, v|
|
|
113
|
+
if string? k
|
|
114
|
+
if string? v
|
|
115
|
+
add_route_from_string v
|
|
116
|
+
elsif in_controller_block? and symbol? v
|
|
117
|
+
add_route v
|
|
118
|
+
end
|
|
119
|
+
elsif symbol? k
|
|
120
|
+
case k.value
|
|
121
|
+
when :action
|
|
122
|
+
if string? v
|
|
123
|
+
add_route_from_string v
|
|
124
|
+
else
|
|
125
|
+
add_route v
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
when :to
|
|
129
|
+
if string? v
|
|
130
|
+
add_route_from_string v[1]
|
|
131
|
+
elsif in_controller_block? and symbol? v
|
|
132
|
+
add_route v
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
@current_controller = nil unless in_controller_block?
|
|
140
|
+
exp
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def add_route_from_string value
|
|
144
|
+
value = value[1] if string? value
|
|
145
|
+
|
|
146
|
+
controller, action = extract_action value
|
|
147
|
+
|
|
148
|
+
if action
|
|
149
|
+
add_route action, controller
|
|
150
|
+
elsif in_controller_block?
|
|
151
|
+
add_route value
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def process_verb exp
|
|
156
|
+
first_arg = exp.first_arg
|
|
157
|
+
second_arg = exp.second_arg
|
|
158
|
+
|
|
159
|
+
if symbol? first_arg and not hash? second_arg
|
|
160
|
+
add_route first_arg
|
|
161
|
+
elsif hash? second_arg
|
|
162
|
+
hash_iterate second_arg do |k, v|
|
|
163
|
+
if symbol? k and k.value == :to
|
|
164
|
+
if string? v
|
|
165
|
+
add_route_from_string v
|
|
166
|
+
elsif in_controller_block? and symbol? v
|
|
167
|
+
add_route v
|
|
168
|
+
end
|
|
169
|
+
elsif action_route?(first_arg)
|
|
170
|
+
if hash? second_arg and controller_name = hash_access(second_arg, :controller)
|
|
171
|
+
loose_action(controller_name, exp.method)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
elsif string? first_arg
|
|
176
|
+
if first_arg.value.include? ':controller' and action_route?(first_arg) #Default routes
|
|
177
|
+
@tracker.routes[:allow_all_actions] = first_arg
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
route = first_arg.value.split "/"
|
|
181
|
+
if route.length != 2
|
|
182
|
+
add_route route[0]
|
|
183
|
+
else
|
|
184
|
+
add_route route[1], route[0]
|
|
185
|
+
end
|
|
186
|
+
elsif in_controller_block? and symbol? first_arg
|
|
187
|
+
add_route first_arg
|
|
188
|
+
else hash? first_arg
|
|
189
|
+
hash_iterate first_arg do |k, v|
|
|
190
|
+
if string? k
|
|
191
|
+
if string? v
|
|
192
|
+
add_route_from_string v
|
|
193
|
+
elsif in_controller_block?
|
|
194
|
+
add_route v
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
@current_controller = nil unless in_controller_block?
|
|
201
|
+
exp
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def process_resources exp
|
|
205
|
+
first_arg = exp.first_arg
|
|
206
|
+
second_arg = exp.second_arg
|
|
207
|
+
|
|
208
|
+
return exp unless symbol? first_arg or string? first_arg
|
|
209
|
+
|
|
210
|
+
if second_arg and second_arg.node_type == :hash
|
|
211
|
+
self.current_controller = first_arg.value
|
|
212
|
+
#handle hash
|
|
213
|
+
add_resources_routes
|
|
214
|
+
elsif exp.args.all? { |s| symbol? s }
|
|
215
|
+
exp.each_arg do |s|
|
|
216
|
+
self.current_controller = s.value
|
|
217
|
+
add_resources_routes
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
@current_controller = nil unless in_controller_block?
|
|
222
|
+
exp
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def process_resource exp
|
|
226
|
+
#Does resource even take more than one controller name?
|
|
227
|
+
exp.each_arg do |s|
|
|
228
|
+
if symbol? s
|
|
229
|
+
self.current_controller = pluralize(s.value.to_s)
|
|
230
|
+
add_resource_routes
|
|
231
|
+
else
|
|
232
|
+
#handle something else, like options
|
|
233
|
+
#or something?
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
@current_controller = nil unless in_controller_block?
|
|
238
|
+
exp
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def process_resources_block exp
|
|
242
|
+
in_controller_block do
|
|
243
|
+
process_resources exp.block_call
|
|
244
|
+
process exp.block
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
@current_controller = nil
|
|
248
|
+
exp
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def process_resource_block exp
|
|
252
|
+
in_controller_block do
|
|
253
|
+
process_resource exp.block_call
|
|
254
|
+
process exp.block
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
@current_controller = nil
|
|
258
|
+
exp
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def process_scope_block exp
|
|
262
|
+
#How to deal with options?
|
|
263
|
+
process exp.block
|
|
264
|
+
exp
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def process_controller_block exp
|
|
268
|
+
if string? exp or symbol? exp
|
|
269
|
+
self.current_controller = exp.block_call.first_arg.value
|
|
270
|
+
|
|
271
|
+
in_controller_block do
|
|
272
|
+
process exp[-1] if exp[-1]
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
@current_controller = nil
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
exp
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def extract_action str
|
|
282
|
+
str.split "#"
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def in_controller_block?
|
|
286
|
+
@controller_block
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def in_controller_block
|
|
290
|
+
prev_block = @controller_block
|
|
291
|
+
@controller_block = true
|
|
292
|
+
yield
|
|
293
|
+
@controller_block = prev_block
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def action_route? arg
|
|
297
|
+
if string? arg
|
|
298
|
+
arg = arg.value
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
arg.is_a? String and (arg.include? ":action" or arg.include? "*action")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def loose_action controller_name, verb = "any"
|
|
305
|
+
self.current_controller = controller_name.value
|
|
306
|
+
@tracker.routes[@current_controller] = [:allow_all_actions, {:allow_verb => verb}]
|
|
307
|
+
end
|
|
308
|
+
end
|