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 +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
|