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.
Files changed (159) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +872 -0
  3. data/FEATURES +16 -0
  4. data/README.md +169 -0
  5. data/WARNING_TYPES +95 -0
  6. data/bin/brakeman +89 -0
  7. data/lib/brakeman.rb +495 -0
  8. data/lib/brakeman/app_tree.rb +161 -0
  9. data/lib/brakeman/brakeman.rake +17 -0
  10. data/lib/brakeman/call_index.rb +219 -0
  11. data/lib/brakeman/checks.rb +191 -0
  12. data/lib/brakeman/checks/base_check.rb +518 -0
  13. data/lib/brakeman/checks/check_basic_auth.rb +88 -0
  14. data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
  15. data/lib/brakeman/checks/check_content_tag.rb +160 -0
  16. data/lib/brakeman/checks/check_create_with.rb +75 -0
  17. data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
  18. data/lib/brakeman/checks/check_default_routes.rb +86 -0
  19. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  20. data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
  21. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  22. data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
  23. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  24. data/lib/brakeman/checks/check_evaluation.rb +36 -0
  25. data/lib/brakeman/checks/check_execute.rb +167 -0
  26. data/lib/brakeman/checks/check_file_access.rb +63 -0
  27. data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
  28. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  29. data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
  30. data/lib/brakeman/checks/check_header_dos.rb +31 -0
  31. data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
  32. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  33. data/lib/brakeman/checks/check_json_encoding.rb +47 -0
  34. data/lib/brakeman/checks/check_json_parsing.rb +107 -0
  35. data/lib/brakeman/checks/check_link_to.rb +132 -0
  36. data/lib/brakeman/checks/check_link_to_href.rb +115 -0
  37. data/lib/brakeman/checks/check_mail_to.rb +49 -0
  38. data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
  39. data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
  40. data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
  41. data/lib/brakeman/checks/check_model_attributes.rb +119 -0
  42. data/lib/brakeman/checks/check_model_serialize.rb +67 -0
  43. data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
  44. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
  45. data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
  46. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  47. data/lib/brakeman/checks/check_redirect.rb +215 -0
  48. data/lib/brakeman/checks/check_regex_dos.rb +69 -0
  49. data/lib/brakeman/checks/check_render.rb +92 -0
  50. data/lib/brakeman/checks/check_render_dos.rb +37 -0
  51. data/lib/brakeman/checks/check_render_inline.rb +54 -0
  52. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  53. data/lib/brakeman/checks/check_route_dos.rb +42 -0
  54. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  55. data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
  56. data/lib/brakeman/checks/check_secrets.rb +40 -0
  57. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  58. data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
  59. data/lib/brakeman/checks/check_send.rb +48 -0
  60. data/lib/brakeman/checks/check_send_file.rb +19 -0
  61. data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
  62. data/lib/brakeman/checks/check_session_settings.rb +170 -0
  63. data/lib/brakeman/checks/check_simple_format.rb +59 -0
  64. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  65. data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
  66. data/lib/brakeman/checks/check_sql.rb +660 -0
  67. data/lib/brakeman/checks/check_sql_cves.rb +101 -0
  68. data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
  69. data/lib/brakeman/checks/check_strip_tags.rb +89 -0
  70. data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
  71. data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
  72. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  73. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  74. data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
  75. data/lib/brakeman/checks/check_validation_regex.rb +116 -0
  76. data/lib/brakeman/checks/check_weak_hash.rb +151 -0
  77. data/lib/brakeman/checks/check_without_protection.rb +80 -0
  78. data/lib/brakeman/checks/check_xml_dos.rb +51 -0
  79. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  80. data/lib/brakeman/differ.rb +66 -0
  81. data/lib/brakeman/file_parser.rb +50 -0
  82. data/lib/brakeman/format/style.css +133 -0
  83. data/lib/brakeman/options.rb +301 -0
  84. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  85. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  86. data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
  87. data/lib/brakeman/parsers/template_parser.rb +89 -0
  88. data/lib/brakeman/processor.rb +102 -0
  89. data/lib/brakeman/processors/alias_processor.rb +1013 -0
  90. data/lib/brakeman/processors/base_processor.rb +277 -0
  91. data/lib/brakeman/processors/config_processor.rb +14 -0
  92. data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
  93. data/lib/brakeman/processors/controller_processor.rb +326 -0
  94. data/lib/brakeman/processors/erb_template_processor.rb +80 -0
  95. data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
  96. data/lib/brakeman/processors/gem_processor.rb +57 -0
  97. data/lib/brakeman/processors/haml_template_processor.rb +190 -0
  98. data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
  99. data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
  100. data/lib/brakeman/processors/lib/find_call.rb +183 -0
  101. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  102. data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
  103. data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
  104. data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
  105. data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
  106. data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
  107. data/lib/brakeman/processors/lib/render_helper.rb +181 -0
  108. data/lib/brakeman/processors/lib/render_path.rb +107 -0
  109. data/lib/brakeman/processors/lib/route_helper.rb +68 -0
  110. data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
  111. data/lib/brakeman/processors/library_processor.rb +119 -0
  112. data/lib/brakeman/processors/model_processor.rb +191 -0
  113. data/lib/brakeman/processors/output_processor.rb +171 -0
  114. data/lib/brakeman/processors/route_processor.rb +17 -0
  115. data/lib/brakeman/processors/slim_template_processor.rb +107 -0
  116. data/lib/brakeman/processors/template_alias_processor.rb +116 -0
  117. data/lib/brakeman/processors/template_processor.rb +74 -0
  118. data/lib/brakeman/report.rb +78 -0
  119. data/lib/brakeman/report/config/remediation.yml +71 -0
  120. data/lib/brakeman/report/ignore/config.rb +135 -0
  121. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  122. data/lib/brakeman/report/renderer.rb +24 -0
  123. data/lib/brakeman/report/report_base.rb +286 -0
  124. data/lib/brakeman/report/report_codeclimate.rb +70 -0
  125. data/lib/brakeman/report/report_csv.rb +55 -0
  126. data/lib/brakeman/report/report_hash.rb +23 -0
  127. data/lib/brakeman/report/report_html.rb +216 -0
  128. data/lib/brakeman/report/report_json.rb +42 -0
  129. data/lib/brakeman/report/report_markdown.rb +156 -0
  130. data/lib/brakeman/report/report_table.rb +107 -0
  131. data/lib/brakeman/report/report_tabs.rb +17 -0
  132. data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
  133. data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
  134. data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
  135. data/lib/brakeman/report/templates/header.html.erb +58 -0
  136. data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
  137. data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
  138. data/lib/brakeman/report/templates/overview.html.erb +38 -0
  139. data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
  140. data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
  141. data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
  142. data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
  143. data/lib/brakeman/rescanner.rb +483 -0
  144. data/lib/brakeman/scanner.rb +317 -0
  145. data/lib/brakeman/tracker.rb +347 -0
  146. data/lib/brakeman/tracker/collection.rb +93 -0
  147. data/lib/brakeman/tracker/config.rb +101 -0
  148. data/lib/brakeman/tracker/constants.rb +101 -0
  149. data/lib/brakeman/tracker/controller.rb +161 -0
  150. data/lib/brakeman/tracker/library.rb +17 -0
  151. data/lib/brakeman/tracker/model.rb +90 -0
  152. data/lib/brakeman/tracker/template.rb +33 -0
  153. data/lib/brakeman/util.rb +481 -0
  154. data/lib/brakeman/version.rb +3 -0
  155. data/lib/brakeman/warning.rb +255 -0
  156. data/lib/brakeman/warning_codes.rb +111 -0
  157. data/lib/ruby_parser/bm_sexp.rb +610 -0
  158. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  159. metadata +362 -0
@@ -0,0 +1,317 @@
1
+ begin
2
+ Brakeman.load_brakeman_dependency 'ruby_parser'
3
+ require 'ruby_parser/bm_sexp.rb'
4
+ require 'ruby_parser/bm_sexp_processor.rb'
5
+ require 'brakeman/processor'
6
+ require 'brakeman/app_tree'
7
+ require 'brakeman/file_parser'
8
+ require 'brakeman/parsers/template_parser'
9
+ rescue LoadError => e
10
+ $stderr.puts e.message
11
+ $stderr.puts "Please install the appropriate dependency."
12
+ exit(-1)
13
+ end
14
+
15
+ #Scans the Rails application.
16
+ class Brakeman::Scanner
17
+ attr_reader :options
18
+ RUBY_1_9 = RUBY_VERSION >= "1.9.0"
19
+
20
+ #Pass in path to the root of the Rails application
21
+ def initialize options, processor = nil
22
+ @options = options
23
+ @app_tree = Brakeman::AppTree.from_options(options)
24
+
25
+ if (!@app_tree.root || !@app_tree.exists?("app")) && !options[:force_scan]
26
+ raise Brakeman::NoApplication, "Please supply the path to a Rails application."
27
+ end
28
+
29
+ @processor = processor || Brakeman::Processor.new(@app_tree, options)
30
+ end
31
+
32
+ #Returns the Tracker generated from the scan
33
+ def tracker
34
+ @processor.tracked_events
35
+ end
36
+
37
+ #Process everything in the Rails application
38
+ def process
39
+ Brakeman.notify "Processing gems..."
40
+ process_gems
41
+ guess_rails_version
42
+ Brakeman.notify "Processing configuration..."
43
+ process_config
44
+ Brakeman.notify "Parsing files..."
45
+ parse_files
46
+ Brakeman.notify "Processing initializers..."
47
+ process_initializers
48
+ Brakeman.notify "Processing libs..."
49
+ process_libs
50
+ Brakeman.notify "Processing routes... "
51
+ process_routes
52
+ Brakeman.notify "Processing templates... "
53
+ process_templates
54
+ Brakeman.notify "Processing data flow in templates..."
55
+ process_template_data_flows
56
+ Brakeman.notify "Processing models... "
57
+ process_models
58
+ Brakeman.notify "Processing controllers... "
59
+ process_controllers
60
+ Brakeman.notify "Processing data flow in controllers..."
61
+ process_controller_data_flows
62
+ Brakeman.notify "Indexing call sites... "
63
+ index_call_sites
64
+ tracker
65
+ end
66
+
67
+ def parse_files
68
+ fp = Brakeman::FileParser.new tracker, @app_tree
69
+
70
+ files = {
71
+ :initializers => @app_tree.initializer_paths,
72
+ :controllers => @app_tree.controller_paths,
73
+ :models => @app_tree.model_paths
74
+ }
75
+
76
+ unless options[:skip_libs]
77
+ files[:libs] = @app_tree.lib_paths
78
+ end
79
+
80
+ files.each do |name, paths|
81
+ fp.parse_files paths, name
82
+ end
83
+
84
+ template_parser = Brakeman::TemplateParser.new(tracker, fp)
85
+
86
+ fp.read_files(@app_tree.template_paths, :templates) do |path, contents|
87
+ template_parser.parse_template path, contents
88
+ end
89
+
90
+ @file_list = fp.file_list
91
+ end
92
+
93
+ #Process config/environment.rb and config/gems.rb
94
+ #
95
+ #Stores parsed information in tracker.config
96
+ def process_config
97
+ if options[:rails3] or options[:rails4] or options[:rails5]
98
+ process_config_file "application.rb"
99
+ process_config_file "environments/production.rb"
100
+ else
101
+ process_config_file "environment.rb"
102
+ process_config_file "gems.rb"
103
+ end
104
+
105
+ if @app_tree.exists?("vendor/plugins/rails_xss") or
106
+ options[:rails3] or options[:escape_html]
107
+
108
+ tracker.config.escape_html = true
109
+ Brakeman.notify "[Notice] Escaping HTML by default"
110
+ end
111
+ end
112
+
113
+ def process_config_file file
114
+ path = "config/#{file}"
115
+
116
+ if @app_tree.exists?(path)
117
+ @processor.process_config(parse_ruby(@app_tree.read(path)), path)
118
+ end
119
+
120
+ rescue => e
121
+ Brakeman.notify "[Notice] Error while processing #{path}"
122
+ tracker.error e.exception(e.message + "\nwhile processing #{path}"), e.backtrace
123
+ end
124
+
125
+ private :process_config_file
126
+
127
+ #Process Gemfile
128
+ def process_gems
129
+ gem_files = {}
130
+ if @app_tree.exists? "Gemfile"
131
+ gem_files[:gemfile] = { :src => parse_ruby(@app_tree.read("Gemfile")), :file => "Gemfile" }
132
+ elsif @app_tree.exists? "gems.rb"
133
+ gem_files[:gemfile] = { :src => parse_ruby(@app_tree.read("gems.rb")), :file => "gems.rb" }
134
+ end
135
+
136
+ if @app_tree.exists? "Gemfile.lock"
137
+ gem_files[:gemlock] = { :src => @app_tree.read("Gemfile.lock"), :file => "Gemfile.lock" }
138
+ elsif @app_tree.exists? "gems.locked"
139
+ gem_files[:gemlock] = { :src => @app_tree.read("gems.locked"), :file => "gems.locked" }
140
+ end
141
+
142
+ if gem_files[:gemfile] or gem_files[:gemlock]
143
+ @processor.process_gems gem_files
144
+ end
145
+ rescue => e
146
+ Brakeman.notify "[Notice] Error while processing Gemfile."
147
+ tracker.error e.exception(e.message + "\nWhile processing Gemfile"), e.backtrace
148
+ end
149
+
150
+ #Set :rails3/:rails4 option if version was not determined from Gemfile
151
+ def guess_rails_version
152
+ unless tracker.options[:rails3] or tracker.options[:rails4]
153
+ if @app_tree.exists?("script/rails")
154
+ tracker.options[:rails3] = true
155
+ Brakeman.notify "[Notice] Detected Rails 3 application"
156
+ elsif @app_tree.exists?("app/channels")
157
+ tracker.options[:rails3] = true
158
+ tracker.options[:rails4] = true
159
+ tracker.options[:rails5] = true
160
+ Brakeman.notify "[Notice] Detected Rails 5 application"
161
+ elsif not @app_tree.exists?("script")
162
+ tracker.options[:rails3] = true
163
+ tracker.options[:rails4] = true
164
+ Brakeman.notify "[Notice] Detected Rails 4 application"
165
+ end
166
+ end
167
+ end
168
+
169
+ #Process all the .rb files in config/initializers/
170
+ #
171
+ #Adds parsed information to tracker.initializers
172
+ def process_initializers
173
+ track_progress @file_list[:initializers] do |init|
174
+ Brakeman.debug "Processing #{init[:path]}"
175
+ process_initializer init
176
+ end
177
+ end
178
+
179
+ #Process an initializer
180
+ def process_initializer init
181
+ @processor.process_initializer(init.path, init.ast)
182
+ end
183
+
184
+ #Process all .rb in lib/
185
+ #
186
+ #Adds parsed information to tracker.libs.
187
+ def process_libs
188
+ if options[:skip_libs]
189
+ Brakeman.notify '[Skipping]'
190
+ return
191
+ end
192
+
193
+ track_progress @file_list[:libs] do |lib|
194
+ Brakeman.debug "Processing #{lib.path}"
195
+ process_lib lib
196
+ end
197
+ end
198
+
199
+ #Process a library
200
+ def process_lib lib
201
+ @processor.process_lib lib.ast, lib.path
202
+ end
203
+
204
+ #Process config/routes.rb
205
+ #
206
+ #Adds parsed information to tracker.routes
207
+ def process_routes
208
+ if @app_tree.exists?("config/routes.rb")
209
+ begin
210
+ @processor.process_routes parse_ruby(@app_tree.read("config/routes.rb"))
211
+ rescue => e
212
+ tracker.error e.exception(e.message + "\nWhile processing routes.rb"), e.backtrace
213
+ Brakeman.notify "[Notice] Error while processing routes - assuming all public controller methods are actions."
214
+ options[:assume_all_routes] = true
215
+ end
216
+ else
217
+ Brakeman.notify "[Notice] No route information found"
218
+ end
219
+ end
220
+
221
+ #Process all .rb files in controllers/
222
+ #
223
+ #Adds processed controllers to tracker.controllers
224
+ def process_controllers
225
+ track_progress @file_list[:controllers] do |controller|
226
+ Brakeman.debug "Processing #{controller.path}"
227
+ process_controller controller
228
+ end
229
+ end
230
+
231
+ def process_controller_data_flows
232
+ controllers = tracker.controllers.sort_by { |name, _| name.to_s }
233
+
234
+ track_progress controllers, "controllers" do |name, controller|
235
+ Brakeman.debug "Processing #{name}"
236
+ controller.src.each do |file, src|
237
+ @processor.process_controller_alias name, src, nil, file
238
+ end
239
+ end
240
+
241
+ #No longer need these processed filter methods
242
+ tracker.filter_cache.clear
243
+ end
244
+
245
+ def process_controller astfile
246
+ begin
247
+ @processor.process_controller(astfile.ast, astfile.path)
248
+ rescue => e
249
+ tracker.error e.exception(e.message + "\nWhile processing #{astfile.path}"), e.backtrace
250
+ end
251
+ end
252
+
253
+ #Process all views and partials in views/
254
+ #
255
+ #Adds processed views to tracker.views
256
+ def process_templates
257
+ templates = @file_list[:templates].sort_by { |t| t[:path] }
258
+
259
+ track_progress templates, "templates" do |template|
260
+ Brakeman.debug "Processing #{template[:path]}"
261
+ process_template template
262
+ end
263
+ end
264
+
265
+ def process_template template
266
+ @processor.process_template(template.name, template.ast, template.type, nil, template.path)
267
+ end
268
+
269
+ def process_template_data_flows
270
+ templates = tracker.templates.sort_by { |name, _| name.to_s }
271
+
272
+ track_progress templates, "templates" do |name, template|
273
+ Brakeman.debug "Processing #{name}"
274
+ @processor.process_template_alias template
275
+ end
276
+ end
277
+
278
+ #Process all the .rb files in models/
279
+ #
280
+ #Adds the processed models to tracker.models
281
+ def process_models
282
+ track_progress @file_list[:models] do |model|
283
+ Brakeman.debug "Processing #{model[:path]}"
284
+ process_model model[:path], model[:ast]
285
+ end
286
+ end
287
+
288
+ def process_model path, ast
289
+ @processor.process_model(ast, path)
290
+ end
291
+
292
+ def track_progress list, type = "files"
293
+ total = list.length
294
+ current = 0
295
+ list.each do |item|
296
+ report_progress current, total, type
297
+ current += 1
298
+ yield item
299
+ end
300
+ end
301
+
302
+ def report_progress(current, total, type = "files")
303
+ return unless @options[:report_progress]
304
+ $stderr.print " #{current}/#{total} #{type} processed\r"
305
+ end
306
+
307
+ def index_call_sites
308
+ tracker.index_call_sites
309
+ end
310
+
311
+ def parse_ruby input
312
+ RubyParser.new.parse input
313
+ end
314
+ end
315
+
316
+ # This is to allow operation without loading the Haml library
317
+ module Haml; class Error < StandardError; end; end
@@ -0,0 +1,347 @@
1
+ require 'set'
2
+ require 'brakeman/call_index'
3
+ require 'brakeman/checks'
4
+ require 'brakeman/report'
5
+ require 'brakeman/processors/lib/find_call'
6
+ require 'brakeman/processors/lib/find_all_calls'
7
+ require 'brakeman/tracker/config'
8
+ require 'brakeman/tracker/constants'
9
+
10
+ #The Tracker keeps track of all the processed information.
11
+ class Brakeman::Tracker
12
+ attr_accessor :controllers, :constants, :templates, :models, :errors,
13
+ :checks, :initializers, :config, :routes, :processor, :libs,
14
+ :template_cache, :options, :filter_cache, :start_time, :end_time,
15
+ :duration, :ignored_filter
16
+
17
+ #Place holder when there should be a model, but it is not
18
+ #clear what model it will be.
19
+ UNKNOWN_MODEL = :BrakemanUnresolvedModel
20
+
21
+ #Creates a new Tracker.
22
+ #
23
+ #The Processor argument is only used by other Processors
24
+ #that might need to access it.
25
+ def initialize(app_tree, processor = nil, options = {})
26
+ @app_tree = app_tree
27
+ @processor = processor
28
+ @options = options
29
+
30
+ @config = Brakeman::Config.new(self)
31
+ @templates = {}
32
+ @controllers = {}
33
+ #Initialize models with the unknown model so
34
+ #we can match models later without knowing precisely what
35
+ #class they are.
36
+ @models = {}
37
+ @models[UNKNOWN_MODEL] = Brakeman::Model.new(UNKNOWN_MODEL, nil, nil, nil, self)
38
+ @routes = {}
39
+ @initializers = {}
40
+ @errors = []
41
+ @libs = {}
42
+ @constants = Brakeman::Constants.new
43
+ @checks = nil
44
+ @processed = nil
45
+ @template_cache = Set.new
46
+ @filter_cache = {}
47
+ @call_index = nil
48
+ @start_time = Time.now
49
+ @end_time = nil
50
+ @duration = nil
51
+ end
52
+
53
+ #Add an error to the list. If no backtrace is given,
54
+ #the one from the exception will be used.
55
+ def error exception, backtrace = nil
56
+ backtrace ||= exception.backtrace
57
+ unless backtrace.is_a? Array
58
+ backtrace = [ backtrace ]
59
+ end
60
+
61
+ Brakeman.debug exception
62
+ Brakeman.debug backtrace
63
+
64
+ @errors << { :error => exception.to_s.gsub("\n", " "), :backtrace => backtrace }
65
+ end
66
+
67
+ #Run a set of checks on the current information. Results will be stored
68
+ #in Tracker#checks.
69
+ def run_checks
70
+ @checks = Brakeman::Checks.run_checks(@app_tree, self)
71
+
72
+ @end_time = Time.now
73
+ @duration = @end_time - @start_time
74
+ @checks
75
+ end
76
+
77
+ def app_path
78
+ @app_path ||= File.expand_path @options[:app_path]
79
+ end
80
+
81
+ #Iterate over all methods in controllers and models.
82
+ def each_method
83
+ classes = [self.controllers, self.models]
84
+
85
+ if @options[:index_libs]
86
+ classes << self.libs
87
+ end
88
+
89
+ classes.each do |set|
90
+ set.each do |set_name, collection|
91
+ collection.each_method do |method_name, definition|
92
+ src = definition[:src]
93
+ yield src, set_name, method_name, definition[:file]
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ #Iterates over each template, yielding the name and the template.
100
+ #Prioritizes templates which have been rendered.
101
+ def each_template
102
+ if @processed.nil?
103
+ @processed, @rest = templates.keys.sort_by{|template| template.to_s}.partition { |k| k.to_s.include? "." }
104
+ end
105
+
106
+ @processed.each do |k|
107
+ yield k, templates[k]
108
+ end
109
+
110
+ @rest.each do |k|
111
+ yield k, templates[k]
112
+ end
113
+ end
114
+
115
+
116
+ def each_class
117
+ classes = [self.controllers, self.models]
118
+
119
+ if @options[:index_libs]
120
+ classes << self.libs
121
+ end
122
+
123
+ classes.each do |set|
124
+ set.each do |set_name, collection|
125
+ collection.src.each do |file, src|
126
+ yield src, set_name, file
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ #Find a method call.
133
+ #
134
+ #Options:
135
+ # * :target => target name(s)
136
+ # * :method => method name(s)
137
+ # * :chained => search in method chains
138
+ #
139
+ #If :target => false or :target => nil, searches for methods without a target.
140
+ #Targets and methods can be specified as a symbol, an array of symbols,
141
+ #or a regular expression.
142
+ #
143
+ #If :chained => true, matches target at head of method chain and method at end.
144
+ #
145
+ #For example:
146
+ #
147
+ # find_call :target => User, :method => :all, :chained => true
148
+ #
149
+ #could match
150
+ #
151
+ # User.human.active.all(...)
152
+ #
153
+ def find_call options
154
+ index_call_sites unless @call_index
155
+ @call_index.find_calls options
156
+ end
157
+
158
+ #Searches the initializers for a method call
159
+ def check_initializers target, method
160
+ finder = Brakeman::FindCall.new target, method, self
161
+
162
+ initializers.sort.each do |name, initializer|
163
+ finder.process_source initializer
164
+ end
165
+
166
+ finder.matches
167
+ end
168
+
169
+ #Returns a Report with this Tracker's information
170
+ def report
171
+ Brakeman::Report.new(@app_tree, self)
172
+ end
173
+
174
+ def warnings
175
+ self.checks.all_warnings
176
+ end
177
+
178
+ def filtered_warnings
179
+ if self.ignored_filter
180
+ self.warnings.reject do |w|
181
+ self.ignored_filter.ignored? w
182
+ end
183
+ else
184
+ self.warnings
185
+ end
186
+ end
187
+
188
+ def add_constant name, value, context = nil
189
+ @constants.add name, value, context
190
+ end
191
+
192
+ def constant_lookup name
193
+ @constants.get_literal name
194
+ end
195
+
196
+ def index_call_sites
197
+ finder = Brakeman::FindAllCalls.new self
198
+
199
+ self.each_method do |definition, set_name, method_name, file|
200
+ finder.process_source definition, :class => set_name, :method => method_name, :file => file
201
+ end
202
+
203
+ self.each_class do |definition, set_name, file|
204
+ finder.process_source definition, :class => set_name, :file => file
205
+ end
206
+
207
+ self.each_template do |name, template|
208
+ finder.process_source template.src, :template => template, :file => template.file
209
+ end
210
+
211
+ @call_index = Brakeman::CallIndex.new finder.calls
212
+ end
213
+
214
+ #Reindex call sites
215
+ #
216
+ #Takes a set of symbols which can include :templates, :models,
217
+ #or :controllers
218
+ #
219
+ #This will limit reindexing to the given sets
220
+ def reindex_call_sites locations
221
+ #If reindexing templates, models, and controllers, just redo
222
+ #everything
223
+ if locations.length == 3
224
+ return index_call_sites
225
+ end
226
+
227
+ if locations.include? :templates
228
+ @call_index.remove_template_indexes
229
+ end
230
+
231
+ classes_to_reindex = Set.new
232
+ method_sets = []
233
+
234
+ if locations.include? :models
235
+ classes_to_reindex.merge self.models.keys
236
+ method_sets << self.models
237
+ end
238
+
239
+ if locations.include? :controllers
240
+ classes_to_reindex.merge self.controllers.keys
241
+ method_sets << self.controllers
242
+ end
243
+
244
+ @call_index.remove_indexes_by_class classes_to_reindex
245
+
246
+ finder = Brakeman::FindAllCalls.new self
247
+
248
+ method_sets.each do |set|
249
+ set.each do |set_name, info|
250
+ info.each_method do |method_name, definition|
251
+ src = definition[:src]
252
+ finder.process_source src, :class => set_name, :method => method_name, :file => definition[:file]
253
+ end
254
+ end
255
+ end
256
+
257
+ if locations.include? :templates
258
+ self.each_template do |name, template|
259
+ finder.process_source template.src, :template => template, :file => template.file
260
+ end
261
+ end
262
+
263
+ @call_index.index_calls finder.calls
264
+ end
265
+
266
+ #Clear information related to templates.
267
+ #If :only_rendered => true, will delete templates rendered from
268
+ #controllers (but not those rendered from other templates)
269
+ def reset_templates options = { :only_rendered => false }
270
+ if options[:only_rendered]
271
+ @templates.delete_if do |name, template|
272
+ template.rendered_from_controller?
273
+ end
274
+ else
275
+ @templates = {}
276
+ end
277
+ @processed = nil
278
+ @rest = nil
279
+ @template_cache.clear
280
+ end
281
+
282
+ #Clear information related to template
283
+ def reset_template name
284
+ name = name.to_sym
285
+ @templates.delete name
286
+ @processed = nil
287
+ @rest = nil
288
+ @template_cache.clear
289
+ end
290
+
291
+ #Clear information related to model
292
+ def reset_model path
293
+ model_name = nil
294
+
295
+ @models.each do |name, model|
296
+ if model.files.include?(path)
297
+ model_name = name
298
+ break
299
+ end
300
+ end
301
+
302
+ @models.delete model_name
303
+ end
304
+
305
+ #Clear information related to model
306
+ def reset_lib path
307
+ lib_name = nil
308
+
309
+ @libs.each do |name, lib|
310
+ if lib.files.include?(path)
311
+ lib_name = name
312
+ break
313
+ end
314
+ end
315
+
316
+ @libs.delete lib_name
317
+ end
318
+
319
+ def reset_controller path
320
+ controller_name = nil
321
+
322
+ #Remove from controller
323
+ @controllers.each do |name, controller|
324
+ if controller.files.include?(path)
325
+ controller_name = name
326
+
327
+ #Remove templates rendered from this controller
328
+ @templates.each do |template_name, template|
329
+ if template.render_path and template.render_path.include_controller? name
330
+ reset_template template_name
331
+ @call_index.remove_template_indexes template_name
332
+ end
333
+ end
334
+
335
+ #Remove calls indexed from this controller
336
+ @call_index.remove_indexes_by_class [name]
337
+ break
338
+ end
339
+ end
340
+ @controllers.delete controller_name
341
+ end
342
+
343
+ #Clear information about routes
344
+ def reset_routes
345
+ @routes = {}
346
+ end
347
+ end