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.
- data/CHANGES +529 -0
- data/README.md +74 -28
- data/bin/brakeman +60 -266
- data/lib/brakeman.rb +422 -0
- data/lib/brakeman/app_tree.rb +101 -0
- data/lib/brakeman/brakeman.rake +10 -0
- data/lib/brakeman/call_index.rb +215 -0
- data/lib/brakeman/checks.rb +180 -0
- data/lib/brakeman/checks/base_check.rb +538 -0
- data/lib/brakeman/checks/check_basic_auth.rb +89 -0
- data/lib/brakeman/checks/check_content_tag.rb +162 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +334 -0
- data/lib/{checks → brakeman/checks}/check_default_routes.rb +13 -6
- data/lib/brakeman/checks/check_deserialize.rb +57 -0
- data/lib/brakeman/checks/check_digest_dos.rb +38 -0
- data/lib/brakeman/checks/check_escape_function.rb +21 -0
- data/lib/brakeman/checks/check_evaluation.rb +33 -0
- data/lib/brakeman/checks/check_execute.rb +98 -0
- data/lib/brakeman/checks/check_file_access.rb +62 -0
- data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
- data/lib/brakeman/checks/check_forgery_setting.rb +54 -0
- data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
- data/lib/brakeman/checks/check_json_parsing.rb +102 -0
- data/lib/brakeman/checks/check_link_to.rb +132 -0
- data/lib/brakeman/checks/check_link_to_href.rb +92 -0
- data/lib/{checks → brakeman/checks}/check_mail_to.rb +14 -13
- data/lib/brakeman/checks/check_mass_assignment.rb +143 -0
- data/lib/brakeman/checks/check_model_attr_accessible.rb +48 -0
- data/lib/brakeman/checks/check_model_attributes.rb +118 -0
- data/lib/brakeman/checks/check_model_serialize.rb +66 -0
- data/lib/{checks → brakeman/checks}/check_nested_attributes.rb +10 -6
- data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
- data/lib/brakeman/checks/check_redirect.rb +177 -0
- data/lib/brakeman/checks/check_render.rb +62 -0
- data/lib/brakeman/checks/check_response_splitting.rb +21 -0
- data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/brakeman/checks/check_sanitize_methods.rb +54 -0
- data/lib/brakeman/checks/check_select_tag.rb +60 -0
- data/lib/brakeman/checks/check_select_vulnerability.rb +58 -0
- data/lib/brakeman/checks/check_send.rb +35 -0
- data/lib/brakeman/checks/check_send_file.rb +19 -0
- data/lib/brakeman/checks/check_session_settings.rb +145 -0
- data/lib/brakeman/checks/check_single_quotes.rb +101 -0
- data/lib/brakeman/checks/check_skip_before_filter.rb +62 -0
- data/lib/brakeman/checks/check_sql.rb +577 -0
- data/lib/brakeman/checks/check_strip_tags.rb +64 -0
- data/lib/brakeman/checks/check_symbol_dos.rb +67 -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_validation_regex.rb +88 -0
- data/lib/brakeman/checks/check_without_protection.rb +64 -0
- data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
- data/lib/brakeman/differ.rb +66 -0
- data/lib/{format → brakeman/format}/style.css +28 -0
- data/lib/brakeman/options.rb +256 -0
- data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
- data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/{scanner_erubis.rb → brakeman/parsers/rails3_erubis.rb} +8 -21
- data/lib/brakeman/processor.rb +102 -0
- data/lib/brakeman/processors/alias_processor.rb +780 -0
- data/lib/{processors → brakeman/processors}/base_processor.rb +90 -74
- data/lib/brakeman/processors/config_processor.rb +14 -0
- data/lib/brakeman/processors/controller_alias_processor.rb +334 -0
- data/lib/brakeman/processors/controller_processor.rb +265 -0
- data/lib/{processors → brakeman/processors}/erb_template_processor.rb +21 -19
- data/lib/brakeman/processors/erubis_template_processor.rb +96 -0
- data/lib/brakeman/processors/gem_processor.rb +59 -0
- data/lib/{processors → brakeman/processors}/haml_template_processor.rb +26 -21
- data/lib/brakeman/processors/lib/find_all_calls.rb +185 -0
- data/lib/{processors → brakeman/processors}/lib/find_call.rb +23 -28
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +82 -0
- data/lib/{processors/config_processor.rb → brakeman/processors/lib/rails2_config_processor.rb} +32 -35
- data/lib/{processors → brakeman/processors}/lib/rails2_route_processor.rb +60 -52
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +129 -0
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +282 -0
- data/lib/{processors → brakeman/processors}/lib/render_helper.rb +54 -20
- data/lib/brakeman/processors/lib/route_helper.rb +62 -0
- data/lib/{processors → brakeman/processors}/library_processor.rb +24 -17
- data/lib/{processors → brakeman/processors}/model_processor.rb +46 -22
- data/lib/{processors → brakeman/processors}/output_processor.rb +34 -40
- data/lib/brakeman/processors/route_processor.rb +17 -0
- data/lib/brakeman/processors/slim_template_processor.rb +113 -0
- data/lib/brakeman/processors/template_alias_processor.rb +120 -0
- data/lib/{processors → brakeman/processors}/template_processor.rb +10 -7
- data/lib/brakeman/report.rb +68 -0
- data/lib/brakeman/report/ignore/config.rb +130 -0
- data/lib/brakeman/report/ignore/interactive.rb +311 -0
- data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
- data/lib/brakeman/report/initializers/multi_json.rb +29 -0
- data/lib/brakeman/report/renderer.rb +24 -0
- data/lib/brakeman/report/report_base.rb +279 -0
- data/lib/brakeman/report/report_csv.rb +56 -0
- data/lib/brakeman/report/report_hash.rb +22 -0
- data/lib/brakeman/report/report_html.rb +203 -0
- data/lib/brakeman/report/report_json.rb +46 -0
- data/lib/brakeman/report/report_table.rb +109 -0
- data/lib/brakeman/report/report_tabs.rb +17 -0
- data/lib/brakeman/report/templates/controller_overview.html.erb +18 -0
- data/lib/brakeman/report/templates/controller_warnings.html.erb +17 -0
- data/lib/brakeman/report/templates/error_overview.html.erb +25 -0
- data/lib/brakeman/report/templates/header.html.erb +44 -0
- data/lib/brakeman/report/templates/ignored_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/model_warnings.html.erb +17 -0
- data/lib/brakeman/report/templates/overview.html.erb +34 -0
- data/lib/brakeman/report/templates/security_warnings.html.erb +19 -0
- data/lib/brakeman/report/templates/template_overview.html.erb +17 -0
- data/lib/brakeman/report/templates/view_warnings.html.erb +30 -0
- data/lib/brakeman/report/templates/warning_overview.html.erb +13 -0
- data/lib/brakeman/rescanner.rb +446 -0
- data/lib/brakeman/scanner.rb +362 -0
- data/lib/brakeman/tracker.rb +296 -0
- data/lib/brakeman/util.rb +413 -0
- data/lib/brakeman/version.rb +3 -0
- data/lib/brakeman/warning.rb +217 -0
- data/lib/brakeman/warning_codes.rb +68 -0
- data/lib/ruby_parser/bm_sexp.rb +562 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +230 -0
- metadata +152 -66
- data/lib/checks.rb +0 -71
- data/lib/checks/base_check.rb +0 -357
- data/lib/checks/check_cross_site_scripting.rb +0 -336
- data/lib/checks/check_evaluation.rb +0 -27
- data/lib/checks/check_execute.rb +0 -110
- data/lib/checks/check_file_access.rb +0 -46
- data/lib/checks/check_forgery_setting.rb +0 -42
- data/lib/checks/check_mass_assignment.rb +0 -74
- data/lib/checks/check_model_attributes.rb +0 -36
- data/lib/checks/check_redirect.rb +0 -98
- data/lib/checks/check_render.rb +0 -65
- data/lib/checks/check_send_file.rb +0 -15
- data/lib/checks/check_session_settings.rb +0 -79
- data/lib/checks/check_sql.rb +0 -146
- data/lib/checks/check_validation_regex.rb +0 -60
- data/lib/processor.rb +0 -86
- data/lib/processors/alias_processor.rb +0 -384
- data/lib/processors/controller_alias_processor.rb +0 -237
- data/lib/processors/controller_processor.rb +0 -202
- data/lib/processors/erubis_template_processor.rb +0 -85
- data/lib/processors/lib/find_model_call.rb +0 -39
- data/lib/processors/lib/processor_helper.rb +0 -36
- data/lib/processors/lib/rails3_route_processor.rb +0 -184
- data/lib/processors/lib/route_helper.rb +0 -34
- data/lib/processors/params_processor.rb +0 -77
- data/lib/processors/route_processor.rb +0 -11
- data/lib/processors/template_alias_processor.rb +0 -86
- data/lib/report.rb +0 -680
- data/lib/scanner.rb +0 -227
- data/lib/tracker.rb +0 -144
- data/lib/util.rb +0 -141
- data/lib/version.rb +0 -1
- 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(' → ') %> → <%= 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,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
|