brakeman 1.9.5 → 2.0.0.pre2

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +27 -0
  3. data/README.md +5 -2
  4. data/bin/brakeman +20 -15
  5. data/lib/brakeman.rb +106 -80
  6. data/lib/brakeman/app_tree.rb +22 -11
  7. data/lib/brakeman/call_index.rb +4 -4
  8. data/lib/brakeman/checks/base_check.rb +33 -5
  9. data/lib/brakeman/checks/check_basic_auth.rb +2 -1
  10. data/lib/brakeman/checks/check_content_tag.rb +8 -29
  11. data/lib/brakeman/checks/check_cross_site_scripting.rb +10 -19
  12. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  13. data/lib/brakeman/checks/check_execute.rb +3 -11
  14. data/lib/brakeman/checks/check_file_access.rb +1 -14
  15. data/lib/brakeman/checks/check_forgery_setting.rb +5 -4
  16. data/lib/brakeman/checks/check_link_to.rb +4 -15
  17. data/lib/brakeman/checks/check_link_to_href.rb +1 -8
  18. data/lib/brakeman/checks/check_mass_assignment.rb +6 -2
  19. data/lib/brakeman/checks/check_model_attributes.rb +1 -0
  20. data/lib/brakeman/checks/check_model_serialize.rb +2 -1
  21. data/lib/brakeman/checks/check_render.rb +2 -15
  22. data/lib/brakeman/checks/check_select_tag.rb +1 -1
  23. data/lib/brakeman/checks/check_select_vulnerability.rb +2 -2
  24. data/lib/brakeman/checks/check_send.rb +3 -0
  25. data/lib/brakeman/checks/check_session_settings.rb +2 -3
  26. data/lib/brakeman/checks/check_skip_before_filter.rb +5 -2
  27. data/lib/brakeman/checks/check_sql.rb +59 -50
  28. data/lib/brakeman/checks/check_symbol_dos.rb +5 -14
  29. data/lib/brakeman/checks/check_unsafe_reflection.rb +2 -15
  30. data/lib/brakeman/options.rb +14 -9
  31. data/lib/brakeman/processors/controller_alias_processor.rb +4 -10
  32. data/lib/brakeman/processors/controller_processor.rb +53 -14
  33. data/lib/brakeman/processors/lib/find_all_calls.rb +40 -40
  34. data/lib/brakeman/processors/lib/processor_helper.rb +5 -1
  35. data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -0
  36. data/lib/brakeman/processors/output_processor.rb +23 -1
  37. data/lib/brakeman/report.rb +28 -14
  38. data/lib/brakeman/scanner.rb +5 -3
  39. data/lib/brakeman/tracker.rb +7 -7
  40. data/lib/brakeman/util.rb +5 -3
  41. data/lib/brakeman/version.rb +1 -1
  42. data/lib/brakeman/warning.rb +12 -9
  43. data/lib/ruby_parser/bm_sexp.rb +5 -1
  44. data/lib/tasks/brakeman.rake +10 -0
  45. metadata +11 -9
  46. data/lib/brakeman/checks/check_yaml_load.rb +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA512:
3
- metadata.gz: 5fec83c0cb268acf3954ea08df30a173ad10b3121d4d30d18c1670de66cf17d01fde3d0649c28f5c5570938d9d4a13368af819f4d580800441c1cdf4b4873cd2
4
- data.tar.gz: ff597aced590649b3a90cf2f895407f9e347a8a81b1f34337e2eff32310958c34bbe711985b70ce58f6f91eb658bc8d6db533819b881fb4d3ec9fa3842cb751e
3
+ data.tar.gz: 0fa96dca9b41558e7db8c55a2ec2a2e4dc95fc79afba243d0e656fe0c120fbe5b8c71c6c2216a4d27036efc5a57db9cd9661d2a2952e6b92f4c98777a4e26173
4
+ metadata.gz: 6ed79f61f202f946c7c417f2945ae57ebba267a4e4f46e45eb07fd0eddebe947046d907ff18775bb942073ec3ea00a2a8a9ccaf9b1b31e66d2afa8d62a95166b
5
5
  SHA1:
6
- metadata.gz: b4735612d0032bfd8a72f0feb285ee4e704a3a1a
7
- data.tar.gz: 1898b18fbf38409a98ba3b5517f3896b057872c9
6
+ data.tar.gz: ca68abc07bbc2bafc9af02bcabab84d4756447b7
7
+ metadata.gz: fad7234ee850e69edbe9bacf2783605edbcf7b64
data/CHANGES CHANGED
@@ -1,3 +1,30 @@
1
+ # 2.0.0
2
+
3
+ * Add `--only-files` option to specify files/paths to scan (Ian Ehlert)
4
+ * Add Marshal/CSV deserialization check
5
+ * Combine deserialization checks into single check
6
+ * Avoid duplicate "Dangerous Send" and "Unsafe Reflection" warnings
7
+ * Avoid duplicate results for Symbol DoS check
8
+ * Medium confidence for mass assignment to attr_protected models
9
+ * Remove "timestamp" key from JSON reports
10
+ * Remove deprecated config file locations
11
+ * Only treat classes with names containing `Controller` like controllers
12
+ * Better handling of classes nested inside controllers
13
+ * Better handling of controller classes nested in classes/modules
14
+ * Handle `->` lambdas with no arguments
15
+ * Handle explicit block argument destructuring
16
+ * Skip Rails config options that are real objects
17
+ * Detect Rails 3 JSON escape config option
18
+ * Much better tracking of warning file names
19
+ * Fix errors when using `--separate-models` (Noah Davis)
20
+ * Fix fingerprint generation to actually use the file path
21
+ * Fix text report console output in JRuby
22
+ * Fix false positives on `Model#id`
23
+ * Fix false positives on `params.to_json`
24
+ * Fix model path guesses to use "models/" instead of "controllers/"
25
+ * Clean up SQL CVE warning messages
26
+ * Use exceptions instead of abort in brakeman lib
27
+
1
28
  # 1.9.5
2
29
 
3
30
  * Add check for unsafe symbol creation
data/README.md CHANGED
@@ -1,13 +1,16 @@
1
1
  ![Brakeman Logo](http://brakemanscanner.org/images/logo_medium.png)
2
2
 
3
- [![Travis CI Status](https://secure.travis-ci.org/presidentbeef/brakeman.png)](https://travis-ci.org/presidentbeef/brakeman) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/presidentbeef/brakeman)
3
+ [![Travis CI
4
+ Status](https://secure.travis-ci.org/presidentbeef/brakeman.png)](https://travis-ci.org/presidentbeef/brakeman)
5
+ [![Code
6
+ Climate](https://codeclimate.com/github/presidentbeef/brakeman.png)](https://codeclimate.com/github/presidentbeef/brakeman)
4
7
 
5
8
  # Brakeman
6
9
 
7
10
  Brakeman is a static analysis tool which checks Ruby on Rails applications for security vulnerabilities.
8
11
 
9
12
  It targets Rails versions 2.x and 3.x.
10
-
13
+
11
14
  There is also a [plugin available](http://brakemanscanner.org/docs/jenkins/) for Jenkins/Hudson.
12
15
 
13
16
  For even more continuous testing, try the [Guard plugin](https://github.com/oreoshake/guard-brakeman).
data/bin/brakeman CHANGED
@@ -52,22 +52,27 @@ trap("INT") do
52
52
  exit!
53
53
  end
54
54
 
55
- if options[:previous_results_json]
56
- vulns = Brakeman.compare options.merge(:quiet => options[:quiet])
57
- puts MultiJson.dump(vulns, :pretty => true)
58
-
59
- if options[:exit_on_warn] and (vulns[:new].count + vulns[:fixed].count > 0)
60
- exit Brakeman::Warnings_Found_Exit_Code
61
- end
62
- else
63
- #Run scan and output a report
64
- tracker = Brakeman.run options.merge(:print_report => true, :quiet => options[:quiet])
65
-
66
- #Return error code if --exit-on-warn is used and warnings were found
67
- if options[:exit_on_warn] and not tracker.checks.all_warnings.empty?
68
- exit Brakeman::Warnings_Found_Exit_Code
69
- end
55
+ if options[:quiet].nil?
56
+ options[:quiet] = :command_line
70
57
  end
71
58
 
59
+ begin
60
+ if options[:previous_results_json]
61
+ vulns = Brakeman.compare options.merge(:quiet => options[:quiet])
62
+ puts MultiJson.dump(vulns, :pretty => true)
72
63
 
64
+ if options[:exit_on_warn] and (vulns[:new].count + vulns[:fixed].count > 0)
65
+ exit Brakeman::Warnings_Found_Exit_Code
66
+ end
67
+ else
68
+ #Run scan and output a report
69
+ tracker = Brakeman.run options.merge(:print_report => true, :quiet => options[:quiet])
73
70
 
71
+ #Return error code if --exit-on-warn is used and warnings were found
72
+ if options[:exit_on_warn] and not tracker.checks.all_warnings.empty?
73
+ exit Brakeman::Warnings_Found_Exit_Code
74
+ end
75
+ end
76
+ rescue Brakeman::Scanner::NoApplication => e
77
+ $stderr.puts e.message
78
+ end
data/lib/brakeman.rb CHANGED
@@ -40,7 +40,7 @@ module Brakeman
40
40
  # * :safe_methods - array of methods to consider safe
41
41
  # * :skip_libs - do not process lib/ directory (default: false)
42
42
  # * :skip_checks - checks not to run (run all if not specified)
43
- # * :relative_path - show relative path of each file(default: false)
43
+ # * :absolute_paths - show absolute path of each file (default: false)
44
44
  # * :summary_only - only output summary section of report
45
45
  # (does not apply to tabs format)
46
46
  #
@@ -63,56 +63,51 @@ module Brakeman
63
63
  options = { :app_path => options }
64
64
  end
65
65
 
66
- options[:app_path] = File.expand_path(options[:app_path])
67
-
68
- file_options = load_options(options[:config_file])
66
+ if options[:quiet] == :command_line
67
+ command_line = true
68
+ options.delete :quiet
69
+ end
69
70
 
70
- options = file_options.merge options
71
+ options = default_options.merge(load_options(options[:config_file], options[:quiet])).merge(options)
71
72
 
72
- options[:quiet] = true if options[:quiet].nil? && file_options[:quiet]
73
+ if options[:quiet].nil? and not command_line
74
+ options[:quiet] = true
75
+ end
73
76
 
74
- options = get_defaults.merge! options
77
+ options[:app_path] = File.expand_path(options[:app_path])
75
78
  options[:output_formats] = get_output_formats options
76
79
 
77
80
  options
78
81
  end
79
82
 
80
- DEPRECATED_CONFIG_FILES = [
81
- File.expand_path("./config.yaml"),
82
- File.expand_path("~/.brakeman/config.yaml"),
83
- File.expand_path("/etc/brakeman/config.yaml"),
84
- "#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"
85
- ]
86
-
87
83
  CONFIG_FILES = [
88
84
  File.expand_path("./config/brakeman.yml"),
89
85
  File.expand_path("~/.brakeman/config.yml"),
90
- File.expand_path("/etc/brakeman/config.yml"),
86
+ File.expand_path("/etc/brakeman/config.yml")
91
87
  ]
92
88
 
93
89
  #Load options from YAML file
94
- def self.load_options custom_location
90
+ def self.load_options custom_location, quiet
95
91
  #Load configuration file
96
92
  if config = config_file(custom_location)
97
93
  options = YAML.load_file config
98
94
  options.each { |k, v| options[k] = Set.new v if v.is_a? Array }
99
- notify "[Notice] Using configuration in #{config}" unless options[:quiet]
95
+
96
+ # notify if options[:quiet] and quiet is nil||false
97
+ notify "[Notice] Using configuration in #{config}" unless (options[:quiet] || quiet)
100
98
  options
101
99
  else
102
100
  {}
103
101
  end
104
102
  end
105
103
 
106
- def self.config_file(custom_location=nil)
107
- DEPRECATED_CONFIG_FILES.each do |f|
108
- notify "#{f} is deprecated, please use one of #{CONFIG_FILES.join(", ")}" if File.file?(f)
109
- end
110
- supported_locations = [File.expand_path(custom_location || "")] + DEPRECATED_CONFIG_FILES + CONFIG_FILES
111
- supported_locations.detect{|f| File.file?(f) }
104
+ def self.config_file custom_location = nil
105
+ supported_locations = [File.expand_path(custom_location || "")] + CONFIG_FILES
106
+ supported_locations.detect {|f| File.file?(f) }
112
107
  end
113
108
 
114
109
  #Default set of options
115
- def self.get_defaults
110
+ def self.default_options
116
111
  { :assume_all_routes => true,
117
112
  :skip_checks => Set.new,
118
113
  :check_arguments => true,
@@ -126,7 +121,6 @@ module Brakeman
126
121
  :message_limit => 100,
127
122
  :parallel_checks => true,
128
123
  :relative_path => false,
129
- :quiet => true,
130
124
  :report_progress => true,
131
125
  :html_style => "#{File.expand_path(File.dirname(__FILE__))}/brakeman/format/style.css"
132
126
  }
@@ -140,61 +134,80 @@ module Brakeman
140
134
  raise ArgumentError, "Cannot specify output format if multiple output files specified"
141
135
  end
142
136
  if options[:output_format]
143
- [
144
- case options[:output_format]
145
- when :html, :to_html
146
- :to_html
147
- when :csv, :to_csv
148
- :to_csv
149
- when :pdf, :to_pdf
150
- :to_pdf
151
- when :tabs, :to_tabs
152
- :to_tabs
153
- when :json, :to_json
154
- :to_json
155
- else
156
- :to_s
157
- end
158
- ]
137
+ get_formats_from_output_format options[:output_format]
138
+ elsif options[:output_files]
139
+ get_formats_from_output_files options[:output_files]
140
+ else
141
+ return [:to_s]
142
+ end
143
+ end
144
+
145
+ def self.get_formats_from_output_format output_format
146
+ case output_format
147
+ when :html, :to_html
148
+ [:to_html]
149
+ when :csv, :to_csv
150
+ [:to_csv]
151
+ when :pdf, :to_pdf
152
+ [:to_pdf]
153
+ when :tabs, :to_tabs
154
+ [:to_tabs]
155
+ when :json, :to_json
156
+ [:to_json]
159
157
  else
160
- return [:to_s] unless options[:output_files]
161
- options[:output_files].map do |output_file|
162
- case output_file
163
- when /\.html$/i
164
- :to_html
165
- when /\.csv$/i
166
- :to_csv
167
- when /\.pdf$/i
168
- :to_pdf
169
- when /\.tabs$/i
170
- :to_tabs
171
- when /\.json$/i
172
- :to_json
173
- else
174
- :to_s
175
- end
158
+ [:to_s]
159
+ end
160
+ end
161
+ private_class_method :get_formats_from_output_format
162
+
163
+ def self.get_formats_from_output_files output_files
164
+ output_files.map do |output_file|
165
+ case output_file
166
+ when /\.html$/i
167
+ :to_html
168
+ when /\.csv$/i
169
+ :to_csv
170
+ when /\.pdf$/i
171
+ :to_pdf
172
+ when /\.tabs$/i
173
+ :to_tabs
174
+ when /\.json$/i
175
+ :to_json
176
+ else
177
+ :to_s
176
178
  end
177
179
  end
178
180
  end
181
+ private_class_method :get_formats_from_output_files
179
182
 
180
183
  #Output list of checks (for `-k` option)
181
184
  def self.list_checks
182
185
  require 'brakeman/scanner'
186
+ format_length = 30
187
+
183
188
  $stderr.puts "Available Checks:"
184
- $stderr.puts "-" * 30
185
- $stderr.puts Checks.checks.map { |c|
186
- c.to_s.match(/^Brakeman::(.*)$/)[1].ljust(27) << c.description
187
- }.sort.join "\n"
189
+ $stderr.puts "-" * format_length
190
+ Checks.checks.each do |check|
191
+ $stderr.printf("%-#{format_length}s%s\n", check.name, check.description)
192
+ end
188
193
  end
189
194
 
190
195
  #Installs Rake task for running Brakeman,
191
196
  #which basically means copying `lib/brakeman/brakeman.rake` to
192
197
  #`lib/tasks/brakeman.rake` in the current Rails application.
193
- def self.install_rake_task
194
- if not File.exists? "Rakefile"
195
- abort "No Rakefile detected"
196
- elsif File.exists? "lib/tasks/brakeman.rake"
197
- abort "Task already exists"
198
+ def self.install_rake_task install_path = nil
199
+ if install_path
200
+ rake_path = File.join(install_path, "Rakefile")
201
+ task_path = File.join(install_path, "lib", "tasks", "brakeman.rake")
202
+ else
203
+ rake_path = "Rakefile"
204
+ task_path = File.join("lib", "tasks", "brakeman.rake")
205
+ end
206
+
207
+ if not File.exists? rake_path
208
+ raise RakeInstallError, "No Rakefile detected"
209
+ elsif File.exists? task_path
210
+ raise RakeInstallError, "Task already exists"
198
211
  end
199
212
 
200
213
  require 'fileutils'
@@ -206,13 +219,13 @@ module Brakeman
206
219
 
207
220
  path = File.expand_path(File.dirname(__FILE__))
208
221
 
209
- FileUtils.cp "#{path}/brakeman/brakeman.rake", "lib/tasks/brakeman.rake"
222
+ FileUtils.cp "#{path}/brakeman/brakeman.rake", task_path
210
223
 
211
- if File.exists? "lib/tasks/brakeman.rake"
212
- notify "Task created in lib/tasks/brakeman.rake"
224
+ if File.exists? task_path
225
+ notify "Task created in #{task_path}"
213
226
  notify "Usage: rake brakeman:run[output_file]"
214
227
  else
215
- notify "Could not create task"
228
+ raise RakeInstallError, "Could not create task"
216
229
  end
217
230
  end
218
231
 
@@ -251,7 +264,7 @@ module Brakeman
251
264
  begin
252
265
  require 'brakeman/scanner'
253
266
  rescue LoadError
254
- abort "Cannot find lib/ directory."
267
+ raise NoBrakemanError, "Cannot find lib/ directory."
255
268
  end
256
269
 
257
270
  #Start scanning
@@ -270,22 +283,32 @@ module Brakeman
270
283
  if options[:output_files]
271
284
  notify "Generating report..."
272
285
 
273
- options[:output_files].each_with_index do |output_file, idx|
274
- File.open output_file, "w" do |f|
275
- f.write tracker.report.send(options[:output_formats][idx])
276
- end
277
- notify "Report saved in '#{output_file}'"
278
- end
286
+ write_report_to_files tracker, options[:output_files]
279
287
  elsif options[:print_report]
280
288
  notify "Generating report..."
281
289
 
282
- options[:output_formats].each do |output_format|
283
- puts tracker.report.send(output_format)
284
- end
290
+ write_report_to_formats tracker, options[:output_formats]
285
291
  end
286
292
 
287
293
  tracker
288
294
  end
295
+
296
+ def self.write_report_to_files tracker, output_files
297
+ output_files.each_with_index do |output_file, idx|
298
+ File.open output_file, "w" do |f|
299
+ f.write tracker.report.format(output_file)
300
+ end
301
+ notify "Report saved in '#{output_file}'"
302
+ end
303
+ end
304
+ private_class_method :write_report_to_files
305
+
306
+ def self.write_report_to_formats tracker, output_formats
307
+ output_formats.each do |output_format|
308
+ puts tracker.report.format(output_format)
309
+ end
310
+ end
311
+ private_class_method :write_report_to_formats
289
312
 
290
313
  #Rescan a subset of files in a Rails application.
291
314
  #
@@ -336,4 +359,7 @@ module Brakeman
336
359
 
337
360
  Brakeman::Differ.new(new_results, previous_results).diff
338
361
  end
362
+
363
+ class RakeInstallError < RuntimeError; end
364
+ class NoBrakemanError < RuntimeError; end
339
365
  end
@@ -8,17 +8,20 @@ module Brakeman
8
8
  root = options[:app_path]
9
9
 
10
10
  # Convert files into Regexp for matching
11
+ init_options = {}
11
12
  if options[:skip_files]
12
- list = "(?:" << options[:skip_files].map { |f| Regexp.escape f }.join("|") << ")$"
13
- new(root, Regexp.new(list))
14
- else
15
- new(root)
13
+ init_options[:skip_files] = Regexp.new("(?:" << options[:skip_files].map { |f| Regexp.escape f }.join("|") << ")$")
16
14
  end
15
+ if options[:only_files]
16
+ init_options[:only_files] = Regexp.new("(?:" << options[:only_files].map { |f| Regexp.escape f }.join("|") << ")")
17
+ end
18
+ new(root, init_options)
17
19
  end
18
20
 
19
- def initialize(root, skip_files = nil)
21
+ def initialize(root, init_options = {})
20
22
  @root = root
21
- @skip_files = skip_files
23
+ @skip_files = init_options[:skip_files]
24
+ @only_files = init_options[:only_files]
22
25
  end
23
26
 
24
27
  def expand_path(path)
@@ -76,14 +79,22 @@ module Brakeman
76
79
  def find_paths(directory, extensions = "*.rb")
77
80
  pattern = @root + "/#{directory}/**/#{extensions}"
78
81
 
79
- Dir.glob(pattern).sort.tap do |paths|
80
- reject_skipped_files(paths)
81
- end
82
+ select_files(Dir.glob(pattern).sort)
83
+ end
84
+
85
+ def select_files(paths)
86
+ paths = select_only_files(paths)
87
+ reject_skipped_files(paths)
88
+ end
89
+
90
+ def select_only_files(paths)
91
+ return paths unless @only_files
92
+ paths.select { |f| @only_files.match f }
82
93
  end
83
94
 
84
95
  def reject_skipped_files(paths)
85
- return unless @skip_files
86
- paths.reject! { |f| @skip_files.match f }
96
+ return paths unless @skip_files
97
+ paths.reject { |f| @skip_files.match f }
87
98
  end
88
99
 
89
100
  end