brakeman 3.0.2 → 3.0.3

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: d05a06e338f3309b5430c67decda380b5bccd1e9
4
- data.tar.gz: 79f70db112bdbcfa605b841f8375adeff1480220
3
+ metadata.gz: e55f96d8f4f9e61787c157081f95ce9a7d184e28
4
+ data.tar.gz: b39b72c6ae5a2216355c6e894bffd4fb2728bc75
5
5
  SHA512:
6
- metadata.gz: 2b2d46615e3e2c2510db8d14eebea2c9da264eb2580e3307f2b7a176d4718447a637ba30898d009871df313dc775d9f0d0c0a17c8993db08b0cd1d947f8fb490
7
- data.tar.gz: f046fd2458e86d5986149e3b6af15b611e992cae6c9cb5b65624a56ea1ead3faddc55b04e989f0060cf6445e1f1cf2b2a912dc00172faf207f3ea4a88bf7f6ab
6
+ metadata.gz: 29fdaef9d8895acd4ba3a375f3c0bf2f8347d68d0e635cdf9f5e8d3e8757d7595f48abaa33fcfee6d8603c5f805900c23b6f99435fa8c04c7d143dedbe319de5
7
+ data.tar.gz: f260fb5a25e2c72ef69dbdc2dc6bacc0b11e68810233c4f51e168af4264d4bfa8d958633a684f69a6c3fe4ceb3fcceefdc390f8b5339fc616b39f00d44a7a716
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ # 3.0.3
2
+
3
+ * Ignore more Arel methods in SQL
4
+ * Warn about protect_from_forgery without exceptions (Neil Matatall)
5
+ * Handle lambdas as filters
6
+ * Ignore quoted_table_name in SQL (Gabriel Sobrinho)
7
+ * Warn about RCE and file access with `open`
8
+ * Handle array include? guard conditionals
9
+ * Do not ignore targets of `to_s` in SQL
10
+ * Add Rake task to exit with error code on warnings (masarakki)
11
+
1
12
  # 3.0.2
2
13
 
3
14
  * Alias process methods called in class scope on models
@@ -7,4 +7,11 @@ namespace :brakeman do
7
7
  files = args[:output_files].split(' ') if args[:output_files]
8
8
  Brakeman.run :app_path => ".", :output_files => files, :print_report => true
9
9
  end
10
+
11
+ desc "Check your code with Brakeman"
12
+ task :check do
13
+ require 'brakeman'
14
+ result = Brakeman.run app_path: '.', print_report: true
15
+ exit Brakeman::Warnings_Found_Exit_Code unless result.filtered_warnings.empty?
16
+ end
10
17
  end
@@ -22,10 +22,12 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
22
22
  Brakeman.debug "Finding system calls using ``"
23
23
  check_for_backticks tracker
24
24
 
25
+ check_open_calls
26
+
25
27
  Brakeman.debug "Finding other system calls"
26
28
  calls = tracker.find_call :targets => [:IO, :Open3, :Kernel, :'POSIX::Spawn', :Process, nil],
27
29
  :methods => [:capture2, :capture2e, :capture3, :exec, :pipeline, :pipeline_r,
28
- :pipeline_rw, :pipeline_start, :pipeline_w, :popen, :popen2, :popen2e,
30
+ :pipeline_rw, :pipeline_start, :pipeline_w, :popen, :popen2, :popen2e,
29
31
  :popen3, :spawn, :syscall, :system]
30
32
 
31
33
  Brakeman.debug "Processing system calls"
@@ -57,7 +59,7 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
57
59
  end
58
60
 
59
61
  warn :result => result,
60
- :warning_type => "Command Injection",
62
+ :warning_type => "Command Injection",
61
63
  :warning_code => :command_injection,
62
64
  :message => "Possible command injection",
63
65
  :code => call,
@@ -66,6 +68,30 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
66
68
  end
67
69
  end
68
70
 
71
+ def check_open_calls
72
+ tracker.find_call(:targets => [nil, :Kernel], :method => :open).each do |result|
73
+ if match = dangerous_open_arg?(result[:call].first_arg)
74
+ warn :result => result,
75
+ :warning_type => "Command Injection",
76
+ :warning_code => :command_injection,
77
+ :message => "Possible command injection in open()",
78
+ :user_input => match.match,
79
+ :confidence => CONFIDENCE[:high]
80
+ end
81
+ end
82
+ end
83
+
84
+ def dangerous_open_arg? exp
85
+ if node_type? exp, :string_interp, :dstr
86
+ # Check for input at start of string
87
+ exp[1] == "" and
88
+ node_type? exp[2], :evstr, :string_eval and
89
+ has_immediate_user_input? exp[2]
90
+ else
91
+ has_immediate_user_input? exp
92
+ end
93
+ end
94
+
69
95
  #Looks for calls using backticks such as
70
96
  #
71
97
  # `rm -rf #{params[:file]}`
@@ -12,6 +12,7 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
12
12
  methods = tracker.find_call :targets => [:Dir, :File, :IO, :Kernel, :"Net::FTP", :"Net::HTTP", :PStore, :Pathname, :Shell], :methods => [:[], :chdir, :chroot, :delete, :entries, :foreach, :glob, :install, :lchmod, :lchown, :link, :load, :load_file, :makedirs, :move, :new, :open, :read, :readlines, :rename, :rmdir, :safe_unlink, :symlink, :syscopy, :sysopen, :truncate, :unlink]
13
13
 
14
14
  methods.concat tracker.find_call :target => :YAML, :methods => [:load_file, :parse_file]
15
+ methods.concat tracker.find_call :target => nil, :method => [:open]
15
16
 
16
17
  Brakeman.debug "Finding calls to load()"
17
18
  methods.concat tracker.find_call :target => false, :method => :load
@@ -52,6 +52,24 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
52
52
  :confidence => CONFIDENCE[:high],
53
53
  :gem_info => gemfile_or_environment,
54
54
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/LZWjzCPgNmU/discussion"
55
+ elsif version_between? "4.0.0", "100.0.0" and forgery_opts = app_controller[:options][:protect_from_forgery]
56
+
57
+ unless forgery_opts.is_a?(Array) and sexp?(forgery_opts.first) and
58
+ access_arg = hash_access(forgery_opts.first.first_arg, :with) and access_arg.value == :exception
59
+
60
+ args = {
61
+ :controller => :ApplicationController,
62
+ :warning_type => "Cross-Site Request Forgery",
63
+ :warning_code => :csrf_not_protected_by_raising_exception,
64
+ :message => "protect_from_forgery should be configured with 'with: :exception'",
65
+ :confidence => CONFIDENCE[:med],
66
+ :file => app_controller[:files].first
67
+ }
68
+
69
+ args.merge!(:code => forgery_opts.first) if forgery_opts.is_a?(Array)
70
+
71
+ warn args
72
+ end
55
73
  end
56
74
  end
57
75
  end
@@ -431,6 +431,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
431
431
  unless IGNORE_METHODS_IN_SQL.include? exp.method
432
432
  if has_immediate_user_input? exp or has_immediate_model? exp
433
433
  exp
434
+ elsif exp.method == :to_s
435
+ find_dangerous_value exp.target, ignore_hash
434
436
  else
435
437
  check_call exp
436
438
  end
@@ -545,11 +547,11 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
545
547
  string_building? exp.first_arg
546
548
  end
547
549
 
548
- IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :to_i, :to_f,
550
+ IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :quoted_table_name, :to_i, :to_f,
549
551
  :sanitize_sql, :sanitize_sql_array, :sanitize_sql_for_assignment,
550
552
  :sanitize_sql_for_conditions, :sanitize_sql_hash,
551
553
  :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,
552
- :to_sql, :sanitize, :exists, :primary_key, :table_name_prefix, :table_name_suffix]
554
+ :to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix]
553
555
 
554
556
  def safe_value? exp
555
557
  return true unless sexp? exp
@@ -563,6 +565,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
563
565
  else
564
566
  IGNORE_METHODS_IN_SQL.include? exp.method or
565
567
  quote_call? exp or
568
+ arel? exp or
566
569
  exp.method.to_s.end_with? "_id"
567
570
  end
568
571
  when :if
@@ -586,6 +589,12 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
586
589
  end
587
590
  end
588
591
 
592
+ AREL_METHODS = [:not, :and, :or, :exists, :join_sources, :arel_table, :gt, :lt, :on, :eq, :eq_any, :where]
593
+
594
+ def arel? exp
595
+ call? exp and (AREL_METHODS.include? exp.method or arel? exp.target)
596
+ end
597
+
589
598
  #Check call for string building
590
599
  def check_call exp
591
600
  return unless call? exp
@@ -465,6 +465,19 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
465
465
  exp
466
466
  end
467
467
 
468
+ # Check if exp is a call to Array#include? on an array literal
469
+ # that contains all literal values. For example:
470
+ #
471
+ # [1, 2, "a"].include? x
472
+ #
473
+ def array_include_all_literals? exp
474
+ call? exp and
475
+ exp.method == :include? and
476
+ node_type? exp.target, :array and
477
+ exp.target.length > 1 and
478
+ exp.target.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
479
+ end
480
+
468
481
  #Sets @inside_if = true
469
482
  def process_if exp
470
483
  if @ignore_ifs.nil?
@@ -498,7 +511,17 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
498
511
  scope do
499
512
  @branch_env = env.current
500
513
  branch_index = 2 + i # s(:if, condition, then_branch, else_branch)
501
- exp[branch_index] = process_if_branch branch
514
+ if i == 0 and array_include_all_literals? condition
515
+ # If the condition is ["a", "b"].include? x
516
+ # set x to "a" inside the true branch
517
+ var = condition.first_arg
518
+ previous_value = env.current[var]
519
+ env.current[var] = condition.target[1]
520
+ exp[branch_index] = process_if_branch branch
521
+ env.current[var] = previous_value
522
+ else
523
+ exp[branch_index] = process_if_branch branch
524
+ end
502
525
  branch_scopes << env.current
503
526
  @branch_env = nil
504
527
  end
@@ -158,9 +158,17 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
158
158
  when :include
159
159
  @current_class[:includes] << class_name(first_arg) if @current_class
160
160
  when :before_filter, :append_before_filter, :before_action, :append_before_action
161
- @current_class[:options][:before_filters] << exp
161
+ if node_type? exp.first_arg, :iter
162
+ add_lambda_filter exp
163
+ else
164
+ @current_class[:options][:before_filters] << exp
165
+ end
162
166
  when :prepend_before_filter, :prepend_before_action
163
- @current_class[:options][:before_filters].unshift exp
167
+ if node_type? exp.first_arg, :iter
168
+ add_lambda_filter exp
169
+ else
170
+ @current_class[:options][:before_filters].unshift exp
171
+ end
164
172
  when :skip_before_filter, :skip_filter, :skip_before_action, :skip_action_callback
165
173
  @current_class[:options][:skip_filters] << exp
166
174
  when :layout
@@ -311,4 +319,25 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
311
319
  process before_filter_call
312
320
  exp
313
321
  end
322
+
323
+ def add_lambda_filter exp
324
+ # Convert into regular block call
325
+ e = exp.dup
326
+ lambda_node = e.delete_at(3)
327
+ result = Sexp.new(:iter, e).line(e.line)
328
+
329
+ # Add block arguments
330
+ if node_type? lambda_node[2], :args
331
+ result << lambda_node[2].last
332
+ else
333
+ result << s(:args)
334
+ end
335
+
336
+ # Add block contents
337
+ if sexp? lambda_node[3]
338
+ result << lambda_node[3]
339
+ end
340
+
341
+ add_fake_filter result
342
+ end
314
343
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "3.0.2"
2
+ Version = "3.0.3"
3
3
  end
@@ -87,6 +87,7 @@ module Brakeman::WarningCodes
87
87
  :CVE_2011_2932 => 83,
88
88
  :cross_site_scripting_inline => 84,
89
89
  :CVE_2014_7829 => 85,
90
+ :csrf_not_protected_by_raising_exception => 86,
90
91
  }
91
92
 
92
93
  def self.code name
@@ -29,6 +29,12 @@ class Brakeman::SexpProcessor
29
29
 
30
30
  attr_reader :env
31
31
 
32
+ # Cache process methods per class
33
+
34
+ def self.processors
35
+ @processors ||= {}
36
+ end
37
+
32
38
  ##
33
39
  # Creates a new SexpProcessor. Use super to invoke this
34
40
  # initializer from SexpProcessor subclasses, then use the
@@ -37,15 +43,14 @@ class Brakeman::SexpProcessor
37
43
 
38
44
  def initialize
39
45
  @expected = Sexp
40
-
41
- # we do this on an instance basis so we can subclass it for
42
- # different processors.
43
- @processors = {}
46
+ @processors = self.class.processors
44
47
  @context = []
45
48
 
46
- public_methods.each do |name|
47
- if name.to_s.start_with? "process_" then
48
- @processors[name[8..-1].to_sym] = name.to_sym
49
+ if @processors.empty?
50
+ public_methods.each do |name|
51
+ if name.to_s.start_with? "process_" then
52
+ @processors[name[8..-1].to_sym] = name.to_sym
53
+ end
49
54
  end
50
55
  end
51
56
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
@@ -30,7 +30,7 @@ cert_chain:
30
30
  bxoxp9KNxkO+709YwLO1rYfmcGghg8WV6MYz3PSHdlgWF4KrjRFc/00hXHqVk0Sf
31
31
  mREEv2LPwHH2SgpSSab+iawnX4l6lV8XcIrmp/HSMySsPVFBeOmB0c05LpEN8w==
32
32
  -----END CERTIFICATE-----
33
- date: 2015-03-09 00:00:00.000000000 Z
33
+ date: 2015-04-30 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: test-unit
@@ -345,7 +345,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
345
  version: '0'
346
346
  requirements: []
347
347
  rubyforge_project:
348
- rubygems_version: 2.2.2
348
+ rubygems_version: 2.4.5
349
349
  signing_key:
350
350
  specification_version: 4
351
351
  summary: Security vulnerability scanner for Ruby on Rails.
metadata.gz.sig CHANGED
Binary file