brakeman 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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