brakeman 0.7.2 → 0.8.0

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/README.md CHANGED
@@ -6,6 +6,10 @@ It targets Rails versions > 2.0 with experimental support for Rails 3.x
6
6
 
7
7
  There is also a [plugin available](https://github.com/presidentbeef/brakeman-jenkins-plugin) for Jenkins/Hudson.
8
8
 
9
+ # Homepage
10
+
11
+ http://brakemanscanner.org/
12
+
9
13
  # Installation
10
14
 
11
15
  Using RubyGems:
@@ -1,25 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
- require "optparse"
2
+ require 'optparse'
3
3
  require 'set'
4
- require 'yaml'
5
4
 
6
5
  $:.unshift "#{File.expand_path(File.dirname(__FILE__))}/../lib"
7
6
 
8
7
  require 'version'
8
+ require 'brakeman'
9
9
 
10
10
  trap("INT") do
11
11
  $stderr.puts "\nInterrupted - exiting."
12
12
  exit!
13
13
  end
14
14
 
15
- def list_checks
16
- require 'scanner'
17
- $stderr.puts "Available Checks:"
18
- $stderr.puts "-" * 30
19
- $stderr.puts Checks.checks.map { |c| c.to_s }.sort.join "\n"
20
- exit
21
- end
22
-
23
15
  #Parse command line options
24
16
  options = {}
25
17
 
@@ -148,147 +140,4 @@ OptionParser.new do |opts|
148
140
  end
149
141
  end.parse!(ARGV)
150
142
 
151
- #Load configuation file
152
- [File.expand_path(options[:config_file].to_s),
153
- File.expand_path("./config.yaml"),
154
- File.expand_path("~/.brakeman/config.yaml"),
155
- File.expand_path("/etc/brakeman/config.yaml"),
156
- "#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"].each do |f|
157
-
158
- if File.exist? f and not File.directory? f
159
- warn "[Notice] Using configuration in #{f}" unless options[:quiet]
160
- OPTIONS = YAML.load_file f
161
- OPTIONS.merge! options
162
- OPTIONS.each do |k,v|
163
- if v.is_a? Array
164
- OPTIONS[k] = Set.new v
165
- end
166
- end
167
- break
168
- end
169
- end
170
-
171
- OPTIONS = options unless defined? OPTIONS
172
-
173
- #List available checks and exits
174
- list_checks if OPTIONS[:list_checks]
175
-
176
- #Set defaults just in case
177
- { :skip_checks => Set.new,
178
- :check_arguments => true,
179
- :safe_methods => Set.new,
180
- :min_confidence => 2,
181
- :combine_locations => true,
182
- :collapse_mass_assignment => true,
183
- :ignore_redirect_to_model => true,
184
- :ignore_model_output => false,
185
- :message_limit => 100,
186
- :html_style => "#{File.expand_path(File.dirname(__FILE__))}/../lib/format/style.css"
187
- }.each do |k,v|
188
- OPTIONS[k] = v if OPTIONS[k].nil?
189
- end
190
-
191
-
192
- #Set output format
193
- if OPTIONS[:output_format]
194
- case OPTIONS[:output_format]
195
- when :html, :to_html
196
- OPTIONS[:output_format] = :to_html
197
- when :csv, :to_csv
198
- OPTIONS[:output_format] = :to_csv
199
- when :pdf, :to_pdf
200
- OPTIONS[:output_format] = :to_pdf
201
- when :tabs, :to_tabs
202
- OPTIONS[:output_format] = :to_tabs
203
- else
204
- OPTIONS[:output_format] = :to_s
205
- end
206
- else
207
- case OPTIONS[:output_file]
208
- when /\.html$/i
209
- OPTIONS[:output_format] = :to_html
210
- when /\.csv$/i
211
- OPTIONS[:output_format] = :to_csv
212
- when /\.pdf$/i
213
- OPTIONS[:output_format] = :to_pdf
214
- when /\.tabs$/i
215
- OPTIONS[:output_format] = :to_tabs
216
- else
217
- OPTIONS[:output_format] = :to_s
218
- end
219
- end
220
-
221
- #Output configuration if requested
222
- if OPTIONS[:create_config]
223
-
224
- if OPTIONS[:create_config].is_a? String
225
- file = OPTIONS[:create_config]
226
- else
227
- file = nil
228
- end
229
-
230
- OPTIONS.delete :create_config
231
-
232
- OPTIONS.each do |k,v|
233
- if v.is_a? Set
234
- OPTIONS[k] = v.to_a
235
- end
236
- end
237
-
238
- if file
239
- File.open file, "w" do |f|
240
- YAML.dump OPTIONS, f
241
- end
242
- puts "Output configuration to #{file}"
243
- else
244
- puts YAML.dump(OPTIONS)
245
- end
246
- exit
247
- end
248
-
249
-
250
- #Check application path
251
- unless OPTIONS[:app_path]
252
- if ARGV[-1].nil?
253
- OPTIONS[:app_path] = File.expand_path "."
254
- else
255
- OPTIONS[:app_path] = File.expand_path ARGV[-1]
256
- end
257
- end
258
-
259
- app_path = OPTIONS[:app_path]
260
-
261
- abort("Please supply the path to a Rails application.") unless app_path and File.exist? app_path + "/app"
262
-
263
- warn "[Notice] Using Ruby #{RUBY_VERSION}. Please make sure this matches the one used to run your Rails application."
264
-
265
- if File.exist? app_path + "/script/rails"
266
- OPTIONS[:rails3] = true
267
- warn "[Notice] Detected Rails 3 application. Enabling experimental Rails 3 support."
268
- end
269
-
270
- #Load scanner
271
- begin
272
- require 'scanner'
273
- rescue LoadError
274
- abort "Cannot find lib/ directory."
275
- end
276
-
277
- #Start scanning
278
- scanner = Scanner.new app_path
279
-
280
- warn "Processing application in #{app_path}"
281
- tracker = scanner.process
282
-
283
- warn "Running checks..."
284
- tracker.run_checks
285
-
286
- warn "Generating report..."
287
- if OPTIONS[:output_file]
288
- File.open OPTIONS[:output_file], "w" do |f|
289
- f.puts tracker.report.send(OPTIONS[:output_format])
290
- end
291
- warn "Report saved in '#{OPTIONS[:output_file]}'"
292
- else
293
- puts tracker.report.send(OPTIONS[:output_format])
294
- end
143
+ Brakeman.run options
@@ -0,0 +1,186 @@
1
+ require 'yaml'
2
+
3
+ OPTIONS = {}
4
+
5
+ module Brakeman
6
+ def self.run options
7
+ if options[:list_checks]
8
+ list_checks
9
+ exit
10
+ end
11
+
12
+ if options[:create_config]
13
+ dump_config options
14
+ exit
15
+ end
16
+
17
+ scan set_options(options)
18
+ end
19
+
20
+ private
21
+
22
+ def self.set_options options
23
+ options = load_options(options[:config_file]).merge! options
24
+ options = get_defaults.merge! options
25
+ options[:output_format] = get_output_format options
26
+
27
+ #Check application path
28
+ unless options[:app_path]
29
+ if ARGV[-1].nil?
30
+ options[:app_path] = File.expand_path "."
31
+ else
32
+ options[:app_path] = File.expand_path ARGV[-1]
33
+ end
34
+ end
35
+
36
+ app_path = options[:app_path]
37
+
38
+ abort("Please supply the path to a Rails application.") unless app_path and File.exist? app_path + "/app"
39
+
40
+ if File.exist? app_path + "/script/rails"
41
+ options[:rails3] = true
42
+ warn "[Notice] Detected Rails 3 application. Enabling experimental Rails 3 support."
43
+ end
44
+
45
+ options
46
+ end
47
+
48
+ def self.load_options config_file
49
+ config_file ||= ""
50
+
51
+ #Load configuation file
52
+ [File.expand_path(config_file),
53
+ File.expand_path("./config.yaml"),
54
+ File.expand_path("~/.brakeman/config.yaml"),
55
+ File.expand_path("/etc/brakeman/config.yaml"),
56
+ "#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"].each do |f|
57
+
58
+ if File.exist? f and not File.directory? f
59
+ warn "[Notice] Using configuration in #{f}" unless options[:quiet]
60
+ options = YAML.load_file f
61
+ options.each do |k,v|
62
+ if v.is_a? Array
63
+ options[k] = Set.new v
64
+ end
65
+ end
66
+
67
+ return options
68
+ end
69
+ end
70
+
71
+ return {}
72
+ end
73
+
74
+ def self.get_defaults
75
+ { :skip_checks => Set.new,
76
+ :check_arguments => true,
77
+ :safe_methods => Set.new,
78
+ :min_confidence => 2,
79
+ :combine_locations => true,
80
+ :collapse_mass_assignment => true,
81
+ :ignore_redirect_to_model => true,
82
+ :ignore_model_output => false,
83
+ :message_limit => 100,
84
+ :html_style => "#{File.expand_path(File.dirname(__FILE__))}/../lib/format/style.css"
85
+ }
86
+ end
87
+
88
+ def self.get_output_format options
89
+ #Set output format
90
+ if options[:output_format]
91
+ case options[:output_format]
92
+ when :html, :to_html
93
+ :to_html
94
+ when :csv, :to_csv
95
+ :to_csv
96
+ when :pdf, :to_pdf
97
+ :to_pdf
98
+ when :tabs, :to_tabs
99
+ :to_tabs
100
+ else
101
+ :to_s
102
+ end
103
+ else
104
+ case options[:output_file]
105
+ when /\.html$/i
106
+ :to_html
107
+ when /\.csv$/i
108
+ :to_csv
109
+ when /\.pdf$/i
110
+ :to_pdf
111
+ when /\.tabs$/i
112
+ :to_tabs
113
+ else
114
+ :to_s
115
+ end
116
+ end
117
+ end
118
+
119
+ def self.list_checks
120
+ require 'scanner'
121
+ $stderr.puts "Available Checks:"
122
+ $stderr.puts "-" * 30
123
+ $stderr.puts Checks.checks.map { |c| c.to_s }.sort.join "\n"
124
+ end
125
+
126
+ def self.dump_config options
127
+ if options[:create_config].is_a? String
128
+ file = options[:create_config]
129
+ else
130
+ file = nil
131
+ end
132
+
133
+ options.delete :create_config
134
+
135
+ options.each do |k,v|
136
+ if v.is_a? Set
137
+ options[k] = v.to_a
138
+ end
139
+ end
140
+
141
+ if file
142
+ File.open file, "w" do |f|
143
+ YAML.dump options, f
144
+ end
145
+ puts "Output configuration to #{file}"
146
+ else
147
+ puts YAML.dump(options)
148
+ end
149
+ exit
150
+ end
151
+
152
+ def self.scan options
153
+ OPTIONS.clear
154
+ OPTIONS.merge! options
155
+
156
+ #Load scanner
157
+ warn "Loading scanner..."
158
+
159
+ begin
160
+ require 'scanner'
161
+ rescue LoadError
162
+ abort "Cannot find lib/ directory."
163
+ end
164
+
165
+ #Start scanning
166
+ scanner = Scanner.new options[:app_path]
167
+
168
+ warn "[Notice] Using Ruby #{RUBY_VERSION}. Please make sure this matches the one used to run your Rails application."
169
+
170
+ warn "Processing application in #{options[:app_path]}"
171
+ tracker = scanner.process
172
+
173
+ warn "Running checks..."
174
+ tracker.run_checks
175
+
176
+ warn "Generating report..."
177
+ if OPTIONS[:output_file]
178
+ File.open OPTIONS[:output_file], "w" do |f|
179
+ f.puts tracker.report.send(OPTIONS[:output_format])
180
+ end
181
+ warn "Report saved in '#{OPTIONS[:output_file]}'"
182
+ else
183
+ puts tracker.report.send(OPTIONS[:output_format])
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,47 @@
1
+ require 'checks/base_check'
2
+
3
+ #Checks if password is stored in controller
4
+ #when using http_basic_authenticate_with
5
+ #
6
+ #Only for Rails >= 3.1
7
+ class CheckBasicAuth < BaseCheck
8
+ Checks.add self
9
+
10
+ def run_check
11
+ return if version_between? "0.0.0", "3.0.99"
12
+
13
+ controllers = tracker.controllers.select do |name, c|
14
+ c[:options][:http_basic_authenticate_with]
15
+ end
16
+
17
+ Hash[controllers].each do |name, controller|
18
+ controller[:options][:http_basic_authenticate_with].each do |call|
19
+
20
+ if pass = get_password(call) and string? pass
21
+ warn :controller => name,
22
+ :warning_type => "Basic Auth",
23
+ :message => "Basic authentication password stored in source code",
24
+ :line => call.line,
25
+ :code => call,
26
+ :confidence => 0
27
+
28
+ break
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def get_password call
35
+ args = call[3][1]
36
+
37
+ return false if args.nil? or not hash? args
38
+
39
+ hash_iterate(args) do |k, v|
40
+ if symbol? k and k[1] == :password
41
+ return v
42
+ end
43
+ end
44
+
45
+ nil
46
+ end
47
+ end
@@ -28,7 +28,7 @@ class CheckCrossSiteScripting < BaseCheck
28
28
  IGNORE_MODEL_METHODS = Set.new([:average, :count, :maximum, :minimum, :sum])
29
29
 
30
30
  #Methods known to not escape their input
31
- KNOWN_DANGEROUS = Set.new([:auto_link, :truncate, :concat])
31
+ KNOWN_DANGEROUS = Set.new([:truncate, :concat])
32
32
 
33
33
  MODEL_METHODS = Set.new([:all, :find, :first, :last, :new])
34
34
 
@@ -52,6 +52,12 @@ class CheckCrossSiteScripting < BaseCheck
52
52
 
53
53
  CheckLinkTo.new(checks, tracker).run_check
54
54
 
55
+ if version_between? "2.0.0", "3.0.5"
56
+ KNOWN_DANGEROUS << :auto_link
57
+ elsif version_between? "3.0.6", "3.0.99"
58
+ IGNORE_METHODS << :auto_link
59
+ end
60
+
55
61
  tracker.each_template do |name, template|
56
62
  @current_template = template
57
63
 
@@ -42,7 +42,7 @@ class CheckMassAssignment < BaseCheck
42
42
  if check and not @results.include? call
43
43
  @results << call
44
44
 
45
- if include_user_input? call[3]
45
+ if include_user_input? call[3] and not hash? call[3][1]
46
46
  confidence = CONFIDENCE[:high]
47
47
  else
48
48
  confidence = CONFIDENCE[:med]
@@ -55,6 +55,7 @@ class CheckMassAssignment < BaseCheck
55
55
  :code => call,
56
56
  :confidence => confidence
57
57
  end
58
+
58
59
  res
59
60
  end
60
61
 
@@ -63,8 +64,7 @@ class CheckMassAssignment < BaseCheck
63
64
  args = process call[3]
64
65
  if args.length <= 1 #empty new()
65
66
  false
66
- elsif hash? args[1]
67
- #Still should probably check contents of hash
67
+ elsif hash? args[1] and not include_user_input? args[1]
68
68
  false
69
69
  else
70
70
  true