brakeman 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGES +14 -0
  5. data/README.md +6 -28
  6. data/lib/brakeman/checks/base_check.rb +5 -4
  7. data/lib/brakeman/checks/check_basic_auth.rb +1 -2
  8. data/lib/brakeman/checks/check_default_routes.rb +65 -15
  9. data/lib/brakeman/checks/check_detailed_exceptions.rb +5 -4
  10. data/lib/brakeman/checks/check_filter_skipping.rb +1 -1
  11. data/lib/brakeman/checks/check_forgery_setting.rb +9 -9
  12. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  13. data/lib/brakeman/checks/check_model_attributes.rb +3 -3
  14. data/lib/brakeman/checks/check_model_serialize.rb +1 -1
  15. data/lib/brakeman/checks/check_redirect.rb +27 -6
  16. data/lib/brakeman/checks/check_render.rb +2 -2
  17. data/lib/brakeman/checks/check_skip_before_filter.rb +2 -2
  18. data/lib/brakeman/checks/check_sql.rb +2 -1
  19. data/lib/brakeman/file_parser.rb +49 -0
  20. data/lib/brakeman/options.rb +1 -1
  21. data/lib/brakeman/parsers/template_parser.rb +88 -0
  22. data/lib/brakeman/processors/alias_processor.rb +25 -2
  23. data/lib/brakeman/processors/controller_alias_processor.rb +3 -3
  24. data/lib/brakeman/processors/controller_processor.rb +106 -54
  25. data/lib/brakeman/processors/lib/rails3_route_processor.rb +27 -12
  26. data/lib/brakeman/processors/lib/route_helper.rb +1 -1
  27. data/lib/brakeman/processors/library_processor.rb +37 -28
  28. data/lib/brakeman/processors/model_processor.rb +117 -34
  29. data/lib/brakeman/report/report_base.rb +1 -1
  30. data/lib/brakeman/rescanner.rb +84 -35
  31. data/lib/brakeman/scanner.rb +84 -148
  32. data/lib/brakeman/tracker.rb +32 -12
  33. data/lib/brakeman/util.rb +13 -4
  34. data/lib/brakeman/version.rb +1 -1
  35. data/lib/brakeman/warning_codes.rb +2 -1
  36. metadata +6 -4
  37. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NzVjZjYxZWMzOTRhYzI0YTA0M2ZlYjkxNDQyZjFkZTNlYzYxZTk4ZA==
4
+ MTMyMjhjZTg2NjUzNjM4Zjg4MjkxMDY3YjljM2RlMzYxNDE1MTcwMg==
5
5
  data.tar.gz: !binary |-
6
- MjY3ZGNkNGJkYzZlOWJmNjI2MGVkMmYxNDMzMDVkOTBkMzZlN2JkYg==
6
+ ODU3OTE0NjFjODgzZWRlODMyZDViZTQwNjUzYTQ0MzNhZjEzNjMyYQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YzZiNTlkMmU2OGRjOTdiZDA1ZWFhMmEwNzUyNzcwMmI5ZmNmOTRmOGRjMjc2
10
- NTkzZTZmOTc0MzIyYTRjN2Y2ZDBjZjJiZDJlNmQ3NDhlM2E5YTZjNTA2ZDI1
11
- MGM2NjNkY2YxYjNhZTVmNWJjZDZmNmE4ZGViNGJkNjc2ODdlNWU=
9
+ YTAzY2FlZWRiZTYwODMwZmQ5YmQ2YTI3M2VjMmJjMTM3Y2YxNDM5NDU3MDYw
10
+ ODJlMGNiYWM0ZDExYzU3NjI1NzY4ZWE2ZDg1MTQ2NzBiYTJkM2E4YzMwNWUw
11
+ OGVhYjU5ZDk0MzY4Njk2MzhmYzk0NGQ1YWEzNWFmMzUzMGY5MzA=
12
12
  data.tar.gz: !binary |-
13
- MTY0ZjY2ZjIyM2VkMTUxYWNhZjFiOGI0YTAzNTUzODhjZTI5MjMyNmNkNGE5
14
- MTI1ZmM2MGIwMTg3ZjJkNmFjZjY3Zjk5NzNlNTQzNjk2NTc1ZmNhNDU3Nzhl
15
- NmI1NTBkZDAwNjkzNjFiMGRiMzg0ODRmMjA4MGZlN2VhOGEyM2I=
13
+ YmVmZTkxZTQxZDNmODJkNGMyYmE2NDdkNDI1Yjk1NmRlNjlhZGRmYjgzZjY0
14
+ NGVhZDYwZGM1MWQ5MGYxMDQ4MWNmNWIxOTkwMDgxNWI3YTIzYjQ0OTQ3ODZk
15
+ MmU2ZTI5YWE3YjQ2OTczMTg3M2NiZWUxYjIxMjhiZWM4NjhhMWY=
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/CHANGES CHANGED
@@ -1,3 +1,17 @@
1
+ # 2.6.0
2
+
3
+ * Fix detection of `:host` setting in redirects with chained calls
4
+ * Add check for CVE-2014-0130
5
+ * Add `find_by`/`find_by!` to SQLi check for Rails 4
6
+ * Parse most files upfront instead of on demand
7
+ * Do not branch values for `+=`
8
+ * Update to use RubyParser 3.5.0 (Patrick Toomey)
9
+ * Improve default route detection in Rails 3/4 (Jeff Jarmoc)
10
+ * Handle controllers and models split across files (Patrick Toomey)
11
+ * Fix handling of `protected_attributes` gem in Rails 4 (Geoffrey Hichborn)
12
+ * Ignore more model methods in redirects
13
+ * Fix CheckRender with nested render calls
14
+
1
15
  # 2.5.0
2
16
 
3
17
  * Add support for RailsLTS 2.3.18.7 and 2.3.18.8
data/README.md CHANGED
@@ -46,13 +46,15 @@ From source:
46
46
 
47
47
  It is simplest to run Brakeman from the root directory of the Rails application. A path may also be supplied.
48
48
 
49
- # Options
49
+ # Basic Options
50
+
51
+ For a full list of options, use `brakeman --help` or see the OPTIONS.md file.
50
52
 
51
53
  To specify an output file for the results:
52
54
 
53
55
  brakeman -o output_file
54
56
 
55
- The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json` and `csv`.
57
+ The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `markdown`, and `csv`.
56
58
 
57
59
  Multiple output files can be specified:
58
60
 
@@ -62,6 +64,8 @@ To suppress informational warnings and just output the report:
62
64
 
63
65
  brakeman -q
64
66
 
67
+ Note all Brakeman output except reports are sent to stderr, making it simple to redirect stdout to a file and just get the report.
68
+
65
69
  To see all kinds of debugging information:
66
70
 
67
71
  brakeman -d
@@ -78,28 +82,6 @@ To do the opposite and only run a certain set of tests:
78
82
 
79
83
  brakeman -t SQL,ValidationRegex
80
84
 
81
- To indicate certain methods are "safe":
82
-
83
- brakeman -s benign_method,totally_safe
84
-
85
- By default, brakeman will assume that unknown methods involving untrusted data are dangerous. For example, this would cause a warning (Rails 2):
86
-
87
- <%= some_method(:option => params[:input]) %>
88
-
89
- To only raise warnings only when untrusted data is being directly used:
90
-
91
- brakeman -r
92
-
93
- By default, each check will be run in a separate thread. To disable this behavior:
94
-
95
- brakeman -n
96
-
97
- Normally Brakeman will parse `routes.rb` and attempt to infer which controller methods are used as actions. However, this is not perfect (especially for Rails 3). To ignore the automatically inferred routes and assume all methods are actions:
98
-
99
- brakeman -a
100
-
101
- Note that this will be enabled automatically if Brakeman runs into an error while parsing the routes.
102
-
103
85
  If Brakeman is running a bit slow, try
104
86
 
105
87
  brakeman --faster
@@ -114,10 +96,6 @@ To skip certain files that Brakeman may have trouble parsing, use:
114
96
 
115
97
  brakeman --skip-files file1,file2,etc
116
98
 
117
- Brakeman will raise warnings on models that use `attr_protected`. To suppress these warnings:
118
-
119
- brakeman --ignore-protected
120
-
121
99
  To compare results of a scan with a previous scan, use the JSON output option and then:
122
100
 
123
101
  brakeman --compare old_report.json
@@ -12,10 +12,10 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
12
12
  CONFIDENCE = { :high => 0, :med => 1, :low => 2 }
13
13
 
14
14
  Match = Struct.new(:type, :match)
15
-
15
+
16
16
  class << self
17
17
  attr_accessor :name
18
-
18
+
19
19
  def inherited(subclass)
20
20
  subclass.name = subclass.to_s.match(/^Brakeman::(.*)$/)[1]
21
21
  end
@@ -177,8 +177,9 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
177
177
  tracker.config[:rails][:active_record][:whitelist_attributes] == Sexp.new(:true)
178
178
 
179
179
  @mass_assign_disabled = true
180
- elsif version_between?("4.0.0", "4.9.9") and not tracker.config[:gems][:protected_attributes]
181
- #May need to revisit dependng on what Rails 4 actually does/has
180
+ elsif version_between?("4.0.0", "4.9.9") && (!tracker.config[:gems][:protected_attributes] || (tracker.config[:rails][:active_record] &&
181
+ tracker.config[:rails][:active_record][:whitelist_attributes] == Sexp.new(:true)))
182
+
182
183
  @mass_assign_disabled = true
183
184
  else
184
185
  #Check for ActiveRecord::Base.send(:attr_accessible, nil)
@@ -31,8 +31,7 @@ class Brakeman::CheckBasicAuth < Brakeman::BaseCheck
31
31
  :message => "Basic authentication password stored in source code",
32
32
  :code => call,
33
33
  :confidence => 0,
34
- :file => controller[:file]
35
-
34
+ :file => controller[:files].first
36
35
  break
37
36
  end
38
37
  end
@@ -9,28 +9,78 @@ class Brakeman::CheckDefaultRoutes < Brakeman::BaseCheck
9
9
  #Checks for :allow_all_actions globally and for individual routes
10
10
  #if it is not enabled globally.
11
11
  def run_check
12
- if tracker.routes[:allow_all_actions]
12
+ check_for_default_routes
13
+ check_for_action_globs
14
+ check_for_cve_2014_0130
15
+ end
16
+
17
+ def check_for_default_routes
18
+ if allow_all_actions?
13
19
  #Default routes are enabled globally
14
- warn :warning_type => "Default Routes",
20
+ warn :warning_type => "Default Routes",
15
21
  :warning_code => :all_default_routes,
16
22
  :message => "All public methods in controllers are available as actions in routes.rb",
17
- :line => tracker.routes[:allow_all_actions].line,
23
+ :line => tracker.routes[:allow_all_actions].line,
18
24
  :confidence => CONFIDENCE[:high],
19
25
  :file => "#{tracker.options[:app_path]}/config/routes.rb"
20
- else #Report each controller separately
21
- Brakeman.debug "Checking each controller for default routes"
22
-
23
- tracker.routes.each do |name, actions|
24
- if actions.is_a? Array and actions[0] == :allow_all_actions
25
- warn :controller => name,
26
- :warning_type => "Default Routes",
27
- :warning_code => :controller_default_routes,
28
- :message => "Any public method in #{name} can be used as an action.",
29
- :line => actions[1],
30
- :confidence => CONFIDENCE[:med],
31
- :file => "#{tracker.options[:app_path]}/config/routes.rb"
26
+ end
27
+ end
28
+
29
+ def check_for_action_globs
30
+ return if allow_all_actions?
31
+ Brakeman.debug "Checking each controller for default routes"
32
+
33
+ tracker.routes.each do |name, actions|
34
+ if actions.is_a? Array and actions[0] == :allow_all_actions
35
+ @actions_allowed_on_controller = true
36
+ if actions[1].is_a? Hash and actions[1][:allow_verb]
37
+ verb = actions[1][:allow_verb]
38
+ else
39
+ verb = "any"
32
40
  end
41
+ warn :controller => name,
42
+ :warning_type => "Default Routes",
43
+ :warning_code => :controller_default_routes,
44
+ :message => "Any public method in #{name} can be used as an action for #{verb} requests.",
45
+ :line => actions[2],
46
+ :confidence => CONFIDENCE[:med],
47
+ :file => "#{tracker.options[:app_path]}/config/routes.rb"
33
48
  end
34
49
  end
35
50
  end
51
+
52
+ def check_for_cve_2014_0130
53
+ case
54
+ when lts_version?("2.3.18.9")
55
+ #TODO: Should support LTS 3.0.20 too
56
+ return
57
+ when version_between?("2.0.0", "2.3.18")
58
+ upgrade = "3.2.18"
59
+ when version_between?("3.0.0", "3.2.17")
60
+ upgrade = "3.2.18"
61
+ when version_between?("4.0.0", "4.0.4")
62
+ upgrade = "4.0.5"
63
+ when version_between?("4.1.0", "4.1.0")
64
+ upgrade = "4.1.1"
65
+ else
66
+ return
67
+ end
68
+
69
+ if allow_all_actions? or @actions_allowed_on_controller
70
+ confidence = CONFIDENCE[:high]
71
+ else
72
+ confidence = CONFIDENCE[:med]
73
+ end
74
+
75
+ warn :warning_type => "Remote Code Execution",
76
+ :warning_code => :CVE_2014_0130,
77
+ :message => "Rails #{tracker.config[:rails_version]} with globbing routes is vulnerable to directory traversal and remote code execution. Patch or upgrade to #{upgrade}",
78
+ :confidence => confidence,
79
+ :file => "#{tracker.options[:app_path]}/config/routes.rb",
80
+ :link => "http://matasano.com/research/AnatomyOfRailsVuln-CVE-2014-0130.pdf"
81
+ end
82
+
83
+ def allow_all_actions?
84
+ tracker.routes[:allow_all_actions]
85
+ end
36
86
  end
@@ -25,8 +25,9 @@ class Brakeman::CheckDetailedExceptions < Brakeman::BaseCheck
25
25
 
26
26
  def check_detailed_exceptions
27
27
  tracker.controllers.each do |name, controller|
28
- controller[:public].each do |name, method|
29
- body = method.body.last
28
+ controller[:public].each do |name, definition|
29
+ src = definition[:src]
30
+ body = src.body.last
30
31
  next unless body
31
32
 
32
33
  if name == :show_detailed_exceptions? and not safe? body
@@ -40,8 +41,8 @@ class Brakeman::CheckDetailedExceptions < Brakeman::BaseCheck
40
41
  :warning_code => :detailed_exceptions,
41
42
  :message => "Detailed exceptions may be enabled in 'show_detailed_exceptions?'",
42
43
  :confidence => confidence,
43
- :code => method,
44
- :file => controller[:file]
44
+ :code => src,
45
+ :file => definition[:file]
45
46
  end
46
47
  end
47
48
  end
@@ -21,7 +21,7 @@ class Brakeman::CheckFilterSkipping < Brakeman::BaseCheck
21
21
 
22
22
  def uses_arbitrary_actions?
23
23
  tracker.routes.each do |name, actions|
24
- if actions == :allow_all_actions
24
+ if actions.include? :allow_all_actions
25
25
  return true
26
26
  end
27
27
  end
@@ -17,22 +17,22 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
17
17
  warn :controller => :ApplicationController,
18
18
  :warning_type => "Cross-Site Request Forgery",
19
19
  :warning_code => :csrf_protection_disabled,
20
- :message => "Forgery protection is disabled",
20
+ :message => "Forgery protection is disabled",
21
21
  :confidence => CONFIDENCE[:high],
22
- :file => app_controller[:file]
22
+ :file => app_controller[:files].first
23
23
 
24
24
  elsif app_controller and not app_controller[:options][:protect_from_forgery]
25
25
 
26
- warn :controller => :ApplicationController,
27
- :warning_type => "Cross-Site Request Forgery",
26
+ warn :controller => :ApplicationController,
27
+ :warning_type => "Cross-Site Request Forgery",
28
28
  :warning_code => :csrf_protection_missing,
29
- :message => "'protect_from_forgery' should be called in ApplicationController",
29
+ :message => "'protect_from_forgery' should be called in ApplicationController",
30
30
  :confidence => CONFIDENCE[:high],
31
- :file => app_controller[:file]
31
+ :file => app_controller[:files].first
32
32
 
33
33
  elsif version_between? "2.1.0", "2.3.10"
34
-
35
- warn :controller => :ApplicationController,
34
+
35
+ warn :controller => :ApplicationController,
36
36
  :warning_type => "Cross-Site Request Forgery",
37
37
  :warning_code => :CVE_2011_0447,
38
38
  :message => "CSRF protection is flawed in unpatched versions of Rails #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 2.3.11 or apply patches as needed",
@@ -42,7 +42,7 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
42
42
 
43
43
  elsif version_between? "3.0.0", "3.0.3"
44
44
 
45
- warn :controller => :ApplicationController,
45
+ warn :controller => :ApplicationController,
46
46
  :warning_type => "Cross-Site Request Forgery",
47
47
  :warning_code => :CVE_2011_0447,
48
48
  :message => "CSRF protection is flawed in unpatched versions of Rails #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 3.0.4 or apply patches as needed",
@@ -26,7 +26,7 @@ class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
26
26
  SUSP_ATTRS.each do |susp_attr, confidence|
27
27
  if susp_attr.is_a?(Regexp) and susp_attr =~ attribute.to_s or susp_attr == attribute
28
28
  warn :model => name,
29
- :file => model[:file],
29
+ :file => model[:files].first,
30
30
  :warning_type => "Mass Assignment",
31
31
  :warning_code => :dangerous_attr_accessible,
32
32
  :message => "Potentially dangerous attribute available for mass assignment",
@@ -3,7 +3,7 @@ require 'brakeman/checks/base_check'
3
3
  #Check if mass assignment is used with models
4
4
  #which inherit from ActiveRecord::Base.
5
5
  #
6
- #If tracker.options[:collapse_mass_assignment] is +true+ (default), all models
6
+ #If tracker.options[:collapse_mass_assignment] is +true+ (default), all models
7
7
  #which do not use attr_accessible will be reported in a single warning
8
8
  class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
9
9
  Brakeman::Checks.add self
@@ -55,7 +55,7 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
55
55
  check_models do |name, model|
56
56
  if model[:options][:attr_protected].nil?
57
57
  warn :model => name,
58
- :file => model[:file],
58
+ :file => model[:files].first,
59
59
  :warning_type => "Attribute Restriction",
60
60
  :warning_code => :no_attr_accessible,
61
61
  :message => "Mass assignment is not restricted using attr_accessible",
@@ -70,7 +70,7 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
70
70
  end
71
71
 
72
72
  warn :model => name,
73
- :file => model[:file],
73
+ :file => model[:files].first,
74
74
  :line => model[:options][:attr_protected].first.line,
75
75
  :warning_type => "Attribute Restriction",
76
76
  :warning_code => warning_code,
@@ -60,7 +60,7 @@ class Brakeman::CheckModelSerialize < Brakeman::BaseCheck
60
60
  :message => "Serialized attributes are vulnerable in Rails #{tracker.config[:rails_version]}, upgrade to #{@upgrade_version} or patch.",
61
61
  :confidence => confidence,
62
62
  :link => "https://groups.google.com/d/topic/rubyonrails-security/KtmwSbEpzrU/discussion",
63
- :file => model[:file]
63
+ :file => model[:files].first
64
64
  end
65
65
  end
66
66
  end
@@ -19,6 +19,10 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
19
19
  @model_find_calls.merge [:from, :group, :having, :joins, :lock, :order, :reorder, :select, :where]
20
20
  end
21
21
 
22
+ if version_between? "4.0.0", "9.9.9"
23
+ @model_find_calls.merge [:find_by, :find_by!, :take]
24
+ end
25
+
22
26
  @tracker.find_call(:target => false, :method => :redirect_to).each do |res|
23
27
  process_result res
24
28
  end
@@ -33,7 +37,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
33
37
 
34
38
  if method == :redirect_to and
35
39
  not only_path?(call) and
36
- not explicit_host?(call) and
40
+ not explicit_host?(call.first_arg) and
37
41
  res = include_user_input?(call)
38
42
 
39
43
  add_result result
@@ -113,11 +117,21 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
113
117
  false
114
118
  end
115
119
 
116
- def explicit_host? call
117
- arg = call.first_arg
120
+ def explicit_host? arg
121
+ return unless sexp? arg
122
+
123
+ if hash? arg
124
+ if value = hash_access(arg, :host)
125
+ return !has_immediate_user_input?(value)
126
+ end
127
+ elsif call? arg
128
+ target = arg.target
118
129
 
119
- if hash? arg and value = hash_access(arg, :host)
120
- return !has_immediate_user_input?(value)
130
+ if hash? target and value = hash_access(target, :host)
131
+ return !has_immediate_user_input?(value)
132
+ elsif call? arg
133
+ return explicit_host? target
134
+ end
121
135
  end
122
136
 
123
137
  false
@@ -142,7 +156,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
142
156
  if node_type? exp, :or
143
157
  model_instance? exp.lhs or model_instance? exp.rhs
144
158
  elsif call? exp
145
- if model_name? exp.target or friendly_model? exp.target and
159
+ if model_target? exp and
146
160
  (@model_find_calls.include? exp.method or exp.method.to_s.match(/^find_by_/))
147
161
  true
148
162
  else
@@ -151,6 +165,13 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
151
165
  end
152
166
  end
153
167
 
168
+ def model_target? exp
169
+ return false unless call? exp
170
+ model_name? exp.target or
171
+ friendly_model? exp.target or
172
+ model_target? exp.target
173
+ end
174
+
154
175
  #Returns true if exp is (probably) a friendly model instance
155
176
  #using the FriendlyId gem
156
177
  def friendly_model? exp
@@ -8,11 +8,11 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
8
8
 
9
9
  def run_check
10
10
  tracker.find_call(:target => nil, :method => :render).each do |result|
11
- process_render result
11
+ process_render_result result
12
12
  end
13
13
  end
14
14
 
15
- def process_render result
15
+ def process_render_result result
16
16
  return unless node_type? result[:call], :render
17
17
 
18
18
  case result[:call].render_type