brakeman 3.0.2 → 3.0.3

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