brakeman-min 0.5.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/CHANGES +529 -0
  2. data/README.md +74 -28
  3. data/bin/brakeman +60 -266
  4. data/lib/brakeman.rb +422 -0
  5. data/lib/brakeman/app_tree.rb +101 -0
  6. data/lib/brakeman/brakeman.rake +10 -0
  7. data/lib/brakeman/call_index.rb +215 -0
  8. data/lib/brakeman/checks.rb +180 -0
  9. data/lib/brakeman/checks/base_check.rb +538 -0
  10. data/lib/brakeman/checks/check_basic_auth.rb +89 -0
  11. data/lib/brakeman/checks/check_content_tag.rb +162 -0
  12. data/lib/brakeman/checks/check_cross_site_scripting.rb +334 -0
  13. data/lib/{checks → brakeman/checks}/check_default_routes.rb +13 -6
  14. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  15. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  16. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  17. data/lib/brakeman/checks/check_evaluation.rb +33 -0
  18. data/lib/brakeman/checks/check_execute.rb +98 -0
  19. data/lib/brakeman/checks/check_file_access.rb +62 -0
  20. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  21. data/lib/brakeman/checks/check_forgery_setting.rb +54 -0
  22. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  23. data/lib/brakeman/checks/check_json_parsing.rb +102 -0
  24. data/lib/brakeman/checks/check_link_to.rb +132 -0
  25. data/lib/brakeman/checks/check_link_to_href.rb +92 -0
  26. data/lib/{checks → brakeman/checks}/check_mail_to.rb +14 -13
  27. data/lib/brakeman/checks/check_mass_assignment.rb +143 -0
  28. data/lib/brakeman/checks/check_model_attr_accessible.rb +48 -0
  29. data/lib/brakeman/checks/check_model_attributes.rb +118 -0
  30. data/lib/brakeman/checks/check_model_serialize.rb +66 -0
  31. data/lib/{checks → brakeman/checks}/check_nested_attributes.rb +10 -6
  32. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  33. data/lib/brakeman/checks/check_redirect.rb +177 -0
  34. data/lib/brakeman/checks/check_render.rb +62 -0
  35. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  36. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  37. data/lib/brakeman/checks/check_sanitize_methods.rb +54 -0
  38. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  39. data/lib/brakeman/checks/check_select_vulnerability.rb +58 -0
  40. data/lib/brakeman/checks/check_send.rb +35 -0
  41. data/lib/brakeman/checks/check_send_file.rb +19 -0
  42. data/lib/brakeman/checks/check_session_settings.rb +145 -0
  43. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  44. data/lib/brakeman/checks/check_skip_before_filter.rb +62 -0
  45. data/lib/brakeman/checks/check_sql.rb +577 -0
  46. data/lib/brakeman/checks/check_strip_tags.rb +64 -0
  47. data/lib/brakeman/checks/check_symbol_dos.rb +67 -0
  48. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  49. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  50. data/lib/brakeman/checks/check_validation_regex.rb +88 -0
  51. data/lib/brakeman/checks/check_without_protection.rb +64 -0
  52. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  53. data/lib/brakeman/differ.rb +66 -0
  54. data/lib/{format → brakeman/format}/style.css +28 -0
  55. data/lib/brakeman/options.rb +256 -0
  56. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  57. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  58. data/lib/{scanner_erubis.rb → brakeman/parsers/rails3_erubis.rb} +8 -21
  59. data/lib/brakeman/processor.rb +102 -0
  60. data/lib/brakeman/processors/alias_processor.rb +780 -0
  61. data/lib/{processors → brakeman/processors}/base_processor.rb +90 -74
  62. data/lib/brakeman/processors/config_processor.rb +14 -0
  63. data/lib/brakeman/processors/controller_alias_processor.rb +334 -0
  64. data/lib/brakeman/processors/controller_processor.rb +265 -0
  65. data/lib/{processors → brakeman/processors}/erb_template_processor.rb +21 -19
  66. data/lib/brakeman/processors/erubis_template_processor.rb +96 -0
  67. data/lib/brakeman/processors/gem_processor.rb +59 -0
  68. data/lib/{processors → brakeman/processors}/haml_template_processor.rb +26 -21
  69. data/lib/brakeman/processors/lib/find_all_calls.rb +185 -0
  70. data/lib/{processors → brakeman/processors}/lib/find_call.rb +23 -28
  71. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  72. data/lib/brakeman/processors/lib/processor_helper.rb +82 -0
  73. data/lib/{processors/config_processor.rb → brakeman/processors/lib/rails2_config_processor.rb} +32 -35
  74. data/lib/{processors → brakeman/processors}/lib/rails2_route_processor.rb +60 -52
  75. data/lib/brakeman/processors/lib/rails3_config_processor.rb +129 -0
  76. data/lib/brakeman/processors/lib/rails3_route_processor.rb +282 -0
  77. data/lib/{processors → brakeman/processors}/lib/render_helper.rb +54 -20
  78. data/lib/brakeman/processors/lib/route_helper.rb +62 -0
  79. data/lib/{processors → brakeman/processors}/library_processor.rb +24 -17
  80. data/lib/{processors → brakeman/processors}/model_processor.rb +46 -22
  81. data/lib/{processors → brakeman/processors}/output_processor.rb +34 -40
  82. data/lib/brakeman/processors/route_processor.rb +17 -0
  83. data/lib/brakeman/processors/slim_template_processor.rb +113 -0
  84. data/lib/brakeman/processors/template_alias_processor.rb +120 -0
  85. data/lib/{processors → brakeman/processors}/template_processor.rb +10 -7
  86. data/lib/brakeman/report.rb +68 -0
  87. data/lib/brakeman/report/ignore/config.rb +130 -0
  88. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  89. data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
  90. data/lib/brakeman/report/initializers/multi_json.rb +29 -0
  91. data/lib/brakeman/report/renderer.rb +24 -0
  92. data/lib/brakeman/report/report_base.rb +279 -0
  93. data/lib/brakeman/report/report_csv.rb +56 -0
  94. data/lib/brakeman/report/report_hash.rb +22 -0
  95. data/lib/brakeman/report/report_html.rb +203 -0
  96. data/lib/brakeman/report/report_json.rb +46 -0
  97. data/lib/brakeman/report/report_table.rb +109 -0
  98. data/lib/brakeman/report/report_tabs.rb +17 -0
  99. data/lib/brakeman/report/templates/controller_overview.html.erb +18 -0
  100. data/lib/brakeman/report/templates/controller_warnings.html.erb +17 -0
  101. data/lib/brakeman/report/templates/error_overview.html.erb +25 -0
  102. data/lib/brakeman/report/templates/header.html.erb +44 -0
  103. data/lib/brakeman/report/templates/ignored_warnings.html.erb +21 -0
  104. data/lib/brakeman/report/templates/model_warnings.html.erb +17 -0
  105. data/lib/brakeman/report/templates/overview.html.erb +34 -0
  106. data/lib/brakeman/report/templates/security_warnings.html.erb +19 -0
  107. data/lib/brakeman/report/templates/template_overview.html.erb +17 -0
  108. data/lib/brakeman/report/templates/view_warnings.html.erb +30 -0
  109. data/lib/brakeman/report/templates/warning_overview.html.erb +13 -0
  110. data/lib/brakeman/rescanner.rb +446 -0
  111. data/lib/brakeman/scanner.rb +362 -0
  112. data/lib/brakeman/tracker.rb +296 -0
  113. data/lib/brakeman/util.rb +413 -0
  114. data/lib/brakeman/version.rb +3 -0
  115. data/lib/brakeman/warning.rb +217 -0
  116. data/lib/brakeman/warning_codes.rb +68 -0
  117. data/lib/ruby_parser/bm_sexp.rb +562 -0
  118. data/lib/ruby_parser/bm_sexp_processor.rb +230 -0
  119. metadata +152 -66
  120. data/lib/checks.rb +0 -71
  121. data/lib/checks/base_check.rb +0 -357
  122. data/lib/checks/check_cross_site_scripting.rb +0 -336
  123. data/lib/checks/check_evaluation.rb +0 -27
  124. data/lib/checks/check_execute.rb +0 -110
  125. data/lib/checks/check_file_access.rb +0 -46
  126. data/lib/checks/check_forgery_setting.rb +0 -42
  127. data/lib/checks/check_mass_assignment.rb +0 -74
  128. data/lib/checks/check_model_attributes.rb +0 -36
  129. data/lib/checks/check_redirect.rb +0 -98
  130. data/lib/checks/check_render.rb +0 -65
  131. data/lib/checks/check_send_file.rb +0 -15
  132. data/lib/checks/check_session_settings.rb +0 -79
  133. data/lib/checks/check_sql.rb +0 -146
  134. data/lib/checks/check_validation_regex.rb +0 -60
  135. data/lib/processor.rb +0 -86
  136. data/lib/processors/alias_processor.rb +0 -384
  137. data/lib/processors/controller_alias_processor.rb +0 -237
  138. data/lib/processors/controller_processor.rb +0 -202
  139. data/lib/processors/erubis_template_processor.rb +0 -85
  140. data/lib/processors/lib/find_model_call.rb +0 -39
  141. data/lib/processors/lib/processor_helper.rb +0 -36
  142. data/lib/processors/lib/rails3_route_processor.rb +0 -184
  143. data/lib/processors/lib/route_helper.rb +0 -34
  144. data/lib/processors/params_processor.rb +0 -77
  145. data/lib/processors/route_processor.rb +0 -11
  146. data/lib/processors/template_alias_processor.rb +0 -86
  147. data/lib/report.rb +0 -680
  148. data/lib/scanner.rb +0 -227
  149. data/lib/tracker.rb +0 -144
  150. data/lib/util.rb +0 -141
  151. data/lib/version.rb +0 -1
  152. data/lib/warning.rb +0 -99
@@ -0,0 +1,18 @@
1
+ <h2>Controllers</h2>
2
+
3
+ <table>
4
+ <tr>
5
+ <th>Name</th>
6
+ <th>Parent</th>
7
+ <th>Includes</th>
8
+ <th>Routes</th>
9
+ </tr>
10
+ <% controller_rows.each do |row| %>
11
+ <tr>
12
+ <td><%= row['Name'] %></td>
13
+ <td><%= row['Parent'] %></td>
14
+ <td><%= row['Includes'] %></td>
15
+ <td><%= row['Routes'] %></td>
16
+ </tr>
17
+ <% end %>
18
+ </table>
@@ -0,0 +1,17 @@
1
+ <p>Controller Warnings</p>
2
+ <table>
3
+ <tr>
4
+ <th>Confidence</th>
5
+ <th>Controller</th>
6
+ <th>Warning Type</th>
7
+ <th>Message</th>
8
+ </tr>
9
+ <% warnings.each do |warning| %>
10
+ <tr>
11
+ <td><%= warning['Confidence']%></td>
12
+ <td><%= warning['Controller']%></td>
13
+ <td><%= warning['Warning Type']%></td>
14
+ <td><%= warning['Message']%></td>
15
+ </tr>
16
+ <% end %>
17
+ </table>
@@ -0,0 +1,25 @@
1
+ <div onClick="toggle('errors_table');"> <h2>Exceptions raised during the analysis (click to see them)</h2 ></div>
2
+ <div>
3
+ <div id='errors_table' style='display:none'>
4
+ <table>
5
+ <tr>
6
+ <th>Error</th>
7
+ <th>Location</th>
8
+ </tr>
9
+ <% tracker.errors.each do |warning| %>
10
+ <tr>
11
+ <td><%= CGI.escapeHTML warning[:error] %></td>
12
+ <td>
13
+ <% if tracker.options[:debug] %>
14
+ <% warning[:backtrace].each do |line| %>
15
+ <%= line %><br/>
16
+ <% end %>
17
+ <% else %>
18
+ <%= warning[:backtrace][0] %>
19
+ <% end %>
20
+ </td>
21
+ </tr>
22
+ <% end %>
23
+ </table>
24
+ </div>
25
+ </div>
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE HTML SYSTEM>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <title>Brakeman Report</title>
6
+ <script>
7
+ function toggle(context) {
8
+ var elem = document.getElementById(context);
9
+
10
+ if (elem.style.display != "block")
11
+ elem.style.display = "block";
12
+ else
13
+ elem.style.display = "none";
14
+
15
+ elem.parentNode.scrollIntoView();
16
+ }
17
+ </script>
18
+ <style>
19
+ <%= css %>
20
+ </style>
21
+ </head>
22
+ <body>
23
+
24
+ <h1>Brakeman Report</h1>
25
+ <table>
26
+ <tr>
27
+ <th>Application Path</th>
28
+ <th>Rails Version</th>
29
+ <th>Brakeman Version</th>
30
+ <th>Report Time</th>
31
+ <th>Checks Performed</th>
32
+ </tr>
33
+ <tr>
34
+ <td><%= File.expand_path tracker.options[:app_path] %></td>
35
+ <td><%= rails_version %></td>
36
+ <td><%= brakeman_version %>
37
+ <td>
38
+ <%= tracker.start_time %><br><br>
39
+ <%= tracker.duration %> seconds
40
+ </td>
41
+ <td><%= checks.checks_run.sort.join(", ") %></td>
42
+ </tr>
43
+ </table>
44
+ <br>
@@ -0,0 +1,21 @@
1
+ <div onClick="toggle('ignored_table');"> <h2><%= warnings.length %> Ignored Warnings (click to see them)</h2 ></div>
2
+ <div>
3
+ <table style="display:none" id="ignored_table">
4
+ <tr>
5
+ <th>Confidence</th>
6
+ <th>File</th>
7
+ <th>Warning Type</th>
8
+ <th>Message</th>
9
+ <th>Note</th>
10
+ </tr>
11
+ <% warnings.each do |warning| %>
12
+ <tr>
13
+ <td><%= warning['Confidence']%></td>
14
+ <td><%= warning['File']%></td>
15
+ <td><%= warning['Warning Type']%></td>
16
+ <td><%= warning['Message']%></td>
17
+ <td><%= warning['Note']%></td>
18
+ </tr>
19
+ <% end %>
20
+ </table>
21
+ </div>
@@ -0,0 +1,17 @@
1
+ <p>Model Warnings</p>
2
+ <table>
3
+ <tr>
4
+ <th>Confidence</th>
5
+ <th>Model</th>
6
+ <th>Warning Type</th>
7
+ <th>Message</th>
8
+ </tr>
9
+ <% warnings.each do |warning| %>
10
+ <tr>
11
+ <td><%= warning['Confidence']%></td>
12
+ <td><%= warning['Model']%></td>
13
+ <td><%= warning['Warning Type']%></td>
14
+ <td><%= warning['Message']%></td>
15
+ </tr>
16
+ <% end %>
17
+ </table>
@@ -0,0 +1,34 @@
1
+ <h2 id='summary'>Summary</h2>
2
+ <table>
3
+ <tr>
4
+ <th>Scanned/Reported</th>
5
+ <th>Total</th>
6
+ </tr>
7
+ <tr>
8
+ <td>Controllers</td>
9
+ <td><%= tracker.controllers.length %></td>
10
+ </tr>
11
+ <tr>
12
+ <td>Models</td>
13
+ <td><%= tracker.models.length - 1 %></td>
14
+ </tr>
15
+ <tr>
16
+ <td>Templates</td>
17
+ <td><%= number_of_templates %></td>
18
+ </tr>
19
+ <tr>
20
+ <td>Errors</td>
21
+ <td><%= tracker.errors.length %></td>
22
+ </tr>
23
+ <tr>
24
+ <td>Security Warnings</td>
25
+ <td><%= warnings %> <span class='high-confidence'>(<%= warnings_summary[:high_confidence] %>)</span></td>
26
+ </tr>
27
+ <% if warnings_summary['Ignored Warnings'] %>
28
+ <tr>
29
+ <td>Ignored Warnings</td>
30
+ <td><%= ignored_warnings %></td>
31
+ </tr>
32
+ <% end %>
33
+ </table>
34
+ <br>
@@ -0,0 +1,19 @@
1
+ <h2>Security Warnings</h2>
2
+ <table>
3
+ <tr>
4
+ <th>Confidence</th>
5
+ <th>Class</th>
6
+ <th>Method</th>
7
+ <th>Warning Type</th>
8
+ <th>Message</th>
9
+ </tr>
10
+ <% warnings.each do |warning| %>
11
+ <tr>
12
+ <td><%= warning['Confidence']%></td>
13
+ <td><%= warning['Class']%></td>
14
+ <td><%= warning['Method']%></td>
15
+ <td><%= warning['Warning Type']%></td>
16
+ <td><%= warning['Message']%></td>
17
+ </tr>
18
+ <% end %>
19
+ </table>
@@ -0,0 +1,17 @@
1
+ <h2>Templates</h2>
2
+
3
+ <% template_rows.each do |template| %>
4
+
5
+ <p><%= template[0] %></p>
6
+ <table>
7
+ <tr>
8
+ <th>Output</th>
9
+ </tr>
10
+ <% template[1].each do |call| %>
11
+ <tr>
12
+ <td><%= call %></td>
13
+ </tr>
14
+ <% end %>
15
+ </table>
16
+
17
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <p>View Warnings</p>
2
+ <table>
3
+ <tr>
4
+ <th>Confidence</th>
5
+ <th>Template</th>
6
+ <th>Warning Type</th>
7
+ <th>Message</th>
8
+ </tr>
9
+ <% warnings.each_with_index do |warning, i| %>
10
+ <tr>
11
+ <td><%= warning['Confidence']%></td>
12
+ <td>
13
+ <% if warning['Called From'] and warning['Called From'].length > 1 %>
14
+ <div class="template_name" onClick="toggle('callers<%= i %>')" >
15
+ <div>
16
+ <%= warning['Template'] %>
17
+ </div>
18
+ <div class="render_path" id="callers<%= i %>" >
19
+ <%= warning['Called From'].join(' &rarr; ') %> &rarr; <%= warning['Template Name'] %>
20
+ </div>
21
+ </div>
22
+ <% else %>
23
+ <%= warning['Template']%>
24
+ <% end %>
25
+ </td>
26
+ <td><%= warning['Warning Type']%></td>
27
+ <td><%= warning['Message']%></td>
28
+ </tr>
29
+ <% end %>
30
+ </table>
@@ -0,0 +1,13 @@
1
+ <table>
2
+ <tr>
3
+ <th>Warning Type</th>
4
+ <th>Total</th>
5
+ </tr>
6
+ <% types.sort.each do |warning_type| %>
7
+ <tr>
8
+ <td><%= warning_type %></td>
9
+ <td><%= warnings_summary[warning_type] %></td>
10
+ </tr>
11
+ <% end %>
12
+ </table>
13
+ <br>
@@ -0,0 +1,446 @@
1
+ require 'brakeman/scanner'
2
+ require 'terminal-table'
3
+ require 'brakeman/util'
4
+
5
+ #Class for rescanning changed files after an initial scan
6
+ class Brakeman::Rescanner < Brakeman::Scanner
7
+
8
+ SCAN_ORDER = [:config, :gemfile, :initializer, :lib, :routes, :template,
9
+ :model, :controller]
10
+
11
+ #Create new Rescanner to scan changed files
12
+ def initialize options, processor, changed_files
13
+ super(options, processor)
14
+
15
+ @paths = changed_files.map {|f| @app_tree.expand_path(f) }
16
+ @old_results = tracker.checks #Old warnings from previous scan
17
+ @changes = nil #True if files had to be rescanned
18
+ @reindex = Set.new
19
+ end
20
+
21
+ #Runs checks.
22
+ #Will rescan files if they have not already been scanned
23
+ def recheck
24
+ rescan if @changes.nil?
25
+
26
+ tracker.run_checks if @changes
27
+
28
+ Brakeman::RescanReport.new @old_results, tracker
29
+ end
30
+
31
+ #Rescans changed files
32
+ def rescan
33
+ tracker.template_cache.clear
34
+
35
+ paths_by_type = {}
36
+
37
+ SCAN_ORDER.each do |type|
38
+ paths_by_type[type] = []
39
+ end
40
+
41
+ @paths.each do |path|
42
+ type = file_type(path)
43
+ paths_by_type[type] << path unless type == :unknown
44
+ end
45
+
46
+ @changes = false
47
+
48
+ SCAN_ORDER.each do |type|
49
+ paths_by_type[type].each do |path|
50
+ Brakeman.debug "Rescanning #{path} as #{type}"
51
+
52
+ if rescan_file path, type
53
+ @changes = true
54
+ end
55
+ end
56
+ end
57
+
58
+ if @changes and not @reindex.empty?
59
+ tracker.reindex_call_sites @reindex
60
+ end
61
+
62
+ self
63
+ end
64
+
65
+ #Rescans a single file
66
+ def rescan_file path, type = nil
67
+ type ||= file_type path
68
+
69
+ unless @app_tree.path_exists?(path)
70
+ return rescan_deleted_file path, type
71
+ end
72
+
73
+ case type
74
+ when :controller
75
+ rescan_controller path
76
+ @reindex << :controllers << :templates
77
+ when :template
78
+ rescan_template path
79
+ @reindex << :templates
80
+ when :model
81
+ rescan_model path
82
+ when :lib
83
+ rescan_lib path
84
+ when :config
85
+ process_config
86
+ when :initializer
87
+ process_initializer path
88
+ when :routes
89
+ # Routes affect which controller methods are treated as actions
90
+ # which affects which templates are rendered, so routes, controllers,
91
+ # and templates rendered from controllers must be rescanned
92
+ tracker.reset_routes
93
+ tracker.reset_templates :only_rendered => true
94
+ process_routes
95
+ process_controllers
96
+ @reindex << :controllers << :templates
97
+ when :gemfile
98
+ if tracker.config[:gems][:rails_xss] and tracker.config[:escape_html]
99
+ tracker.config[:escape_html] = false
100
+ end
101
+
102
+ process_gems
103
+ else
104
+ return false #Nothing to do, file hopefully does not need to be rescanned
105
+ end
106
+
107
+ true
108
+ end
109
+
110
+ def rescan_controller path
111
+ #Process source
112
+ process_controller path
113
+
114
+ #Process data flow and template rendering
115
+ #from the controller
116
+ tracker.controllers.each do |name, controller|
117
+ if controller[:file] == path
118
+ tracker.templates.each do |template_name, template|
119
+ next unless template[:caller]
120
+ unless template[:caller].grep(/^#{name}#/).empty?
121
+ tracker.reset_template template_name
122
+ end
123
+ end
124
+
125
+ @processor.process_controller_alias controller[:name], controller[:src]
126
+ end
127
+ end
128
+ end
129
+
130
+ def rescan_template path
131
+ return unless path.match KNOWN_TEMPLATE_EXTENSIONS and @app_tree.path_exists?(path)
132
+
133
+ template_name = template_path_to_name(path)
134
+
135
+ tracker.reset_template template_name
136
+ process_template path
137
+
138
+ @processor.process_template_alias tracker.templates[template_name]
139
+
140
+ rescan = Set.new
141
+
142
+ template_matcher = /^Template:(.+)/
143
+ controller_matcher = /^(.+Controller)#(.+)/
144
+ template_name_matcher = /^#{template_name}\./
145
+
146
+ #Search for processed template and process it.
147
+ #Search for rendered versions of template and re-render (if necessary)
148
+ tracker.templates.each do |name, template|
149
+ if template[:file] == path or template[:file].nil?
150
+ next unless template[:caller] and name.to_s.match(template_name_matcher)
151
+
152
+ template[:caller].each do |from|
153
+ if from.match(template_matcher)
154
+ rescan << [:template, $1.to_sym]
155
+ elsif from.match(controller_matcher)
156
+ rescan << [:controller, $1.to_sym, $2.to_sym]
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ rescan.each do |r|
163
+ if r[0] == :controller
164
+ controller = tracker.controllers[r[1]]
165
+
166
+ unless @paths.include? controller[:file]
167
+ @processor.process_controller_alias controller[:name], controller[:src], r[2]
168
+ end
169
+ elsif r[0] == :template
170
+ template = tracker.templates[r[1]]
171
+
172
+ rescan_template template[:file]
173
+ end
174
+ end
175
+ end
176
+
177
+ def rescan_model path
178
+ num_models = tracker.models.length
179
+ tracker.reset_model path
180
+ process_model path if @app_tree.path_exists?(path)
181
+
182
+ #Only need to rescan other things if a model is added or removed
183
+ if num_models != tracker.models.length
184
+ process_templates
185
+ process_controllers
186
+ @reindex << :templates << :controllers
187
+ end
188
+
189
+ @reindex << :models
190
+ end
191
+
192
+ def rescan_lib path
193
+ process_lib path if @app_tree.path_exists?(path)
194
+
195
+ lib = nil
196
+
197
+ tracker.libs.each do |name, library|
198
+ if library[:file] == path
199
+ lib = library
200
+ break
201
+ end
202
+ end
203
+
204
+ rescan_mixin lib if lib
205
+ end
206
+
207
+ #Handle rescanning when a file is deleted
208
+ def rescan_deleted_file path, type
209
+ case type
210
+ when :controller
211
+ rescan_deleted_controller path
212
+ when :template
213
+ rescan_deleted_template path
214
+ when :model
215
+ rescan_model path
216
+ when :lib
217
+ rescan_deleted_lib path
218
+ when :initializer
219
+ rescan_deleted_initializer path
220
+ else
221
+ if remove_deleted_file path
222
+ return true
223
+ else
224
+ Brakeman.notify "Ignoring deleted file: #{path}"
225
+ end
226
+ end
227
+
228
+ true
229
+ end
230
+
231
+ def rescan_deleted_controller path
232
+ tracker.reset_controller path
233
+ end
234
+
235
+ def rescan_deleted_template path
236
+ return unless path.match KNOWN_TEMPLATE_EXTENSIONS
237
+
238
+ template_name = template_path_to_name(path)
239
+
240
+ #Remove template
241
+ tracker.reset_template template_name
242
+
243
+ rendered_from_controller = /^#{template_name}\.(.+Controller)#(.+)/
244
+ rendered_from_view = /^#{template_name}\.Template:(.+)/
245
+
246
+ #Remove any rendered versions, or partials rendered from it
247
+ tracker.templates.delete_if do |name, template|
248
+ if template[:file] == path
249
+ true
250
+ elsif template[:file].nil?
251
+ name = name.to_s
252
+
253
+ name.match(rendered_from_controller) or name.match(rendered_from_view)
254
+ end
255
+ end
256
+ end
257
+
258
+ def rescan_deleted_lib path
259
+ deleted_lib = nil
260
+
261
+ tracker.libs.delete_if do |name, lib|
262
+ if lib[:file] == path
263
+ deleted_lib = lib
264
+ true
265
+ end
266
+ end
267
+
268
+ rescan_mixin deleted_lib if deleted_lib
269
+ end
270
+
271
+ def rescan_deleted_initializer path
272
+ tracker.initializers.delete Pathname.new(path).basename.to_s
273
+ end
274
+
275
+ #Check controllers, templates, models and libs for data from file
276
+ #and delete it.
277
+ def remove_deleted_file path
278
+ deleted = false
279
+
280
+ [:controllers, :templates, :models, :libs].each do |collection|
281
+ tracker.send(collection).delete_if do |name, data|
282
+ if data[:file] == path
283
+ deleted = true
284
+ true
285
+ end
286
+ end
287
+ end
288
+
289
+ deleted
290
+ end
291
+
292
+ #Guess at what kind of file the path contains
293
+ def file_type path
294
+ case path
295
+ when /\/app\/controllers/
296
+ :controller
297
+ when /\/app\/views/
298
+ :template
299
+ when /\/app\/models/
300
+ :model
301
+ when /\/lib/
302
+ :lib
303
+ when /\/config\/initializers/
304
+ :initializer
305
+ when /config\/routes\.rb/
306
+ :routes
307
+ when /\/config\/.+\.rb/
308
+ :config
309
+ when /Gemfile/
310
+ :gemfile
311
+ else
312
+ :unknown
313
+ end
314
+ end
315
+
316
+ def rescan_mixin lib
317
+ method_names = []
318
+
319
+ [:public, :private, :protected].each do |access|
320
+ lib[access].each do |name, meth|
321
+ method_names << name
322
+ end
323
+ end
324
+
325
+ method_matcher = /##{method_names.map {|n| Regexp.escape(n.to_s)}.join('|')}$/
326
+
327
+ #Rescan controllers that mixed in library
328
+ tracker.controllers.each do |name, controller|
329
+ if controller[:includes].include? lib[:name]
330
+ unless @paths.include? controller[:file]
331
+ rescan_file controller[:file]
332
+ end
333
+ end
334
+ end
335
+
336
+ to_rescan = []
337
+
338
+ #Check if a method from this mixin was used to render a template.
339
+ #This is not precise, because a different controller might have the
340
+ #same method...
341
+ tracker.templates.each do |name, template|
342
+ next unless template[:caller]
343
+
344
+ unless template[:caller].grep(method_matcher).empty?
345
+ name.to_s.match /^([^.]+)/
346
+
347
+ original = tracker.templates[$1.to_sym]
348
+
349
+ if original
350
+ to_rescan << [name, original[:file]]
351
+ end
352
+ end
353
+ end
354
+
355
+ to_rescan.each do |template|
356
+ tracker.reset_template template[0]
357
+ rescan_file template[1]
358
+ end
359
+ end
360
+ end
361
+
362
+ #Class to make reporting of rescan results simpler to deal with
363
+ class Brakeman::RescanReport
364
+ include Brakeman::Util
365
+ attr_reader :old_results, :new_results
366
+
367
+ def initialize old_results, tracker
368
+ @tracker = tracker
369
+ @old_results = old_results
370
+ @new_results = tracker.checks
371
+ @all_warnings = nil
372
+ @diff = nil
373
+ end
374
+
375
+ #Returns true if any warnings were found (new or old)
376
+ def any_warnings?
377
+ not all_warnings.empty?
378
+ end
379
+
380
+ #Returns an array of all warnings found
381
+ def all_warnings
382
+ @all_warnings ||= new_results.all_warnings
383
+ end
384
+
385
+ #Returns an array of warnings which were in the old report but are not in the
386
+ #new report after rescanning
387
+ def fixed_warnings
388
+ diff[:fixed]
389
+ end
390
+
391
+ #Returns an array of warnings which were in the new report but were not in
392
+ #the old report
393
+ def new_warnings
394
+ diff[:new]
395
+ end
396
+
397
+ #Returns true if there are any new or fixed warnings
398
+ def warnings_changed?
399
+ not (diff[:new].empty? and diff[:fixed].empty?)
400
+ end
401
+
402
+ #Returns a hash of arrays for :new and :fixed warnings
403
+ def diff
404
+ @diff ||= @new_results.diff(@old_results)
405
+ end
406
+
407
+ #Returns an array of warnings which were in the old report and the new report
408
+ def existing_warnings
409
+ @old ||= all_warnings.select do |w|
410
+ not new_warnings.include? w
411
+ end
412
+ end
413
+
414
+ #Output total, fixed, and new warnings
415
+ def to_s(verbose = false)
416
+ if !verbose
417
+ <<-OUTPUT
418
+ Total warnings: #{all_warnings.length}
419
+ Fixed warnings: #{fixed_warnings.length}
420
+ New warnings: #{new_warnings.length}
421
+ OUTPUT
422
+ else
423
+ #Eventually move this to different method, or make default to_s
424
+ out = ""
425
+
426
+ {:fixed => fixed_warnings, :new => new_warnings, :existing => existing_warnings}.each do |warning_type, warnings|
427
+ if warnings.length > 0
428
+ out << "#{warning_type.to_s.titleize} warnings: #{warnings.length}\n"
429
+
430
+ table = Terminal::Table.new(:headings => ["Confidence", "Class", "Method", "Warning Type", "Message"]) do |t|
431
+ warnings.sort_by { |w| w.confidence}.each do |warning|
432
+ w = warning.to_row
433
+
434
+ w["Confidence"] = Brakeman::Report::TEXT_CONFIDENCE[w["Confidence"]]
435
+
436
+ t << [w["Confidence"], w["Class"], w["Method"], w["Warning Type"], w["Message"]]
437
+ end
438
+ end
439
+ out << truncate_table(table.to_s)
440
+ end
441
+ end
442
+
443
+ out
444
+ end
445
+ end
446
+ end