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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +11 -0
- data/lib/brakeman/brakeman.rake +7 -0
- data/lib/brakeman/checks/check_execute.rb +28 -2
- data/lib/brakeman/checks/check_file_access.rb +1 -0
- data/lib/brakeman/checks/check_forgery_setting.rb +18 -0
- data/lib/brakeman/checks/check_sql.rb +11 -2
- data/lib/brakeman/processors/alias_processor.rb +24 -1
- data/lib/brakeman/processors/controller_processor.rb +31 -2
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning_codes.rb +1 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +12 -7
- metadata +3 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e55f96d8f4f9e61787c157081f95ce9a7d184e28
|
4
|
+
data.tar.gz: b39b72c6ae5a2216355c6e894bffd4fb2728bc75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/brakeman/brakeman.rake
CHANGED
@@ -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, :
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/brakeman/version.rb
CHANGED
@@ -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
|
-
|
47
|
-
|
48
|
-
|
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.
|
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-
|
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.
|
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
|