railroader 4.3.4

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 (165) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +1091 -0
  3. data/FEATURES +16 -0
  4. data/README.md +174 -0
  5. data/bin/railroader +8 -0
  6. data/lib/railroader/app_tree.rb +191 -0
  7. data/lib/railroader/call_index.rb +219 -0
  8. data/lib/railroader/checks/base_check.rb +505 -0
  9. data/lib/railroader/checks/check_basic_auth.rb +88 -0
  10. data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
  11. data/lib/railroader/checks/check_content_tag.rb +200 -0
  12. data/lib/railroader/checks/check_create_with.rb +74 -0
  13. data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
  14. data/lib/railroader/checks/check_default_routes.rb +86 -0
  15. data/lib/railroader/checks/check_deserialize.rb +56 -0
  16. data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
  17. data/lib/railroader/checks/check_digest_dos.rb +38 -0
  18. data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
  19. data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
  20. data/lib/railroader/checks/check_escape_function.rb +21 -0
  21. data/lib/railroader/checks/check_evaluation.rb +35 -0
  22. data/lib/railroader/checks/check_execute.rb +189 -0
  23. data/lib/railroader/checks/check_file_access.rb +71 -0
  24. data/lib/railroader/checks/check_file_disclosure.rb +35 -0
  25. data/lib/railroader/checks/check_filter_skipping.rb +31 -0
  26. data/lib/railroader/checks/check_forgery_setting.rb +81 -0
  27. data/lib/railroader/checks/check_header_dos.rb +31 -0
  28. data/lib/railroader/checks/check_i18n_xss.rb +48 -0
  29. data/lib/railroader/checks/check_jruby_xml.rb +36 -0
  30. data/lib/railroader/checks/check_json_encoding.rb +47 -0
  31. data/lib/railroader/checks/check_json_parsing.rb +107 -0
  32. data/lib/railroader/checks/check_link_to.rb +132 -0
  33. data/lib/railroader/checks/check_link_to_href.rb +146 -0
  34. data/lib/railroader/checks/check_mail_to.rb +49 -0
  35. data/lib/railroader/checks/check_mass_assignment.rb +196 -0
  36. data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
  37. data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
  38. data/lib/railroader/checks/check_model_attributes.rb +119 -0
  39. data/lib/railroader/checks/check_model_serialize.rb +67 -0
  40. data/lib/railroader/checks/check_nested_attributes.rb +38 -0
  41. data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
  42. data/lib/railroader/checks/check_number_to_currency.rb +74 -0
  43. data/lib/railroader/checks/check_permit_attributes.rb +43 -0
  44. data/lib/railroader/checks/check_quote_table_name.rb +40 -0
  45. data/lib/railroader/checks/check_redirect.rb +256 -0
  46. data/lib/railroader/checks/check_regex_dos.rb +68 -0
  47. data/lib/railroader/checks/check_render.rb +97 -0
  48. data/lib/railroader/checks/check_render_dos.rb +37 -0
  49. data/lib/railroader/checks/check_render_inline.rb +53 -0
  50. data/lib/railroader/checks/check_response_splitting.rb +21 -0
  51. data/lib/railroader/checks/check_route_dos.rb +42 -0
  52. data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
  53. data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
  54. data/lib/railroader/checks/check_secrets.rb +40 -0
  55. data/lib/railroader/checks/check_select_tag.rb +59 -0
  56. data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
  57. data/lib/railroader/checks/check_send.rb +47 -0
  58. data/lib/railroader/checks/check_send_file.rb +19 -0
  59. data/lib/railroader/checks/check_session_manipulation.rb +35 -0
  60. data/lib/railroader/checks/check_session_settings.rb +176 -0
  61. data/lib/railroader/checks/check_simple_format.rb +58 -0
  62. data/lib/railroader/checks/check_single_quotes.rb +101 -0
  63. data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
  64. data/lib/railroader/checks/check_sql.rb +700 -0
  65. data/lib/railroader/checks/check_sql_cves.rb +106 -0
  66. data/lib/railroader/checks/check_ssl_verify.rb +48 -0
  67. data/lib/railroader/checks/check_strip_tags.rb +89 -0
  68. data/lib/railroader/checks/check_symbol_dos.rb +71 -0
  69. data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
  70. data/lib/railroader/checks/check_translate_bug.rb +45 -0
  71. data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
  72. data/lib/railroader/checks/check_unscoped_find.rb +57 -0
  73. data/lib/railroader/checks/check_validation_regex.rb +116 -0
  74. data/lib/railroader/checks/check_weak_hash.rb +148 -0
  75. data/lib/railroader/checks/check_without_protection.rb +80 -0
  76. data/lib/railroader/checks/check_xml_dos.rb +45 -0
  77. data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
  78. data/lib/railroader/checks.rb +209 -0
  79. data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
  80. data/lib/railroader/commandline.rb +179 -0
  81. data/lib/railroader/differ.rb +66 -0
  82. data/lib/railroader/file_parser.rb +54 -0
  83. data/lib/railroader/format/style.css +133 -0
  84. data/lib/railroader/options.rb +339 -0
  85. data/lib/railroader/parsers/rails2_erubis.rb +6 -0
  86. data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
  87. data/lib/railroader/parsers/rails3_erubis.rb +81 -0
  88. data/lib/railroader/parsers/template_parser.rb +108 -0
  89. data/lib/railroader/processor.rb +102 -0
  90. data/lib/railroader/processors/alias_processor.rb +1229 -0
  91. data/lib/railroader/processors/base_processor.rb +295 -0
  92. data/lib/railroader/processors/config_processor.rb +14 -0
  93. data/lib/railroader/processors/controller_alias_processor.rb +278 -0
  94. data/lib/railroader/processors/controller_processor.rb +249 -0
  95. data/lib/railroader/processors/erb_template_processor.rb +77 -0
  96. data/lib/railroader/processors/erubis_template_processor.rb +92 -0
  97. data/lib/railroader/processors/gem_processor.rb +64 -0
  98. data/lib/railroader/processors/haml_template_processor.rb +191 -0
  99. data/lib/railroader/processors/lib/basic_processor.rb +37 -0
  100. data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
  101. data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
  102. data/lib/railroader/processors/lib/find_call.rb +183 -0
  103. data/lib/railroader/processors/lib/find_return_value.rb +166 -0
  104. data/lib/railroader/processors/lib/module_helper.rb +111 -0
  105. data/lib/railroader/processors/lib/processor_helper.rb +88 -0
  106. data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
  107. data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
  108. data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
  109. data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
  110. data/lib/railroader/processors/lib/render_helper.rb +181 -0
  111. data/lib/railroader/processors/lib/render_path.rb +107 -0
  112. data/lib/railroader/processors/lib/route_helper.rb +68 -0
  113. data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
  114. data/lib/railroader/processors/library_processor.rb +74 -0
  115. data/lib/railroader/processors/model_processor.rb +91 -0
  116. data/lib/railroader/processors/output_processor.rb +144 -0
  117. data/lib/railroader/processors/route_processor.rb +17 -0
  118. data/lib/railroader/processors/slim_template_processor.rb +111 -0
  119. data/lib/railroader/processors/template_alias_processor.rb +118 -0
  120. data/lib/railroader/processors/template_processor.rb +85 -0
  121. data/lib/railroader/report/config/remediation.yml +71 -0
  122. data/lib/railroader/report/ignore/config.rb +153 -0
  123. data/lib/railroader/report/ignore/interactive.rb +362 -0
  124. data/lib/railroader/report/pager.rb +112 -0
  125. data/lib/railroader/report/renderer.rb +24 -0
  126. data/lib/railroader/report/report_base.rb +292 -0
  127. data/lib/railroader/report/report_codeclimate.rb +79 -0
  128. data/lib/railroader/report/report_csv.rb +55 -0
  129. data/lib/railroader/report/report_hash.rb +23 -0
  130. data/lib/railroader/report/report_html.rb +216 -0
  131. data/lib/railroader/report/report_json.rb +45 -0
  132. data/lib/railroader/report/report_markdown.rb +107 -0
  133. data/lib/railroader/report/report_table.rb +117 -0
  134. data/lib/railroader/report/report_tabs.rb +17 -0
  135. data/lib/railroader/report/report_text.rb +198 -0
  136. data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
  137. data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
  138. data/lib/railroader/report/templates/error_overview.html.erb +29 -0
  139. data/lib/railroader/report/templates/header.html.erb +58 -0
  140. data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
  141. data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
  142. data/lib/railroader/report/templates/overview.html.erb +38 -0
  143. data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
  144. data/lib/railroader/report/templates/template_overview.html.erb +21 -0
  145. data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
  146. data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
  147. data/lib/railroader/report.rb +88 -0
  148. data/lib/railroader/rescanner.rb +483 -0
  149. data/lib/railroader/scanner.rb +321 -0
  150. data/lib/railroader/tracker/collection.rb +93 -0
  151. data/lib/railroader/tracker/config.rb +154 -0
  152. data/lib/railroader/tracker/constants.rb +171 -0
  153. data/lib/railroader/tracker/controller.rb +161 -0
  154. data/lib/railroader/tracker/library.rb +17 -0
  155. data/lib/railroader/tracker/model.rb +90 -0
  156. data/lib/railroader/tracker/template.rb +33 -0
  157. data/lib/railroader/tracker.rb +362 -0
  158. data/lib/railroader/util.rb +503 -0
  159. data/lib/railroader/version.rb +3 -0
  160. data/lib/railroader/warning.rb +294 -0
  161. data/lib/railroader/warning_codes.rb +117 -0
  162. data/lib/railroader.rb +544 -0
  163. data/lib/ruby_parser/bm_sexp.rb +626 -0
  164. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  165. metadata +386 -0
@@ -0,0 +1,483 @@
1
+ require 'railroader/scanner'
2
+ require 'railroader/util'
3
+ require 'railroader/differ'
4
+
5
+ #Class for rescanning changed files after an initial scan
6
+ class Railroader::Rescanner < Railroader::Scanner
7
+ include Railroader::Util
8
+ KNOWN_TEMPLATE_EXTENSIONS = Railroader::TemplateParser::KNOWN_TEMPLATE_EXTENSIONS
9
+ SCAN_ORDER = [:config, :gemfile, :initializer, :lib, :routes, :template,
10
+ :model, :controller]
11
+
12
+ #Create new Rescanner to scan changed files
13
+ def initialize options, processor, changed_files
14
+ super(options, processor)
15
+
16
+ @paths = changed_files.map {|f| @app_tree.expand_path(f) }
17
+ @old_results = tracker.filtered_warnings #Old warnings from previous scan
18
+ @changes = nil #True if files had to be rescanned
19
+ @reindex = Set.new
20
+ end
21
+
22
+ #Runs checks.
23
+ #Will rescan files if they have not already been scanned
24
+ def recheck
25
+ rescan if @changes.nil?
26
+
27
+ tracker.run_checks if @changes
28
+
29
+ Railroader::RescanReport.new @old_results, tracker
30
+ end
31
+
32
+ #Rescans changed files
33
+ def rescan
34
+ tracker.template_cache.clear
35
+
36
+ paths_by_type = {}
37
+
38
+ SCAN_ORDER.each do |type|
39
+ paths_by_type[type] = []
40
+ end
41
+
42
+ @paths.each do |path|
43
+ type = file_type(path)
44
+ paths_by_type[type] << path unless type == :unknown
45
+ end
46
+
47
+ @changes = false
48
+
49
+ SCAN_ORDER.each do |type|
50
+ paths_by_type[type].each do |path|
51
+ Railroader.debug "Rescanning #{path} as #{type}"
52
+
53
+ if rescan_file path, type
54
+ @changes = true
55
+ end
56
+ end
57
+ end
58
+
59
+ if @changes and not @reindex.empty?
60
+ tracker.reindex_call_sites @reindex
61
+ end
62
+
63
+ self
64
+ end
65
+
66
+ #Rescans a single file
67
+ def rescan_file path, type = nil
68
+ type ||= file_type path
69
+
70
+ unless @app_tree.path_exists?(path)
71
+ return rescan_deleted_file path, type
72
+ end
73
+
74
+ case type
75
+ when :controller
76
+ rescan_controller path
77
+ when :template
78
+ rescan_template path
79
+ when :model
80
+ rescan_model path
81
+ when :lib
82
+ rescan_lib path
83
+ when :config
84
+ process_config
85
+ when :initializer
86
+ rescan_initializer path
87
+ when :routes
88
+ rescan_routes
89
+ when :gemfile
90
+ if tracker.config.has_gem? :rails_xss and tracker.config.escape_html?
91
+ tracker.config.escape_html = false
92
+ end
93
+
94
+ process_gems
95
+ else
96
+ return false #Nothing to do, file hopefully does not need to be rescanned
97
+ end
98
+
99
+ true
100
+ end
101
+
102
+ def rescan_controller path
103
+ controller = tracker.reset_controller path
104
+ paths = controller.nil? ? [path] : controller.files
105
+ parse_ruby_files(paths).each do |astfile|
106
+ process_controller astfile
107
+ end
108
+
109
+ #Process data flow and template rendering
110
+ #from the controller
111
+ tracker.controllers.each do |name, controller|
112
+ if controller.files.include?(path)
113
+ tracker.templates.each do |template_name, template|
114
+ next unless template.render_path
115
+ if template.render_path.include_controller? name
116
+ tracker.reset_template template_name
117
+ end
118
+ end
119
+
120
+ controller.src.each do |file, src|
121
+ @processor.process_controller_alias controller.name, src, nil, file
122
+ end
123
+ end
124
+ end
125
+
126
+ @reindex << :templates << :controllers
127
+ end
128
+
129
+ def rescan_template path
130
+ return unless path.match KNOWN_TEMPLATE_EXTENSIONS and @app_tree.path_exists?(path)
131
+
132
+ template_name = template_path_to_name(path)
133
+
134
+ tracker.reset_template template_name
135
+ fp = Railroader::FileParser.new(tracker, @app_tree)
136
+ template_parser = Railroader::TemplateParser.new(tracker, fp)
137
+ template_parser.parse_template path, @app_tree.read_path(path)
138
+ process_template fp.file_list[:templates].first
139
+
140
+ @processor.process_template_alias tracker.templates[template_name]
141
+
142
+ rescan = Set.new
143
+
144
+ #Search for processed template and process it.
145
+ #Search for rendered versions of template and re-render (if necessary)
146
+ tracker.templates.each do |_name, template|
147
+ if template.file == path or template.file.nil?
148
+ next unless template.render_path and template.name.to_sym == template_name.to_sym
149
+
150
+ template.render_path.each do |from|
151
+ case from[:type]
152
+ when :template
153
+ rescan << [:template, from[:name]]
154
+ when :controller
155
+ rescan << [:controller, from[:class], from[:method]]
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ rescan.each do |r|
162
+ if r[0] == :controller
163
+ controller = tracker.controllers[r[1]]
164
+
165
+ controller.src.each do |file, src|
166
+ unless @paths.include? file
167
+ @processor.process_controller_alias controller.name, src, r[2], file
168
+ end
169
+ end
170
+ elsif r[0] == :template
171
+ template = tracker.templates[r[1]]
172
+
173
+ rescan_template template.file
174
+ end
175
+ end
176
+
177
+ @reindex << :templates
178
+ end
179
+
180
+ def rescan_model path
181
+ num_models = tracker.models.length
182
+ model = tracker.reset_model path
183
+ paths = model.nil? ? [path] : model.files
184
+ parse_ruby_files(paths).each do |astfile|
185
+ process_model astfile.path, astfile.ast
186
+ end
187
+
188
+ #Only need to rescan other things if a model is added or removed
189
+ if num_models != tracker.models.length
190
+ process_template_data_flows
191
+ process_controller_data_flows
192
+ @reindex << :templates << :controllers
193
+ end
194
+
195
+ @reindex << :models
196
+ end
197
+
198
+ def rescan_lib path
199
+ lib = tracker.reset_lib path
200
+ paths = lib.nil? ? [path] : lib.files
201
+ parse_ruby_files(paths).each do |astfile|
202
+ process_lib astfile
203
+ end
204
+
205
+ lib = nil
206
+
207
+ tracker.libs.each do |_name, library|
208
+ if library.files.include?(path)
209
+ lib = library
210
+ break
211
+ end
212
+ end
213
+
214
+ rescan_mixin lib if lib
215
+ end
216
+
217
+ def rescan_routes
218
+ # Routes affect which controller methods are treated as actions
219
+ # which affects which templates are rendered, so routes, controllers,
220
+ # and templates rendered from controllers must be rescanned
221
+ tracker.reset_routes
222
+ tracker.reset_templates :only_rendered => true
223
+ process_routes
224
+ process_controller_data_flows
225
+ @reindex << :controllers << :templates
226
+ end
227
+
228
+ def rescan_initializer path
229
+ parse_ruby_files([path]).each do |astfile|
230
+ process_initializer astfile
231
+ end
232
+ end
233
+
234
+ #Handle rescanning when a file is deleted
235
+ def rescan_deleted_file path, type
236
+ case type
237
+ when :controller
238
+ rescan_controller path
239
+ when :template
240
+ rescan_deleted_template path
241
+ when :model
242
+ rescan_model path
243
+ when :lib
244
+ rescan_deleted_lib path
245
+ when :initializer
246
+ rescan_deleted_initializer path
247
+ else
248
+ if remove_deleted_file path
249
+ return true
250
+ else
251
+ Railroader.notify "Ignoring deleted file: #{path}"
252
+ end
253
+ end
254
+
255
+ true
256
+ end
257
+
258
+ def rescan_deleted_template path
259
+ return unless path.match KNOWN_TEMPLATE_EXTENSIONS
260
+
261
+ template_name = template_path_to_name(path)
262
+
263
+ #Remove template
264
+ tracker.reset_template template_name
265
+
266
+ rendered_from_controller = /^#{template_name}\.(.+Controller)#(.+)/
267
+ rendered_from_view = /^#{template_name}\.Template:(.+)/
268
+
269
+ #Remove any rendered versions, or partials rendered from it
270
+ tracker.templates.delete_if do |_name, template|
271
+ template.file == path or template.name.to_sym == template_name.to_sym
272
+ end
273
+ end
274
+
275
+ def rescan_deleted_lib path
276
+ deleted_lib = nil
277
+
278
+ tracker.libs.delete_if do |_name, lib|
279
+ if lib.files.include?(path)
280
+ deleted_lib = lib
281
+ true
282
+ end
283
+ end
284
+
285
+ rescan_mixin deleted_lib if deleted_lib
286
+ end
287
+
288
+ def rescan_deleted_initializer path
289
+ tracker.initializers.delete Pathname.new(path).basename.to_s
290
+ end
291
+
292
+ #Check controllers, templates, models and libs for data from file
293
+ #and delete it.
294
+ def remove_deleted_file path
295
+ deleted = false
296
+
297
+ [:controllers, :models, :libs].each do |collection|
298
+ tracker.send(collection).delete_if do |_name, data|
299
+ if data.files.include?(path)
300
+ deleted = true
301
+ true
302
+ end
303
+ end
304
+ end
305
+
306
+ tracker.templates.delete_if do |_name, data|
307
+ if data.file == path
308
+ deleted = true
309
+ true
310
+ end
311
+ end
312
+
313
+ deleted
314
+ end
315
+
316
+ #Guess at what kind of file the path contains
317
+ def file_type path
318
+ case path
319
+ when /\/app\/controllers/
320
+ :controller
321
+ when /\/app\/views/
322
+ :template
323
+ when /\/app\/models/
324
+ :model
325
+ when /\/lib/
326
+ :lib
327
+ when /\/config\/initializers/
328
+ :initializer
329
+ when /config\/routes\.rb/
330
+ :routes
331
+ when /\/config\/.+\.(rb|yml)/
332
+ :config
333
+ when /Gemfile|gems\./
334
+ :gemfile
335
+ else
336
+ :unknown
337
+ end
338
+ end
339
+
340
+ def rescan_mixin lib
341
+ method_names = []
342
+
343
+ lib.each_method do |name, _meth|
344
+ method_names << name
345
+ end
346
+
347
+ to_rescan = []
348
+
349
+ #Rescan controllers that mixed in library
350
+ tracker.controllers.each do |_name, controller|
351
+ if controller.includes.include? lib.name
352
+ controller.files.each do |path|
353
+ unless @paths.include? path
354
+ to_rescan << path
355
+ end
356
+ end
357
+ end
358
+ end
359
+
360
+ to_rescan.each do |controller|
361
+ tracker.reset_controller controller
362
+ rescan_file controller
363
+ end
364
+
365
+ to_rescan = []
366
+
367
+ #Check if a method from this mixin was used to render a template.
368
+ #This is not precise, because a different controller might have the
369
+ #same method...
370
+ tracker.templates.each do |name, template|
371
+ next unless template.render_path
372
+
373
+ if template.render_path.include_any_method? method_names
374
+ name.to_s.match /^([^.]+)/
375
+
376
+ original = tracker.templates[$1.to_sym]
377
+
378
+ if original
379
+ to_rescan << [name, original.file]
380
+ end
381
+ end
382
+ end
383
+
384
+ to_rescan.each do |template|
385
+ tracker.reset_template template[0]
386
+ rescan_file template[1]
387
+ end
388
+ end
389
+
390
+ def parse_ruby_files list
391
+ paths = list.select { |path| @app_tree.path_exists? path }
392
+ file_parser = Railroader::FileParser.new(tracker, @app_tree)
393
+ file_parser.parse_files paths, :rescan
394
+ file_parser.file_list[:rescan]
395
+ end
396
+ end
397
+
398
+ #Class to make reporting of rescan results simpler to deal with
399
+ class Railroader::RescanReport
400
+ include Railroader::Util
401
+ attr_reader :old_results, :new_results
402
+
403
+ def initialize old_results, tracker
404
+ @tracker = tracker
405
+ @old_results = old_results
406
+ @all_warnings = nil
407
+ @diff = nil
408
+ end
409
+
410
+ #Returns true if any warnings were found (new or old)
411
+ def any_warnings?
412
+ not all_warnings.empty?
413
+ end
414
+
415
+ #Returns an array of all warnings found
416
+ def all_warnings
417
+ @all_warnings ||= @tracker.filtered_warnings
418
+ end
419
+
420
+ #Returns an array of warnings which were in the old report but are not in the
421
+ #new report after rescanning
422
+ def fixed_warnings
423
+ diff[:fixed]
424
+ end
425
+
426
+ #Returns an array of warnings which were in the new report but were not in
427
+ #the old report
428
+ def new_warnings
429
+ diff[:new]
430
+ end
431
+
432
+ #Returns true if there are any new or fixed warnings
433
+ def warnings_changed?
434
+ not (diff[:new].empty? and diff[:fixed].empty?)
435
+ end
436
+
437
+ #Returns a hash of arrays for :new and :fixed warnings
438
+ def diff
439
+ @diff ||= Railroader::Differ.new(all_warnings, @old_results).diff
440
+ end
441
+
442
+ #Returns an array of warnings which were in the old report and the new report
443
+ def existing_warnings
444
+ @old ||= all_warnings.select do |w|
445
+ not new_warnings.include? w
446
+ end
447
+ end
448
+
449
+ #Output total, fixed, and new warnings
450
+ def to_s(verbose = false)
451
+ Railroader.load_railroader_dependency 'terminal-table'
452
+
453
+ if !verbose
454
+ <<-OUTPUT
455
+ Total warnings: #{all_warnings.length}
456
+ Fixed warnings: #{fixed_warnings.length}
457
+ New warnings: #{new_warnings.length}
458
+ OUTPUT
459
+ else
460
+ #Eventually move this to different method, or make default to_s
461
+ out = ""
462
+
463
+ {:fixed => fixed_warnings, :new => new_warnings, :existing => existing_warnings}.each do |warning_type, warnings|
464
+ if warnings.length > 0
465
+ out << "#{warning_type.to_s.titleize} warnings: #{warnings.length}\n"
466
+
467
+ table = Terminal::Table.new(:headings => ["Confidence", "Class", "Method", "Warning Type", "Message"]) do |t|
468
+ warnings.sort_by { |w| w.confidence}.each do |warning|
469
+ w = warning.to_row
470
+
471
+ w["Confidence"] = Railroader::Report::TEXT_CONFIDENCE[w["Confidence"]]
472
+
473
+ t << [w["Confidence"], w["Class"], w["Method"], w["Warning Type"], w["Message"]]
474
+ end
475
+ end
476
+ out << truncate_table(table.to_s)
477
+ end
478
+ end
479
+
480
+ out
481
+ end
482
+ end
483
+ end