contrast-agent 4.1.0 → 4.2.0

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
  SHA256:
3
- metadata.gz: 17afcbc69a4ed7c1434a31d5d074d43d06bbbe5dc2a63ab73fb38a924634b620
4
- data.tar.gz: 3650e096bc067ef278d7566001a8bc45d86fd398199bc82f034c7065c4ad3246
3
+ metadata.gz: 713e0f9cd16184d4b5da094f0d05b006870f557edfa0fc0ac199bf1035acfe45
4
+ data.tar.gz: f37a1ab7901a7c42bf5dce897c6dd0218f4df40d057c8719e3027f802ecd3c52
5
5
  SHA512:
6
- metadata.gz: 90518dbf5524571ba773ecc5f2013ee67ab653f0feff52600e2c99591fca0b1090c42eb9484b8bb9621ec59633be34e0bc6acf9472a9c852f0ccaacffa5b7ba6
7
- data.tar.gz: 444c90ed99aea811bf633c5cd724847f3b82093ed4299946defd05050165501d753b55bcb520cf6fb99c69a4cc2d13457e68cbf882ce14222bc7900787497c6c
6
+ metadata.gz: f1bcf5495957815fbe1acc801e9cdc9567a1a25f657b425a335cfa1412054ecdf4f92662d0eef0b19bdc2a1c5295b758d19ed4adf4cb7f37ef67bfaf4b67262f
7
+ data.tar.gz: bd3e6f02d68c7eaa9385f6626e52b3fe16cecbac10d53f8c82dd01b733c9f39666183afd21df601318ed95c482338b786e13cb8f087c8076872cd5fb3f4cfad8
@@ -365,12 +365,18 @@ module Contrast
365
365
  unless target_module.instance_methods(false).include? underlying_method_name
366
366
  # alias_method may be private
367
367
  target_module.send(:alias_method, underlying_method_name, method_name)
368
+ # TODO: RUBY-1052
369
+ # rubocop:disable Kernel/DefineMethod
368
370
  target_module.send(:define_method, method_name, unbound_method.bind(target_module))
371
+ # rubocop:enable Kernel/DefineMethod
369
372
  end
370
373
  target_module.send(visibility, method_name) # e.g., module.private(:my_method)
371
374
  when :prepend
372
375
  prepending_module = Module.new
376
+ # TODO: RUBY-1052
377
+ # rubocop:disable Kernel/DefineMethod
373
378
  prepending_module.send(:define_method, method_name, unbound_method.bind(target_module))
379
+ # rubocop:enable Kernel/DefineMethod
374
380
  prepending_module.send(visibility, method_name)
375
381
  # This prepends to the singleton class (it patches a class method)
376
382
  target_module.prepend prepending_module
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'contrast/agent/at_exit_hook'
5
4
  require 'contrast/agent/protect/rule/base_service'
6
5
  require 'contrast/utils/stack_trace_utils'
7
6
  require 'contrast/utils/object_share'
@@ -45,11 +44,6 @@ module Contrast
45
44
  raise Contrast::SecurityException.new(
46
45
  self,
47
46
  "Command Injection rule triggered. Call to #{ classname }.#{ method } blocked.")
48
- ensure
49
- # Kernel#exec replaces the current process and does not go through
50
- # at_exit hooks. Kernel#` runs as a subshell - messages appended
51
- # here do not seem to be present in the original process.
52
- Contrast::Agent::AtExitHook.on_exit if %i[exec `].include?(method.to_sym)
53
47
  end
54
48
 
55
49
  def build_attack_with_match context, input_analysis_result, result, candidate_string, **kwargs
@@ -104,37 +98,27 @@ module Contrast
104
98
 
105
99
  def report_command_execution context, command, **kwargs
106
100
  return unless report_any_command_execution?
107
- return nil if protect_excluded_by_code?
101
+ return if protect_excluded_by_code?
108
102
 
109
103
  build_attack_with_match(context, nil, nil, command, **kwargs)
110
104
  end
111
105
 
112
106
  def find_probable_attacker context, potential_attack_string, ia_results, **kwargs
113
- result = nil
114
- if chained_command?(potential_attack_string) # this is probably an attack
115
- most_likely = nil
116
- ia_results.each do |input_analysis_result|
117
- next unless chained_command?(input_analysis_result.value)
118
-
119
- most_likely = input_analysis_result
120
- break
121
- end
122
- end
123
- return result unless most_likely
107
+ return unless chained_command?(potential_attack_string)
108
+
109
+ likely_attacker = ia_results.find { |input_analysis_result| chained_command?(input_analysis_result.value) }
110
+ return unless likely_attacker
124
111
 
125
- result ||= build_attack_with_match(
112
+ build_attack_with_match(
126
113
  context,
127
- most_likely,
128
- result,
114
+ likely_attacker,
115
+ nil,
129
116
  potential_attack_string,
130
117
  **kwargs)
131
- result
132
118
  end
133
119
 
134
120
  def chained_command? command
135
- return true if CHAINED_COMMAND_CHARS.match(command)
136
-
137
- false
121
+ CHAINED_COMMAND_CHARS.match(command)
138
122
  end
139
123
 
140
124
  # Part of the Hardening for Command Injection detection is the
@@ -15,67 +15,73 @@ module Contrast
15
15
  # Instead, we should say "If I'm already doing Contrast things, don't track
16
16
  # this"
17
17
  class Scope
18
- # The following %i[] list is the authoritative list
19
- # of scopes. If you define a new symbol here, you'll
20
- # get scope methods:
21
- # %i[monkey] ->
22
- # enter_monkey_scope!
23
- # exit_monkey_scope!
24
- # in_monkey_scope?
25
- # with_monkey_scope { special_monkey_function }
26
18
  SCOPE_LIST = %i[contrast deserialization].cs__freeze
27
19
 
28
- iv_list = SCOPE_LIST.map { |name| :"@#{ name }_scope" }
29
- define_method 'initialize' do
30
- iv_list.each do |iv_sym|
31
- instance_variable_set(iv_sym, 0)
32
- end
20
+ def initialize
21
+ instance_variable_set(:@contrast_scope, 0)
22
+ instance_variable_set(:@deserialization_scope, 0)
33
23
  end
34
24
 
35
- SCOPE_LIST.each do |name|
36
- iv_sym = :"@#{ name }_scope"
25
+ def in_contrast_scope?
26
+ instance_variable_get(:@contrast_scope).positive?
27
+ end
37
28
 
38
- define_method "in_#{ name }_scope?" do
39
- instance_variable_get(iv_sym).positive?
40
- end
29
+ def in_deserialization_scope?
30
+ instance_variable_get(:@deserialization_scope).positive?
31
+ end
41
32
 
42
- enter_method_sym = :"enter_#{ name }_scope!"
43
- define_method enter_method_sym do
44
- level = instance_variable_get(iv_sym)
45
- instance_variable_set(iv_sym, level + 1)
46
- end
33
+ def enter_contrast_scope!
34
+ level = instance_variable_get(:@contrast_scope)
35
+ instance_variable_set(:@contrast_scope, level + 1)
36
+ end
47
37
 
48
- exit_method_sym = :"exit_#{ name }_scope!"
49
- define_method exit_method_sym do
50
- # by design, can go below zero.
51
- # every exit/enter pair (regardless of series)
52
- # should cancel each other out.
53
- #
54
- # so we prefer this sequence:
55
- # scope = 0
56
- # exit = -1
57
- # enter = 0
58
- # enter = 1
59
- # exit = 0
60
- # scope = 0
61
- #
62
- # over this sequence:
63
- # scope = 0
64
- # exit = 0
65
- # enter = 1
66
- # enter = 2
67
- # exit = 1
68
- # scope = 1
69
- level = instance_variable_get(iv_sym)
70
- instance_variable_set(iv_sym, level - 1)
71
- end
38
+ def enter_deserialization_scope!
39
+ level = instance_variable_get(:@deserialization_scope)
40
+ instance_variable_set(:@deserialization_scope, level + 1)
41
+ end
72
42
 
73
- define_method "with_#{ name }_scope" do |*_args, &block|
74
- send enter_method_sym
75
- block.call
76
- ensure
77
- send exit_method_sym
78
- end
43
+ # Scope Exits...
44
+ # by design, can go below zero.
45
+ # every exit/enter pair (regardless of series)
46
+ # should cancel each other out.
47
+ #
48
+ # so we prefer this sequence:
49
+ # scope = 0
50
+ # exit = -1
51
+ # enter = 0
52
+ # enter = 1
53
+ # exit = 0
54
+ # scope = 0
55
+ #
56
+ # over this sequence:
57
+ # scope = 0
58
+ # exit = 0
59
+ # enter = 1
60
+ # enter = 2
61
+ # exit = 1
62
+ # scope = 1
63
+ def exit_contrast_scope!
64
+ level = instance_variable_get(:@contrast_scope)
65
+ instance_variable_set(:@contrast_scope, level - 1)
66
+ end
67
+
68
+ def exit_deserialization_scope!
69
+ level = instance_variable_get(:@deserialization_scope)
70
+ instance_variable_set(:@deserialization_scope, level - 1)
71
+ end
72
+
73
+ def with_contrast_scope
74
+ enter_contrast_scope!
75
+ yield
76
+ ensure
77
+ exit_contrast_scope!
78
+ end
79
+
80
+ def with_deserialization_scope
81
+ enter_deserialization_scope!
82
+ yield
83
+ ensure
84
+ exit_deserialization_scope!
79
85
  end
80
86
 
81
87
  # Dynamic versions of the above.
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '4.1.0'
6
+ VERSION = '4.2.0'
7
7
  end
8
8
  end
@@ -65,7 +65,8 @@ module Contrast
65
65
  end
66
66
 
67
67
  %w[name default description required_languages display].each do |field|
68
- define_method(field) { hsh[field].dup }
68
+ # TODO: RUBY-1052
69
+ define_method(field) { hsh[field].dup } # rubocop:disable Kernel/DefineMethod
69
70
  end
70
71
  end
71
72
 
@@ -42,18 +42,61 @@ module Contrast
42
42
  # For each instance method on a scope, define a forwarder
43
43
  # to the scope on the current execution context's scope.
44
44
 
45
- Contrast::Agent::Scope.public_instance_methods(false).each do |method_sym|
46
- define_method(method_sym) do |*args, &block|
47
- scope_for_current_ec.send(method_sym, *args, &block)
48
- end
49
- end
50
-
51
45
  def scope_for_current_ec
52
46
  MONITOR.synchronize do
53
47
  return EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new
54
48
  end
55
49
  end
56
50
 
51
+ def enter_contrast_scope!
52
+ scope_for_current_ec.enter_contrast_scope!
53
+ end
54
+
55
+ def enter_deserialization_scope!
56
+ scope_for_current_ec.enter_deserialization_scope!
57
+ end
58
+
59
+ def enter_scope! name
60
+ scope_for_current_ec.enter_scope! name
61
+ end
62
+
63
+ def exit_contrast_scope!
64
+ scope_for_current_ec.exit_contrast_scope!
65
+ end
66
+
67
+ def exit_deserialization_scope!
68
+ scope_for_current_ec.exit_deserialization_scope!
69
+ end
70
+
71
+ def exit_scope! name
72
+ scope_for_current_ec.exit_scope! name
73
+ end
74
+
75
+ def in_contrast_scope?
76
+ scope_for_current_ec.in_contrast_scope?
77
+ end
78
+
79
+ def in_deserialization_scope?
80
+ scope_for_current_ec.in_deserialization_scope?
81
+ end
82
+
83
+ def in_scope? name
84
+ scope_for_current_ec.in_scope? name
85
+ end
86
+
87
+ def with_contrast_scope
88
+ scope_for_current_ec.enter_contrast_scope!
89
+ yield
90
+ ensure
91
+ scope_for_current_ec.exit_contrast_scope!
92
+ end
93
+
94
+ def with_deserialization_scope
95
+ scope_for_current_ec.enter_deserialization_scope!
96
+ ensure
97
+ scope_for_current_ec.exit_deserialization_scope!
98
+ end
99
+
57
100
  # TODO: RUBY-572
58
101
  #
59
102
  # Current behavior is to no-op if we're not "in a request context".
@@ -57,19 +57,22 @@ module Contrast
57
57
  # Meta-define an accessor for each state attribute.
58
58
 
59
59
  PROTECT_STATE_ATTRS.each do |attr|
60
- define_method(attr) do
60
+ # TODO: RUBY-1052
61
+ define_method(attr) do # rubocop:disable Kernel/DefineMethod
61
62
  protect_state[attr]
62
63
  end
63
64
  end
64
65
 
65
66
  ASSESS_STATE_ATTRS.each do |attr|
66
- define_method(attr) do
67
+ # TODO: RUBY-1052
68
+ define_method(attr) do # rubocop:disable Kernel/DefineMethod
67
69
  assess_state[attr]
68
70
  end
69
71
  end
70
72
 
71
73
  APPLICATION_STATE_ATTRS.each do |attr|
72
- define_method(attr) do
74
+ # TODO: RUBY-1052
75
+ define_method(attr) do # rubocop:disable Kernel/DefineMethod
73
76
  application_state[attr]
74
77
  end
75
78
  end
@@ -35,7 +35,7 @@ module Contrast
35
35
  # operation happens in C, we have to do it here rather than rely on the
36
36
  # patch of our String append or concat methods.
37
37
  def cs__track_join ary, separator, ret
38
- return ret unless ary
38
+ return ret unless ary&.any? { |element| Contrast::Agent::Assess::Tracker.tracked?(element) }
39
39
  return ret if Contrast::Agent::Patching::Policy::Patch.skip_assess_analysis?
40
40
 
41
41
  with_contrast_scope do
@@ -28,9 +28,6 @@ module Contrast
28
28
  Kernel,
29
29
  nil,
30
30
  [source])
31
- # Exec replaces the current process, if we occur in a forked process
32
- # our appendage of this finding will not make it to TS
33
- Contrast::Agent::AtExitHook.on_exit
34
31
  end
35
32
 
36
33
  private
@@ -14,6 +14,7 @@ def self.add_authors spec
14
14
  donald.propst@contrastsecurity.com
15
15
  alex.macdonald@contrastsecurity.com
16
16
  mark.petersen@contrastsecurity.com
17
+ joshua.reed@contrastsecurity.com
17
18
  ]
18
19
  end
19
20
 
@@ -1 +1 @@
1
- 2.15.1
1
+ 2.16.0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contrast-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - galen.palmer@contrastsecurity.com
@@ -9,10 +9,11 @@ authors:
9
9
  - donald.propst@contrastsecurity.com
10
10
  - alex.macdonald@contrastsecurity.com
11
11
  - mark.petersen@contrastsecurity.com
12
+ - joshua.reed@contrastsecurity.com
12
13
  autorequire:
13
14
  bindir: exe
14
15
  cert_chain: []
15
- date: 2020-11-20 00:00:00.000000000 Z
16
+ date: 2020-12-18 00:00:00.000000000 Z
16
17
  dependencies:
17
18
  - !ruby/object:Gem::Dependency
18
19
  name: amazing_print
@@ -498,20 +499,20 @@ executables:
498
499
  - contrast_service
499
500
  extensions:
500
501
  - ext/cs__common/extconf.rb
501
- - ext/cs__contrast_patch/extconf.rb
502
502
  - ext/cs__assess_active_record_named/extconf.rb
503
- - ext/cs__assess_module/extconf.rb
504
- - ext/cs__assess_marshal_module/extconf.rb
505
- - ext/cs__assess_hash/extconf.rb
506
- - ext/cs__assess_regexp/extconf.rb
507
- - ext/cs__assess_string/extconf.rb
508
- - ext/cs__protect_kernel/extconf.rb
509
- - ext/cs__assess_string_interpolation26/extconf.rb
510
- - ext/cs__assess_kernel/extconf.rb
511
503
  - ext/cs__assess_fiber_track/extconf.rb
504
+ - ext/cs__assess_basic_object/extconf.rb
505
+ - ext/cs__contrast_patch/extconf.rb
512
506
  - ext/cs__assess_array/extconf.rb
507
+ - ext/cs__protect_kernel/extconf.rb
508
+ - ext/cs__assess_kernel/extconf.rb
509
+ - ext/cs__assess_regexp/extconf.rb
510
+ - ext/cs__assess_hash/extconf.rb
511
+ - ext/cs__assess_module/extconf.rb
512
+ - ext/cs__assess_string_interpolation26/extconf.rb
513
+ - ext/cs__assess_marshal_module/extconf.rb
513
514
  - ext/cs__assess_yield_track/extconf.rb
514
- - ext/cs__assess_basic_object/extconf.rb
515
+ - ext/cs__assess_string/extconf.rb
515
516
  extra_rdoc_files: []
516
517
  files:
517
518
  - ".clang-format"