brakeman-min 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/FEATURES +16 -0
  2. data/README.md +118 -0
  3. data/WARNING_TYPES +69 -0
  4. data/bin/brakeman +269 -0
  5. data/lib/checks.rb +67 -0
  6. data/lib/checks/base_check.rb +353 -0
  7. data/lib/checks/check_cross_site_scripting.rb +324 -0
  8. data/lib/checks/check_default_routes.rb +29 -0
  9. data/lib/checks/check_evaluation.rb +27 -0
  10. data/lib/checks/check_execute.rb +110 -0
  11. data/lib/checks/check_file_access.rb +46 -0
  12. data/lib/checks/check_forgery_setting.rb +42 -0
  13. data/lib/checks/check_mail_to.rb +48 -0
  14. data/lib/checks/check_mass_assignment.rb +72 -0
  15. data/lib/checks/check_model_attributes.rb +36 -0
  16. data/lib/checks/check_nested_attributes.rb +34 -0
  17. data/lib/checks/check_redirect.rb +98 -0
  18. data/lib/checks/check_render.rb +65 -0
  19. data/lib/checks/check_send_file.rb +15 -0
  20. data/lib/checks/check_session_settings.rb +36 -0
  21. data/lib/checks/check_sql.rb +124 -0
  22. data/lib/checks/check_validation_regex.rb +60 -0
  23. data/lib/format/style.css +105 -0
  24. data/lib/processor.rb +83 -0
  25. data/lib/processors/alias_processor.rb +384 -0
  26. data/lib/processors/base_processor.rb +237 -0
  27. data/lib/processors/config_processor.rb +146 -0
  28. data/lib/processors/controller_alias_processor.rb +237 -0
  29. data/lib/processors/controller_processor.rb +202 -0
  30. data/lib/processors/erb_template_processor.rb +84 -0
  31. data/lib/processors/erubis_template_processor.rb +62 -0
  32. data/lib/processors/haml_template_processor.rb +131 -0
  33. data/lib/processors/lib/find_call.rb +176 -0
  34. data/lib/processors/lib/find_model_call.rb +39 -0
  35. data/lib/processors/lib/processor_helper.rb +36 -0
  36. data/lib/processors/lib/render_helper.rb +137 -0
  37. data/lib/processors/library_processor.rb +118 -0
  38. data/lib/processors/model_processor.rb +125 -0
  39. data/lib/processors/output_processor.rb +233 -0
  40. data/lib/processors/params_processor.rb +77 -0
  41. data/lib/processors/route_processor.rb +338 -0
  42. data/lib/processors/template_alias_processor.rb +86 -0
  43. data/lib/processors/template_processor.rb +55 -0
  44. data/lib/report.rb +651 -0
  45. data/lib/scanner.rb +215 -0
  46. data/lib/scanner_erubis.rb +43 -0
  47. data/lib/tracker.rb +144 -0
  48. data/lib/util.rb +141 -0
  49. data/lib/version.rb +1 -0
  50. data/lib/warning.rb +97 -0
  51. metadata +141 -0
@@ -0,0 +1,29 @@
1
+ require 'checks/base_check'
2
+
3
+ #Checks if default routes are allowed in routes.rb
4
+ class CheckDefaultRoutes < BaseCheck
5
+ Checks.add self
6
+
7
+ #Checks for :allow_all_actions globally and for individual routes
8
+ #if it is not enabled globally.
9
+ def run_check
10
+ if tracker.routes[:allow_all_actions]
11
+ #Default routes are enabled globally
12
+ warn :warning_type => "Default Routes",
13
+ :message => "All public methods in controllers are available as actions in routes.rb",
14
+ :line => tracker.routes[:allow_all_actions].line,
15
+ :confidence => CONFIDENCE[:high],
16
+ :file => "#{OPTIONS[:app_path]}/config/routes.rb"
17
+ else #Report each controller separately
18
+ tracker.routes.each do |name, actions|
19
+ if actions == :allow_all_actions
20
+ warn :controller => name,
21
+ :warning_type => "Default Routes",
22
+ :message => "Any public method in #{name} can be used as an action.",
23
+ :confidence => CONFIDENCE[:med],
24
+ :file => "#{OPTIONS[:app_path]}/config/routes.rb"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ require 'checks/base_check'
2
+
3
+ #This check looks for calls to +eval+, +instance_eval+, etc. which include
4
+ #user input.
5
+ class CheckEvaluation < BaseCheck
6
+ Checks.add self
7
+
8
+ #Process calls
9
+ def run_check
10
+ calls = tracker.find_call nil, [:eval, :instance_eval, :class_eval, :module_eval]
11
+
12
+ calls.each do |call|
13
+ process_result call
14
+ end
15
+ end
16
+
17
+ #Warns if result includes user input
18
+ def process_result result
19
+ if include_user_input? result[-1]
20
+ warn :result => result,
21
+ :warning_type => "Dangerous Eval",
22
+ :message => "User input in eval",
23
+ :code => result[-1],
24
+ :confidence => CONFIDENCE[:high]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,110 @@
1
+ require 'checks/base_check'
2
+ require 'processors/lib/find_call'
3
+
4
+ #Checks for string interpolation and parameters in calls to
5
+ #Kernel#system, Kernel#exec, Kernel#syscall, and inside backticks.
6
+ #
7
+ #Examples of command injection vulnerabilities:
8
+ #
9
+ # system("rf -rf #{params[:file]}")
10
+ # exec(params[:command])
11
+ # `unlink #{params[:something}`
12
+ class CheckExecute < BaseCheck
13
+ Checks.add self
14
+
15
+ #Check models, controllers, and views for command injection.
16
+ def run_check
17
+ check_for_backticks tracker
18
+
19
+ calls = tracker.find_call [:IO, :Open3, :Kernel, []], [:exec, :popen, :popen3, :syscall, :system]
20
+
21
+ calls.each do |result|
22
+ process result
23
+ end
24
+ end
25
+
26
+ #Processes results from FindCall.
27
+ def process_result exp
28
+ call = exp[-1]
29
+
30
+ args = process call[3]
31
+
32
+ case call[2]
33
+ when :system, :exec
34
+ failure = include_user_input?(args[1]) || include_interp?(args[1])
35
+ else
36
+ failure = include_user_input?(args) || include_interp?(args)
37
+ end
38
+
39
+ if failure and not duplicate? call, exp[1]
40
+ add_result call, exp[1]
41
+
42
+ if @string_interp
43
+ confidence = CONFIDENCE[:med]
44
+ else
45
+ confidence = CONFIDENCE[:high]
46
+ end
47
+
48
+ warn :result => exp,
49
+ :warning_type => "Command Injection",
50
+ :message => "Possible command injection",
51
+ :line => call.line,
52
+ :code => call,
53
+ :confidence => confidence
54
+ end
55
+
56
+ exp
57
+ end
58
+
59
+ #Looks for calls using backticks such as
60
+ #
61
+ # `rm -rf #{params[:file]}`
62
+ def check_for_backticks tracker
63
+ tracker.each_method do |exp, set_name, method_name|
64
+ @current_set = set_name
65
+ @current_method = method_name
66
+
67
+ process exp
68
+ end
69
+
70
+ @current_set = nil
71
+
72
+ tracker.each_template do |name, template|
73
+ @current_template = template
74
+
75
+ process template[:src]
76
+ end
77
+
78
+ @current_template = nil
79
+ end
80
+
81
+ #Processes backticks.
82
+ def process_dxstr exp
83
+ return exp if duplicate? exp
84
+
85
+ add_result exp
86
+
87
+ if include_user_input? exp
88
+ confidence = CONFIDENCE[:high]
89
+ else
90
+ confidence = CONFIDENCE[:med]
91
+ end
92
+
93
+ warning = { :warning_type => "Command Injection",
94
+ :message => "Possible command injection",
95
+ :line => exp.line,
96
+ :code => exp,
97
+ :confidence => confidence }
98
+
99
+ if @current_template
100
+ warning[:template] = @current_template
101
+ else
102
+ warning[:class] = @current_set
103
+ warning[:method] = @current_method
104
+ end
105
+
106
+ warn warning
107
+
108
+ exp
109
+ end
110
+ end
@@ -0,0 +1,46 @@
1
+ require 'checks/base_check'
2
+ require 'processors/lib/processor_helper'
3
+
4
+ #Checks for user input in methods which open or manipulate files
5
+ class CheckFileAccess < BaseCheck
6
+ Checks.add self
7
+
8
+ def run_check
9
+ methods = tracker.find_call [[:Dir, :File, :IO, :Kernel, :"Net::FTP", :"Net::HTTP", :PStore, :Pathname, :Shell, :YAML], []], [:[], :chdir, :chroot, :delete, :entries, :foreach, :glob, :install, :lchmod, :lchown, :link, :load, :load_file, :makedirs, :move, :new, :open, :read, :read_lines, :rename, :rmdir, :safe_unlink, :symlink, :syscopy, :sysopen, :truncate, :unlink]
10
+
11
+ methods.concat tracker.find_call(:FileUtils, nil)
12
+
13
+ methods.each do |call|
14
+ process_result call
15
+ end
16
+ end
17
+
18
+ def process_result result
19
+ call = result[-1]
20
+
21
+ file_name = call[3][1]
22
+
23
+ if check = include_user_input?(file_name)
24
+ unless duplicate? call, result[1]
25
+ add_result call, result[1]
26
+
27
+ if check == :params
28
+ message = "Parameter"
29
+ elsif check == :cookies
30
+ message = "Cookie"
31
+ else
32
+ message = "User input"
33
+ end
34
+
35
+ message << " value used in file name"
36
+
37
+ warn :result => result,
38
+ :warning_type => "File Access",
39
+ :message => message,
40
+ :confidence => CONFIDENCE[:high],
41
+ :line => call.line,
42
+ :code => call
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ require 'checks/base_check'
2
+
3
+ #Checks that +protect_from_forgery+ is set in the ApplicationController.
4
+ #
5
+ #Also warns for CSRF weakness in certain versions of Rails:
6
+ #http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2d95a3cc23e03665
7
+ class CheckForgerySetting < BaseCheck
8
+ Checks.add self
9
+
10
+ def run_check
11
+ app_controller = tracker.controllers[:ApplicationController]
12
+ if tracker.config[:rails][:action_controller] and
13
+ tracker.config[:rails][:action_controller][:allow_forgery_protection] == Sexp.new(:false)
14
+
15
+ warn :controller => :ApplicationController,
16
+ :warning_type => "Cross Site Request Forgery",
17
+ :message => "Forgery protection is disabled",
18
+ :confidence => CONFIDENCE[:high]
19
+
20
+ elsif app_controller and not app_controller[:options][:protect_from_forgery]
21
+
22
+ warn :controller => :ApplicationController,
23
+ :warning_type => "Cross-Site Request Forgery",
24
+ :message => "'protect_from_forgery' should be called in ApplicationController",
25
+ :confidence => CONFIDENCE[:high]
26
+
27
+ elsif version_between? "2.1.0", "2.3.10"
28
+
29
+ warn :controller => :ApplicationController,
30
+ :warning_type => "Cross-Site Request Forgery",
31
+ :message => "CSRF protection is flawed in #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 2.3.11 or apply patches",
32
+ :confidence => CONFIDENCE[:high]
33
+
34
+ elsif version_between? "3.0.0", "3.0.3"
35
+
36
+ warn :controller => :ApplicationController,
37
+ :warning_type => "Cross-Site Request Forgery",
38
+ :message => "CSRF protection is flawed in #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 3.0.4",
39
+ :confidence => CONFIDENCE[:high]
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ require 'checks/base_check'
2
+ require 'processors/lib/find_call'
3
+
4
+ #Check for cross site scripting vulnerability in mail_to :encode => :javascript
5
+ #with certain versions of Rails (< 2.3.11 or < 3.0.4).
6
+ #
7
+ #http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f02a48ede8315f81
8
+ class CheckMailTo < BaseCheck
9
+ Checks.add self
10
+
11
+ def run_check
12
+ if (version_between? "2.3.0", "2.3.10" or version_between? "3.0.0", "3.0.3") and result = mail_to_javascript?
13
+ message = "Vulnerability in mail_to using javascript encoding (CVE-2011-0446). Upgrade to Rails version "
14
+
15
+ if version_between? "2.3.0", "2.3.10"
16
+ message << "2.3.11"
17
+ else
18
+ message << "3.0.4"
19
+ end
20
+
21
+ warn :result => result,
22
+ :warning_type => "Mail Link",
23
+ :message => message,
24
+ :confidence => CONFIDENCE[:high]
25
+ end
26
+ end
27
+
28
+ #Check for javascript encoding of mail_to address
29
+ # mail_to email, name, :encode => :javascript
30
+ def mail_to_javascript?
31
+ tracker.find_call([], :mail_to).each do |result|
32
+ call = result[-1]
33
+ args = call[-1]
34
+
35
+ args.each do |arg|
36
+ if hash? arg
37
+ hash_iterate arg do |k, v|
38
+ if symbol? v and v[-1] == :javascript
39
+ return result
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ false
47
+ end
48
+ end
@@ -0,0 +1,72 @@
1
+ require 'checks/base_check'
2
+
3
+ #Checks for mass assignments to models.
4
+ #
5
+ #See http://guides.rubyonrails.org/security.html#mass-assignment for details
6
+ class CheckMassAssignment < BaseCheck
7
+ Checks.add self
8
+
9
+ def run_check
10
+ return if mass_assign_disabled? tracker
11
+
12
+ models = []
13
+ tracker.models.each do |name, m|
14
+ if parent?(tracker, m, :"ActiveRecord::Base") and m[:attr_accessible].nil?
15
+ models << name
16
+ end
17
+ end
18
+
19
+ return if models.empty?
20
+
21
+ @results = Set.new
22
+
23
+ calls = tracker.find_call models, [:new,
24
+ :attributes=,
25
+ :update_attribute,
26
+ :update_attributes,
27
+ :update_attributes!]
28
+
29
+ calls.each do |result|
30
+ process result
31
+ end
32
+ end
33
+
34
+ #All results should be Model.new(...) or Model.attributes=() calls
35
+ def process_result res
36
+ call = res[-1]
37
+
38
+ check = check_call call
39
+
40
+ if check and not @results.include? call
41
+ @results << call
42
+
43
+ if include_user_input? call[3]
44
+ confidence = CONFIDENCE[:high]
45
+ else
46
+ confidence = CONFIDENCE[:med]
47
+ end
48
+
49
+ warn :result => res,
50
+ :warning_type => "Mass Assignment",
51
+ :message => "Unprotected mass assignment",
52
+ :line => call.line,
53
+ :code => call,
54
+ :confidence => confidence
55
+ end
56
+ res
57
+ end
58
+
59
+ #Want to ignore calls to Model.new that have no arguments
60
+ def check_call call
61
+ args = process call[3]
62
+ if args.length <= 1 #empty new()
63
+ false
64
+ elsif hash? args[1]
65
+ #Still should probably check contents of hash
66
+ false
67
+ else
68
+ true
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,36 @@
1
+ require 'checks/base_check'
2
+
3
+ #Check if mass assignment is used with models
4
+ #which inherit from ActiveRecord::Base.
5
+ #
6
+ #If OPTIONS[:collapse_mass_assignment] is +true+ (default), all models which do
7
+ #not use attr_accessible will be reported in a single warning
8
+ class CheckModelAttributes < BaseCheck
9
+ Checks.add self
10
+
11
+ def run_check
12
+ return if mass_assign_disabled? tracker
13
+
14
+ names = []
15
+
16
+ tracker.models.each do |name, model|
17
+ if model[:attr_accessible].nil? and parent? tracker, model, :"ActiveRecord::Base"
18
+ if OPTIONS[:collapse_mass_assignment]
19
+ names << name.to_s
20
+ else
21
+ warn :model => name,
22
+ :warning_type => "Attribute Restriction",
23
+ :message => "Mass assignment is not restricted using attr_accessible",
24
+ :confidence => CONFIDENCE[:high]
25
+ end
26
+ end
27
+ end
28
+
29
+ if OPTIONS[:collapse_mass_assignment] and not names.empty?
30
+ warn :model => names.sort.join(", "),
31
+ :warning_type => "Attribute Restriction",
32
+ :message => "Mass assignment is not restricted using attr_accessible",
33
+ :confidence => CONFIDENCE[:high]
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ require 'checks/base_check'
2
+ require 'processors/lib/find_call'
3
+
4
+ #Check for vulnerability in nested attributes in Rails 2.3.9 and 3.0.0
5
+ #http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f9f913d328dafe0c
6
+ class CheckNestedAttributes < BaseCheck
7
+ Checks.add self
8
+
9
+ def run_check
10
+ version = tracker.config[:rails_version]
11
+
12
+ if (version == "2.3.9" or version == "3.0.0") and uses_nested_attributes?
13
+ message = "Vulnerability in nested attributes (CVE-2010-3933). Upgrade to Rails version "
14
+
15
+ if version == "2.3.9"
16
+ message << "2.3.10"
17
+ else
18
+ message << "3.0.1"
19
+ end
20
+
21
+ warn :warning_type => "Nested Attributes",
22
+ :message => message,
23
+ :confidence => CONFIDENCE[:high]
24
+ end
25
+ end
26
+
27
+ def uses_nested_attributes?
28
+ tracker.models.each do |name, model|
29
+ return true if model[:options][:accepts_nested_attributes_for]
30
+ end
31
+
32
+ false
33
+ end
34
+ end