brakeman-lib 3.4.0 → 3.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e27ccfd7420f1e8272480ef9ca0de23fee00b540
4
- data.tar.gz: 09ed85e36994255d6634bbcb7c8ca59e8790541b
3
+ metadata.gz: 41a53603e2fa56ad3a6fd895b93db05b1e532503
4
+ data.tar.gz: ec97788b6b989dd66c7dcc4698a86ec143a34eeb
5
5
  SHA512:
6
- metadata.gz: 117943f4633a23ef3a34286fdcb7f445594c2bee527a6dea48e1a9b77bdff5b0e24aa563d22703e8caf34c4094ee17cdf6a83492ad9f247bcd00473984c0588c
7
- data.tar.gz: c194c2535ff1db2743fcf66893d6d0dce2d5ff89ba7fc3a358c8a15564ea6de61bd0365f595051ab36eb6c6af987680fda94eba89aaca06d5d1085a584f31708
6
+ metadata.gz: ec25a806ebdb9aa3ad8842020a12a5b9b1e7c581b9bf4ead664ceeec9b2a00d6c63ffa71ad955e105660b8a39cb4a0db3ddf38a716ce3d7303438a2b5ee78076
7
+ data.tar.gz: d55e00d236e989175bace2779b3bf94390ec189542911ed408a73e07146076699d72ad6e2d1e3f560532e8cb43e10391a262a1900c302e8d7c52941d9d6121d3
data/CHANGES CHANGED
@@ -1,3 +1,13 @@
1
+ # 3.4.1
2
+
3
+ * Show action help at start of interactive ignore
4
+ * Check CSRF setting in direct subclasses of `ActionController::Base` (Jason Yeo)
5
+ * Configurable engines path (Jason Yeo)
6
+ * Use Ruby version to turn off SymbolDoS check
7
+ * Pull Ruby version from `.ruby-version` or Gemfile
8
+ * Avoid warning about `where_values_hash` in SQLi
9
+ * Fix ignoring link interpolation not at beginning of string
10
+
1
11
  # 3.4.0
2
12
 
3
13
  * Add new `plain` report format
data/lib/brakeman.rb CHANGED
@@ -146,7 +146,8 @@ module Brakeman
146
146
  :relative_path => false,
147
147
  :report_progress => true,
148
148
  :html_style => "#{File.expand_path(File.dirname(__FILE__))}/brakeman/format/style.css",
149
- :output_color => true
149
+ :output_color => true,
150
+ :engine_paths => ["engines/*"]
150
151
  }
151
152
  end
152
153
 
@@ -19,6 +19,7 @@ module Brakeman
19
19
  init_options[:only_files] = regex_for_paths(options[:only_files])
20
20
  end
21
21
  init_options[:additional_libs_path] = options[:additional_libs_path]
22
+ init_options[:engine_paths] = options[:engine_paths]
22
23
  new(root, init_options)
23
24
  end
24
25
 
@@ -57,6 +58,9 @@ module Brakeman
57
58
  @skip_files = init_options[:skip_files]
58
59
  @only_files = init_options[:only_files]
59
60
  @additional_libs_path = init_options[:additional_libs_path] || []
61
+ @engine_paths = init_options[:engine_paths] || []
62
+ @absolute_engine_paths = @engine_paths.select { |path| path.start_with?(File::SEPARATOR) }
63
+ @relative_engine_paths = @engine_paths - @absolute_engine_paths
60
64
  end
61
65
 
62
66
  def expand_path(path)
@@ -101,8 +105,7 @@ module Brakeman
101
105
  end
102
106
 
103
107
  def layout_exists?(name)
104
- pattern = "#{@root}/{engines/*/,}app/views/layouts/#{name}.html.{erb,haml,slim}"
105
- !Dir.glob(pattern).empty?
108
+ !Dir.glob("#{root_search_pattern}app/views/layouts/#{name}.html.{erb,haml,slim}").empty?
106
109
  end
107
110
 
108
111
  def lib_paths
@@ -121,10 +124,14 @@ module Brakeman
121
124
  @additional_libs_path.collect{ |path| find_paths path }.flatten
122
125
  end
123
126
 
124
- def find_paths(directory, extensions = "*.rb")
125
- pattern = @root + "/{engines/*/,}#{directory}/**/#{extensions}"
127
+ def find_paths(directory, extensions = ".rb")
128
+ select_files(glob_files(directory, "*", extensions).sort)
129
+ end
130
+
131
+ def glob_files(directory, name, extensions = ".rb")
132
+ pattern = "#{root_search_pattern}#{directory}/**/#{name}#{extensions}"
126
133
 
127
- select_files(Dir.glob(pattern).sort)
134
+ Dir.glob(pattern)
128
135
  end
129
136
 
130
137
  def select_files(paths)
@@ -160,5 +167,15 @@ module Brakeman
160
167
 
161
168
  files.match(project_relative_path)
162
169
  end
170
+
171
+ def root_search_pattern
172
+ return @root_search_pattern if @root_search_pattern
173
+ abs = @absolute_engine_paths.to_a.map { |path| path.gsub /#{File::SEPARATOR}+$/, '' }
174
+ rel = @relative_engine_paths.to_a.map { |path| path.gsub /#{File::SEPARATOR}+$/, '' }
175
+
176
+ roots = ([@root] + abs).join(",")
177
+ rel_engines = (rel + [""]).join("/,")
178
+ @root_search_patrern = "{#{roots}}/{#{rel_engines}}"
179
+ end
163
180
  end
164
181
  end
@@ -7,41 +7,41 @@ require 'brakeman/checks/base_check'
7
7
  class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
8
8
  Brakeman::Checks.add self
9
9
 
10
- @description = "Verifies that protect_from_forgery is enabled in ApplicationController"
10
+ @description = "Verifies that protect_from_forgery is enabled in direct subclasses of ActionController::Base"
11
11
 
12
12
  def run_check
13
- app_controller = tracker.controllers[:ApplicationController]
14
- return unless app_controller and app_controller.ancestor? :"ActionController::Base"
15
-
16
- if tracker.config.allow_forgery_protection?
17
- csrf_warning :warning_code => :csrf_protection_disabled,
18
- :message => "Forgery protection is disabled"
19
-
20
- elsif app_controller and not app_controller.protect_from_forgery?
21
- csrf_warning :warning_code => :csrf_protection_missing,
22
- :message => "'protect_from_forgery' should be called in ApplicationController",
23
- :line => app_controller.top_line
24
-
25
- elsif version_between? "2.1.0", "2.3.10"
26
- cve_2011_0447 "2.3.11"
27
-
28
- elsif version_between? "3.0.0", "3.0.3"
29
- cve_2011_0447 "3.0.4"
30
-
31
- elsif version_between? "4.0.0", "100.0.0" and forgery_opts = app_controller.options[:protect_from_forgery]
32
- unless forgery_opts.is_a?(Array) and sexp?(forgery_opts.first) and
13
+ tracker.controllers
14
+ .select { |_, controller| controller.parent == :"ActionController::Base" }
15
+ .each do |name, controller|
16
+ if controller and not controller.protect_from_forgery?
17
+ csrf_warning :controller => name,
18
+ :warning_code => :csrf_protection_missing,
19
+ :message => "'protect_from_forgery' should be called in #{name}",
20
+ :file => controller.file,
21
+ :line => controller.top_line
22
+ elsif version_between? "4.0.0", "100.0.0" and forgery_opts = controller.options[:protect_from_forgery]
23
+ unless forgery_opts.is_a?(Array) and sexp?(forgery_opts.first) and
33
24
  access_arg = hash_access(forgery_opts.first.first_arg, :with) and symbol? access_arg and
34
25
  access_arg.value == :exception
35
26
 
36
- args = {
37
- :warning_code => :csrf_not_protected_by_raising_exception,
38
- :message => "protect_from_forgery should be configured with 'with: :exception'",
39
- :confidence => CONFIDENCE[:med]
40
- }
27
+ args = {
28
+ :controller => name,
29
+ :warning_type => "Cross-Site Request Forgery",
30
+ :warning_code => :csrf_not_protected_by_raising_exception,
31
+ :message => "protect_from_forgery should be configured with 'with: :exception'",
32
+ :confidence => CONFIDENCE[:med],
33
+ :file => controller.file
34
+ }
35
+
36
+ args.merge!(:code => forgery_opts.first) if forgery_opts.is_a?(Array)
41
37
 
42
- args.merge!(:code => forgery_opts.first) if forgery_opts.is_a?(Array)
38
+ csrf_warning args
39
+ end
43
40
 
44
- csrf_warning args
41
+ end
42
+
43
+ if controller.options[:protect_from_forgery]
44
+ check_cve_2011_0447
45
45
  end
46
46
  end
47
47
  end
@@ -50,14 +50,26 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
50
50
  opts = {
51
51
  :controller => :ApplicationController,
52
52
  :warning_type => "Cross-Site Request Forgery",
53
- :confidence => CONFIDENCE[:high],
54
- :file => tracker.controllers[:ApplicationController].file
53
+ :confidence => CONFIDENCE[:high]
55
54
  }.merge opts
56
55
 
57
56
  warn opts
58
57
  end
59
58
 
60
- def cve_2011_0447 new_version
59
+ def check_cve_2011_0447
60
+ @warned_cve_2011_0447 ||= false
61
+ return if @warned_cve_2011_0447
62
+
63
+ if version_between? "2.1.0", "2.3.10"
64
+ new_version = "2.3.11"
65
+ elsif version_between? "3.0.0", "3.0.3"
66
+ new_version = "3.0.4"
67
+ else
68
+ return
69
+ end
70
+
71
+ @warned_cve_2011_0447 = true # only warn once
72
+
61
73
  csrf_warning :warning_code => :CVE_2011_0447,
62
74
  :message => "CSRF protection is flawed in unpatched versions of Rails #{rails_version} (CVE-2011-0447). Upgrade to #{new_version} or apply patches as needed",
63
75
  :gem_info => gemfile_or_environment,
@@ -1,14 +1,14 @@
1
1
  require 'brakeman/checks/check_cross_site_scripting'
2
2
 
3
3
  #Checks for calls to link_to which pass in potentially hazardous data
4
- #to the second argument. While this argument must be html_safe to not break
5
- #the html, it must also be url safe as determined by calling a
6
- #:url_safe_method. This prevents attacks such as javascript:evil() or
4
+ #to the second argument. While this argument must be html_safe to not break
5
+ #the html, it must also be url safe as determined by calling a
6
+ #:url_safe_method. This prevents attacks such as javascript:evil() or
7
7
  #data:<encoded XSS> which is html_safe, but not safe as an href
8
8
  #Props to Nick Green for the idea.
9
9
  class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
10
10
  Brakeman::Checks.add self
11
-
11
+
12
12
  @description = "Checks to see if values used for hrefs are sanitized using a :url_safe_method to protect against javascript:/data: XSS"
13
13
 
14
14
  def run_check
@@ -23,7 +23,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
23
23
  @models = tracker.models.keys
24
24
  @inspect_arguments = tracker.options[:check_arguments]
25
25
 
26
- methods = tracker.find_call :target => false, :method => :link_to
26
+ methods = tracker.find_call :target => false, :method => :link_to
27
27
  methods.each do |call|
28
28
  process_result call
29
29
  end
@@ -40,19 +40,15 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
40
40
  url_arg = url_arg.first_arg
41
41
  end
42
42
 
43
- #Ignore situations where the href is an interpolated string
44
- #with something before the user input
45
- return if string_interp?(url_arg) && !url_arg[1].chomp.empty?
46
-
47
43
  return if call? url_arg and ignore_call? url_arg.target, url_arg.method
48
44
 
49
45
  if input = has_immediate_user_input?(url_arg)
50
46
  message = "Unsafe #{friendly_type_of input} in link_to href"
51
47
 
52
- unless duplicate? result or call_on_params? url_arg
48
+ unless duplicate? result or call_on_params? url_arg or ignore_interpolation? url_arg, input.match
53
49
  add_result result
54
50
  warn :result => result,
55
- :warning_type => "Cross Site Scripting",
51
+ :warning_type => "Cross Site Scripting",
56
52
  :warning_code => :xss_link_to_href,
57
53
  :message => message,
58
54
  :user_input => input,
@@ -61,19 +57,19 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
61
57
  end
62
58
  elsif has_immediate_model? url_arg or model_find_call? url_arg
63
59
 
64
- # Decided NOT warn on models. polymorphic_path is called it a model is
60
+ # Decided NOT warn on models. polymorphic_path is called it a model is
65
61
  # passed to link_to (which passes it to url_for)
66
62
 
67
63
  elsif array? url_arg
68
- # Just like models, polymorphic path/url is called if the argument is
69
- # an array
64
+ # Just like models, polymorphic path/url is called if the argument is
65
+ # an array
70
66
 
71
67
  elsif hash? url_arg
72
68
 
73
69
  # url_for uses the key/values pretty carefully and I don't see a risk.
74
70
  # IF you have default routes AND you accept user input for :controller
75
- # and :only_path, then MAYBE you could trigger a javascript:/data:
76
- # attack.
71
+ # and :only_path, then MAYBE you could trigger a javascript:/data:
72
+ # attack.
77
73
 
78
74
  elsif @matched
79
75
  if @matched.type == :model and not tracker.options[:ignore_model_output]
@@ -82,10 +78,10 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
82
78
  message = "Unsafe parameter value in link_to href"
83
79
  end
84
80
 
85
- if message and not duplicate? result
81
+ if message and not duplicate? result and not ignore_interpolation? url_arg, @matched.match
86
82
  add_result result
87
- warn :result => result,
88
- :warning_type => "Cross Site Scripting",
83
+ warn :result => result,
84
+ :warning_type => "Cross Site Scripting",
89
85
  :warning_code => :xss_link_to_href,
90
86
  :message => message,
91
87
  :user_input => @matched,
@@ -95,6 +91,24 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
95
91
  end
96
92
  end
97
93
 
94
+ #Ignore situations where the href is an interpolated string
95
+ #with something before the user input
96
+ def ignore_interpolation? arg, suspect
97
+ return unless string_interp? arg
98
+ return true unless arg[1].chomp.empty? # plain string before interpolation
99
+
100
+ first_interp = arg.find_nodes(:evstr).first
101
+ return unless first_interp
102
+
103
+ first_interp[1].deep_each do |e|
104
+ if suspect == e
105
+ return false
106
+ end
107
+ end
108
+
109
+ true
110
+ end
111
+
98
112
  def ignore_call? target, method
99
113
  decorated_model? method or super
100
114
  end
@@ -549,7 +549,9 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
549
549
  :quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array,
550
550
  :sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,
551
551
  :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,
552
- :to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix]
552
+ :to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix,
553
+ :where_values_hash
554
+ ]
553
555
 
554
556
  def safe_value? exp
555
557
  return true unless sexp? exp
@@ -9,6 +9,7 @@ class Brakeman::CheckSymbolDoS < Brakeman::BaseCheck
9
9
 
10
10
  def run_check
11
11
  return if rails_version and rails_version >= "5.0.0"
12
+ return if tracker.config.ruby_version >= "2.2"
12
13
 
13
14
  tracker.find_call(:methods => UNSAFE_METHODS, :nested => true).each do |result|
14
15
  check_unsafe_symbol_creation(result)
@@ -3,7 +3,7 @@ require 'brakeman/checks/base_check'
3
3
  # Checks for string interpolation and parameters in calls to
4
4
  # String#constantize, String#safe_constantize, Module#const_get and Module#qualified_const_get.
5
5
  #
6
- # Exploit examples at: http://blog.conviso.com.br/2013/02/exploiting-unsafe-reflection-in.html
6
+ # Exploit examples at: http://blog.conviso.com.br/exploiting-unsafe-reflection-in-rubyrails-applications/
7
7
  class Brakeman::CheckUnsafeReflection < Brakeman::BaseCheck
8
8
  Brakeman::Checks.add self
9
9
 
@@ -136,6 +136,11 @@ module Brakeman::Options
136
136
  options[:additional_libs_path].merge paths
137
137
  end
138
138
 
139
+ opts.on "--add-engines-path path1,path2,etc", Array, "Include these engines in the scan" do |paths|
140
+ options[:engine_paths] ||= Set.new
141
+ options[:engine_paths].merge paths
142
+ end
143
+
139
144
  opts.on "-t", "--test Check1,Check2,etc", Array, "Only run the specified checks" do |checks|
140
145
  checks.each_with_index do |s, index|
141
146
  if s[0,5] != "Check"
@@ -21,19 +21,26 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
21
21
  end
22
22
 
23
23
  def process_call exp
24
- if exp.target == nil and exp.method == :gem
25
- gem_name = exp.first_arg
26
- return exp unless string? gem_name
24
+ if exp.target == nil
25
+ if exp.method == :gem
26
+ gem_name = exp.first_arg
27
+ return exp unless string? gem_name
27
28
 
28
- gem_version = exp.second_arg
29
+ gem_version = exp.second_arg
29
30
 
30
- version = if string? gem_version
31
- gem_version.value
32
- else
33
- nil
34
- end
31
+ version = if string? gem_version
32
+ gem_version.value
33
+ else
34
+ nil
35
+ end
35
36
 
36
- @tracker.config.add_gem gem_name.value, version, @gemfile, exp.line
37
+ @tracker.config.add_gem gem_name.value, version, @gemfile, exp.line
38
+ elsif exp.method == :ruby
39
+ version = exp.first_arg
40
+ if string? version
41
+ @tracker.config.set_ruby_version version.value
42
+ end
43
+ end
37
44
  end
38
45
 
39
46
  exp
@@ -58,11 +58,13 @@ module Brakeman
58
58
  HighLine.new.choose do |m|
59
59
  m.choice "Inspect all warnings" do
60
60
  @skip_ignored = false
61
+ pre_show_help
61
62
  process_warnings
62
63
  end
63
64
 
64
65
  m.choice "Hide previously ignored warnings" do
65
66
  @skip_ignored = true
67
+ pre_show_help
66
68
  process_warnings
67
69
  end
68
70
 
@@ -92,7 +94,20 @@ module Brakeman
92
94
  m.choice "s"
93
95
  m.choice "q"
94
96
  m.choice "?" do
95
- say <<-HELP
97
+ show_help
98
+ "?"
99
+ end
100
+ end
101
+ end
102
+
103
+ def pre_show_help
104
+ say "-" * 20
105
+ say "Actions:", :cyan
106
+ show_help
107
+ end
108
+
109
+ def show_help
110
+ say <<-HELP
96
111
  i - Add warning to ignore list
97
112
  n - Add warning to ignore list and add note
98
113
  s - Skip this warning (will remain ignored or shown)
@@ -101,11 +116,7 @@ a - Ignore this warning and all remaining warnings
101
116
  k - Skip this warning and all remaining warnings
102
117
  q - Quit, do not update ignored warnings
103
118
  ? - Display this help
104
- HELP
105
-
106
- "?"
107
- end
108
- end
119
+ HELP
109
120
  end
110
121
 
111
122
  def penultimate_menu
@@ -108,6 +108,10 @@ class Brakeman::Scanner
108
108
  tracker.config.escape_html = true
109
109
  Brakeman.notify "[Notice] Escaping HTML by default"
110
110
  end
111
+
112
+ if @app_tree.exists? ".ruby-version"
113
+ tracker.config.set_ruby_version @app_tree.read ".ruby-version"
114
+ end
111
115
  end
112
116
 
113
117
  def process_config_file file
@@ -5,7 +5,7 @@ module Brakeman
5
5
  include Util
6
6
 
7
7
  attr_reader :rails, :tracker
8
- attr_accessor :rails_version
8
+ attr_accessor :rails_version, :ruby_version
9
9
  attr_writer :erubis, :escape_html
10
10
  attr_reader :gems
11
11
 
@@ -16,6 +16,7 @@ module Brakeman
16
16
  @settings = {}
17
17
  @escape_html = nil
18
18
  @erubis = nil
19
+ @ruby_version = ""
19
20
  end
20
21
 
21
22
  def allow_forgery_protection?
@@ -92,6 +93,14 @@ module Brakeman
92
93
  end
93
94
  end
94
95
 
96
+ def set_ruby_version version
97
+ return unless version.is_a? String
98
+
99
+ if version =~ /(\d+\.\d+\.\d+)/
100
+ self.ruby_version = $1
101
+ end
102
+ end
103
+
95
104
  def session_settings
96
105
  @rails[:action_controller] &&
97
106
  @rails[:action_controller][:session]
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "3.4.0"
2
+ Version = "3.4.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 3.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain:
11
11
  - brakeman-public_cert.pem
12
- date: 2016-09-07 00:00:00.000000000 Z
12
+ date: 2016-11-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -31,14 +31,14 @@ dependencies:
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: 3.8.1
34
+ version: 3.8.3
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: 3.8.1
41
+ version: 3.8.3
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: ruby2ruby
44
44
  requirement: !ruby/object:Gem::Requirement