brakeman 1.5.3 → 1.6.0.pre1
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.
- data/bin/brakeman +20 -6
- data/lib/brakeman.rb +21 -1
- data/lib/brakeman/checks.rb +2 -7
- data/lib/brakeman/checks/base_check.rb +31 -26
- data/lib/brakeman/checks/check_basic_auth.rb +1 -7
- data/lib/brakeman/checks/check_cross_site_scripting.rb +38 -33
- data/lib/brakeman/checks/check_evaluation.rb +2 -1
- data/lib/brakeman/checks/check_execute.rb +5 -1
- data/lib/brakeman/checks/check_file_access.rb +6 -4
- data/lib/brakeman/checks/check_link_to.rb +8 -5
- data/lib/brakeman/checks/check_link_to_href.rb +6 -5
- data/lib/brakeman/checks/check_mail_to.rb +2 -4
- data/lib/brakeman/checks/check_mass_assignment.rb +12 -6
- data/lib/brakeman/checks/check_redirect.rb +17 -14
- data/lib/brakeman/checks/check_render.rb +4 -4
- data/lib/brakeman/checks/check_send.rb +4 -2
- data/lib/brakeman/checks/check_session_settings.rb +16 -21
- data/lib/brakeman/checks/check_skip_before_filter.rb +2 -4
- data/lib/brakeman/checks/check_sql.rb +8 -7
- data/lib/brakeman/checks/check_validation_regex.rb +2 -4
- data/lib/brakeman/checks/check_without_protection.rb +8 -9
- data/lib/brakeman/differ.rb +61 -0
- data/lib/brakeman/format/style.css +4 -0
- data/lib/brakeman/options.rb +8 -0
- data/lib/brakeman/processors/alias_processor.rb +5 -7
- data/lib/brakeman/processors/controller_alias_processor.rb +10 -3
- data/lib/brakeman/processors/erb_template_processor.rb +2 -0
- data/lib/brakeman/processors/erubis_template_processor.rb +2 -0
- data/lib/brakeman/processors/gem_processor.rb +8 -0
- data/lib/brakeman/processors/haml_template_processor.rb +2 -0
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +2 -6
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -4
- data/lib/brakeman/processors/lib/render_helper.rb +3 -1
- data/lib/brakeman/processors/library_processor.rb +4 -6
- data/lib/brakeman/processors/template_alias_processor.rb +1 -1
- data/lib/brakeman/report.rb +257 -198
- data/lib/brakeman/rescanner.rb +112 -10
- data/lib/brakeman/scanner.rb +3 -4
- data/lib/brakeman/templates/controller_overview.html.erb +18 -0
- data/lib/brakeman/templates/controller_warnings.html.erb +17 -0
- data/lib/brakeman/templates/error_overview.html.erb +14 -0
- data/lib/brakeman/templates/header.html.erb +38 -0
- data/lib/brakeman/templates/model_warnings.html.erb +17 -0
- data/lib/brakeman/templates/overview.html.erb +28 -0
- data/lib/brakeman/templates/security_warnings.html.erb +28 -0
- data/lib/brakeman/templates/template_overview.html.erb +17 -0
- data/lib/brakeman/templates/view_warnings.html.erb +17 -0
- data/lib/brakeman/templates/warning_overview.html.erb +13 -0
- data/lib/brakeman/tracker.rb +1 -1
- data/lib/brakeman/util.rb +24 -4
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +11 -3
- data/lib/ruby_parser/bm_sexp.rb +5 -11
- metadata +84 -23
data/lib/brakeman/rescanner.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'brakeman/scanner'
|
2
|
+
require 'terminal-table'
|
3
|
+
require 'brakeman/util'
|
2
4
|
|
3
5
|
#Class for rescanning changed files after an initial scan
|
4
6
|
class Brakeman::Rescanner < Brakeman::Scanner
|
@@ -64,6 +66,10 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
64
66
|
def rescan_file path, type = nil
|
65
67
|
type ||= file_type path
|
66
68
|
|
69
|
+
unless File.exist? path
|
70
|
+
return rescan_deleted_file path, type
|
71
|
+
end
|
72
|
+
|
67
73
|
case type
|
68
74
|
when :controller
|
69
75
|
rescan_controller path
|
@@ -89,6 +95,10 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
89
95
|
process_controllers
|
90
96
|
@reindex << :controllers << :templates
|
91
97
|
when :gemfile
|
98
|
+
if tracker.config[:gems][:rails_xss] and tracker.config[:escape_html]
|
99
|
+
tracker.config[:escape_html] = false
|
100
|
+
end
|
101
|
+
|
92
102
|
process_gems
|
93
103
|
else
|
94
104
|
return false #Nothing to do, file hopefully does not need to be rescanned
|
@@ -111,7 +121,7 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
111
121
|
end
|
112
122
|
end
|
113
123
|
|
114
|
-
@processor.process_controller_alias controller[:src]
|
124
|
+
@processor.process_controller_alias controller[:name], controller[:src]
|
115
125
|
end
|
116
126
|
end
|
117
127
|
end
|
@@ -154,7 +164,7 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
154
164
|
controller = tracker.controllers[r[1]]
|
155
165
|
|
156
166
|
unless @paths.include? controller[:file]
|
157
|
-
@processor.process_controller_alias controller[:src], r[2]
|
167
|
+
@processor.process_controller_alias controller[:name], controller[:src], r[2]
|
158
168
|
end
|
159
169
|
elsif r[0] == :template
|
160
170
|
template = tracker.templates[r[1]]
|
@@ -179,6 +189,98 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
179
189
|
@reindex << :models
|
180
190
|
end
|
181
191
|
|
192
|
+
#Handle rescanning when a file is deleted
|
193
|
+
def rescan_deleted_file path, type
|
194
|
+
case type
|
195
|
+
when :controller
|
196
|
+
rescan_deleted_controller path
|
197
|
+
when :template
|
198
|
+
rescan_deleted_template path
|
199
|
+
when :model
|
200
|
+
rescan_model path
|
201
|
+
when :lib
|
202
|
+
rescan_deleted_lib path
|
203
|
+
when :initializer
|
204
|
+
rescan_deleted_initializer path
|
205
|
+
else
|
206
|
+
if remove_deleted_file path
|
207
|
+
return true
|
208
|
+
else
|
209
|
+
Brakeman.notify "Ignoring deleted file: #{path}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
true
|
214
|
+
end
|
215
|
+
|
216
|
+
def rescan_deleted_controller path
|
217
|
+
#Remove from controller
|
218
|
+
tracker.controllers.delete_if do |name, controller|
|
219
|
+
if controller[:file] == path
|
220
|
+
template_matcher = /(.+)\.#{name}#/
|
221
|
+
|
222
|
+
#Remove templates rendered from this controller
|
223
|
+
tracker.templates.keys.each do |template_name|
|
224
|
+
if template_name.to_s.match template_matcher
|
225
|
+
tracker.templates.delete template_name
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
true
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def rescan_deleted_template path
|
235
|
+
return unless path.match KNOWN_TEMPLATE_EXTENSIONS
|
236
|
+
|
237
|
+
template_name = template_path_to_name(path)
|
238
|
+
|
239
|
+
#Remove template
|
240
|
+
tracker.reset_template template_name
|
241
|
+
|
242
|
+
rendered_from_controller = /^#{template_name}\.(.+Controller)#(.+)/
|
243
|
+
rendered_from_view = /^#{template_name}\.Template:(.+)/
|
244
|
+
|
245
|
+
#Remove any rendered versions, or partials rendered from it
|
246
|
+
tracker.templates.delete_if do |name, template|
|
247
|
+
if template[:file] == path
|
248
|
+
true
|
249
|
+
elsif template[:file].nil?
|
250
|
+
name = name.to_s
|
251
|
+
|
252
|
+
name.match(rendered_from_controller) or name.match(rendered_from_view)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def rescan_deleted_lib path
|
258
|
+
tracker.libs.delete_if do |name, lib|
|
259
|
+
lib[:path] == path
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def rescan_deleted_initializer path
|
264
|
+
tracker.initializers.delete Pathname.new(path).basename.to_s
|
265
|
+
end
|
266
|
+
|
267
|
+
#Check controllers, templates, models and libs for data from file
|
268
|
+
#and delete it.
|
269
|
+
def remove_deleted_file path
|
270
|
+
deleted = false
|
271
|
+
|
272
|
+
[:controllers, :templates, :models, :libs].each do |collection|
|
273
|
+
tracker.send(collection).delete_if do |name, data|
|
274
|
+
if data[:file] == path
|
275
|
+
deleted = true
|
276
|
+
true
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
deleted
|
282
|
+
end
|
283
|
+
|
182
284
|
#Guess at what kind of file the path contains
|
183
285
|
def file_type path
|
184
286
|
case path
|
@@ -206,6 +308,7 @@ end
|
|
206
308
|
|
207
309
|
#Class to make reporting of rescan results simpler to deal with
|
208
310
|
class Brakeman::RescanReport
|
311
|
+
include Brakeman::Util
|
209
312
|
attr_reader :old_results, :new_results
|
210
313
|
|
211
314
|
def initialize old_results, tracker
|
@@ -271,17 +374,16 @@ New warnings: #{new_warnings.length}
|
|
271
374
|
if warnings.length > 0
|
272
375
|
out << "#{warning_type.to_s.titleize} warnings: #{warnings.length}\n"
|
273
376
|
|
274
|
-
table =
|
275
|
-
|
276
|
-
|
277
|
-
w = warning.to_row
|
377
|
+
table = Terminal::Table.new(:headings => ["Confidence", "Class", "Method", "Warning Type", "Message"]) do |t|
|
378
|
+
warnings.sort_by { |w| w.confidence}.each do |warning|
|
379
|
+
w = warning.to_row
|
278
380
|
|
279
|
-
|
381
|
+
w["Confidence"] = Brakeman::Report::TEXT_CONFIDENCE[w["Confidence"]]
|
280
382
|
|
281
|
-
|
383
|
+
t << [w["Confidence"], w["Class"], w["Method"], w["Warning Type"], w["Message"]]
|
384
|
+
end
|
282
385
|
end
|
283
|
-
|
284
|
-
out << table.to_s
|
386
|
+
out << truncate_table(table.to_s)
|
285
387
|
end
|
286
388
|
end
|
287
389
|
|
data/lib/brakeman/scanner.rb
CHANGED
@@ -92,8 +92,7 @@ class Brakeman::Scanner
|
|
92
92
|
end
|
93
93
|
|
94
94
|
if File.exists? "#@path/vendor/plugins/rails_xss" or
|
95
|
-
options[:rails3] or options[:escape_html]
|
96
|
-
(File.exists? "#@path/Gemfile" and File.read("#@path/Gemfile").include? "rails_xss")
|
95
|
+
options[:rails3] or options[:escape_html]
|
97
96
|
|
98
97
|
tracker.config[:escape_html] = true
|
99
98
|
Brakeman.notify "[Notice] Escaping HTML by default"
|
@@ -228,7 +227,7 @@ class Brakeman::Scanner
|
|
228
227
|
|
229
228
|
Brakeman.notify "Processing data flow in controllers..."
|
230
229
|
|
231
|
-
tracker.controllers.each do |name, controller|
|
230
|
+
tracker.controllers.sort_by{|name| name.to_s}.each do |name, controller|
|
232
231
|
Brakeman.debug "Processing #{name}"
|
233
232
|
if @report_progress
|
234
233
|
$stderr.print " #{current}/#{total} controllers processed\r"
|
@@ -281,7 +280,7 @@ class Brakeman::Scanner
|
|
281
280
|
|
282
281
|
Brakeman.notify "Processing data flow in templates..."
|
283
282
|
|
284
|
-
tracker.templates.keys.dup.each do |name|
|
283
|
+
tracker.templates.keys.dup.sort_by{|name| name.to_s}.each do |name|
|
285
284
|
Brakeman.debug "Processing #{name}"
|
286
285
|
if @report_progress
|
287
286
|
count += 1
|
@@ -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,14 @@
|
|
1
|
+
<div onClick="toggle('errors_table');"> <h2>Exceptions raised during the analysis (click to see them)</h2 ></div> <div id='errors_table' style='display:none'>
|
2
|
+
<table>
|
3
|
+
<tr>
|
4
|
+
<th>Error</th>
|
5
|
+
<th>Location</th>
|
6
|
+
</tr>
|
7
|
+
<% tracker.errors.each do |warning| %>
|
8
|
+
<tr>
|
9
|
+
<td><%= CGI.escapeHTML warning[:error] %></td>
|
10
|
+
<td><%= warning[:backtrace][0] %></td>
|
11
|
+
</tr>
|
12
|
+
<% end %>
|
13
|
+
</table>
|
14
|
+
</div>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<!DOCTYPE HTML SYSTEM>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Brakeman Report</title>
|
5
|
+
<script>
|
6
|
+
function toggle(context) {
|
7
|
+
var elem = document.getElementById(context);
|
8
|
+
|
9
|
+
if (elem.style.display != "block")
|
10
|
+
elem.style.display = "block";
|
11
|
+
else
|
12
|
+
elem.style.display = "none";
|
13
|
+
|
14
|
+
elem.parentNode.scrollIntoView();
|
15
|
+
}
|
16
|
+
</script>
|
17
|
+
<style>
|
18
|
+
<%= css %>
|
19
|
+
</style>
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
|
23
|
+
<h1>Brakeman Report</h1>
|
24
|
+
<table>
|
25
|
+
<tr>
|
26
|
+
<th>Application Path</th>
|
27
|
+
<th>Rails Version</th>
|
28
|
+
<th>Report Generation Time</th>
|
29
|
+
<th>Checks Performed</th>
|
30
|
+
</tr>
|
31
|
+
<tr>
|
32
|
+
<td><%= File.expand_path tracker.options[:app_path] %></td>
|
33
|
+
<td><%= rails_version %></td>
|
34
|
+
<td><%= Time.now %></td>
|
35
|
+
<td><%= checks.checks_run.sort.join(", ") %></td>
|
36
|
+
</tr>
|
37
|
+
</table>
|
38
|
+
<br>
|
@@ -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,28 @@
|
|
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(@tracker) %></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
|
+
</table>
|
28
|
+
<br>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<h2>Security Warnings</h2>
|
2
|
+
<table>
|
3
|
+
<% if warning_messages.any? %>
|
4
|
+
<tr>
|
5
|
+
<th>Confidence</th>
|
6
|
+
<th>Class</th>
|
7
|
+
<th>Method</th>
|
8
|
+
<th>Warning Type</th>
|
9
|
+
<th>Message</th>
|
10
|
+
</tr>
|
11
|
+
<% warning_messages.each do |warning| %>
|
12
|
+
<tr>
|
13
|
+
<td><%= warning['Confidence']%></td>
|
14
|
+
<td><%= warning['Class']%></td>
|
15
|
+
<td><%= warning['Method']%></td>
|
16
|
+
<td><%= warning['Warning Type']%></td>
|
17
|
+
<td><%= warning['Message']%></td>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
<% else %>
|
21
|
+
<tr>
|
22
|
+
<th>General Warnings</th>
|
23
|
+
</tr>
|
24
|
+
<tr>
|
25
|
+
<td>[NONE]</td>
|
26
|
+
</tr>
|
27
|
+
<% end %>
|
28
|
+
</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,17 @@
|
|
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 do |warning| %>
|
10
|
+
<tr>
|
11
|
+
<td><%= warning['Confidence']%></td>
|
12
|
+
<td><%= warning['Template']%></td>
|
13
|
+
<td><%= warning['Warning Type']%></td>
|
14
|
+
<td><%= warning['Message']%></td>
|
15
|
+
</tr>
|
16
|
+
<% end %>
|
17
|
+
</table>
|
data/lib/brakeman/tracker.rb
CHANGED
@@ -85,7 +85,7 @@ class Brakeman::Tracker
|
|
85
85
|
#Prioritizes templates which have been rendered.
|
86
86
|
def each_template
|
87
87
|
if @processed.nil?
|
88
|
-
@processed, @rest = templates.keys.partition { |k| k.to_s.include? "." }
|
88
|
+
@processed, @rest = templates.keys.sort_by{|template| template.to_s}.partition { |k| k.to_s.include? "." }
|
89
89
|
end
|
90
90
|
|
91
91
|
@processed.each do |k|
|
data/lib/brakeman/util.rb
CHANGED
@@ -88,10 +88,8 @@ module Brakeman::Util
|
|
88
88
|
key = Sexp.new(:lit, key)
|
89
89
|
end
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
return v
|
94
|
-
end
|
91
|
+
if index = hash.find_index(key) and index > 0
|
92
|
+
return hash[index + 1]
|
95
93
|
end
|
96
94
|
|
97
95
|
nil
|
@@ -326,4 +324,26 @@ module Brakeman::Util
|
|
326
324
|
|
327
325
|
context
|
328
326
|
end
|
327
|
+
|
328
|
+
def truncate_table str
|
329
|
+
@terminal_width ||= ::HighLine::SystemExtensions::terminal_size[0]
|
330
|
+
lines = str.lines
|
331
|
+
|
332
|
+
lines.map do |line|
|
333
|
+
if line.chomp.length > @terminal_width
|
334
|
+
line[0..(@terminal_width - 3)] + ">>"
|
335
|
+
else
|
336
|
+
line
|
337
|
+
end
|
338
|
+
end.join
|
339
|
+
end
|
340
|
+
|
341
|
+
# rely on Terminal::Table to build the structure, extract the data out in CSV format
|
342
|
+
def table_to_csv table
|
343
|
+
output = CSV.generate_line(table.headings.cells.map{|cell| cell.to_s.strip})
|
344
|
+
table.rows.each do |row|
|
345
|
+
output << CSV.generate_line(row.cells.map{|cell| cell.to_s.strip})
|
346
|
+
end
|
347
|
+
output
|
348
|
+
end
|
329
349
|
end
|