contrast-agent 3.15.0 → 3.16.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent.rb +2 -9
  3. data/lib/contrast/agent/assess/contrast_event.rb +142 -70
  4. data/lib/contrast/agent/assess/events/source_event.rb +1 -1
  5. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +10 -3
  6. data/lib/contrast/agent/assess/policy/policy_node.rb +15 -10
  7. data/lib/contrast/agent/assess/policy/policy_scanner.rb +7 -1
  8. data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -1
  9. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +0 -3
  10. data/lib/contrast/agent/assess/policy/propagator/select.rb +1 -3
  11. data/lib/contrast/agent/assess/policy/propagator/splat.rb +0 -5
  12. data/lib/contrast/agent/assess/policy/propagator/split.rb +12 -13
  13. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +21 -14
  14. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +4 -5
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +39 -14
  16. data/lib/contrast/agent/assess/policy/trigger_node.rb +31 -37
  17. data/lib/contrast/agent/assess/property/evented.rb +5 -18
  18. data/lib/contrast/agent/assess/property/tagged.rb +9 -3
  19. data/lib/contrast/agent/assess/property/updated.rb +0 -5
  20. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
  21. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +23 -8
  22. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +82 -14
  23. data/lib/contrast/agent/assess/tag.rb +1 -1
  24. data/lib/contrast/agent/at_exit_hook.rb +5 -5
  25. data/lib/contrast/agent/patching/policy/after_load_patch.rb +5 -5
  26. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +20 -20
  27. data/lib/contrast/agent/patching/policy/module_policy.rb +10 -10
  28. data/lib/contrast/agent/patching/policy/policy.rb +16 -2
  29. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +3 -5
  30. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
  31. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
  32. data/lib/contrast/agent/request.rb +34 -34
  33. data/lib/contrast/agent/static_analysis.rb +6 -6
  34. data/lib/contrast/agent/version.rb +1 -1
  35. data/lib/contrast/api/communication/socket_client.rb +36 -1
  36. data/lib/contrast/api/decorators/address.rb +13 -13
  37. data/lib/contrast/api/decorators/message.rb +1 -0
  38. data/lib/contrast/api/decorators/trace_event.rb +20 -18
  39. data/lib/contrast/components/app_context.rb +39 -30
  40. data/lib/contrast/components/contrast_service.rb +9 -9
  41. data/lib/contrast/components/settings.rb +20 -23
  42. data/lib/contrast/config/service_configuration.rb +4 -2
  43. data/lib/contrast/configuration.rb +1 -1
  44. data/lib/contrast/extension/assess/array.rb +7 -3
  45. data/lib/contrast/extension/assess/erb.rb +5 -0
  46. data/lib/contrast/extension/assess/eval_trigger.rb +6 -6
  47. data/lib/contrast/extension/assess/exec_trigger.rb +1 -1
  48. data/lib/contrast/extension/assess/fiber.rb +3 -3
  49. data/lib/contrast/extension/assess/hash.rb +3 -3
  50. data/lib/contrast/extension/assess/kernel.rb +18 -20
  51. data/lib/contrast/extension/assess/marshal.rb +8 -4
  52. data/lib/contrast/extension/assess/regexp.rb +3 -3
  53. data/lib/contrast/extension/assess/string.rb +13 -11
  54. data/lib/contrast/extension/protect/kernel.rb +3 -3
  55. data/lib/contrast/framework/base_support.rb +1 -1
  56. data/lib/contrast/framework/manager.rb +3 -3
  57. data/lib/contrast/framework/rack/patch/session_cookie.rb +9 -9
  58. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +13 -13
  59. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +10 -10
  60. data/lib/contrast/framework/rails/patch/support.rb +1 -1
  61. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +11 -11
  62. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +12 -12
  63. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -3
  64. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +12 -12
  65. data/lib/contrast/framework/sinatra/patch/base.rb +11 -11
  66. data/lib/contrast/framework/sinatra/support.rb +4 -4
  67. data/lib/contrast/logger/log.rb +7 -2
  68. data/lib/contrast/utils/invalid_configuration_util.rb +2 -5
  69. data/resources/assess/policy.json +31 -12
  70. data/ruby-agent.gemspec +4 -3
  71. data/service_executables/VERSION +1 -1
  72. data/service_executables/linux/contrast-service +0 -0
  73. data/service_executables/mac/contrast-service +0 -0
  74. metadata +31 -17
@@ -14,6 +14,7 @@ module ERBPropagator
14
14
  binding_variable_set = used_binding.local_variables
15
15
 
16
16
  erb_pre_result = preshift.object.src
17
+ parent_events = []
17
18
  binding_variable_set.each do |bound_var_symbol|
18
19
  bound_variable_value = used_binding.local_variable_get(bound_var_symbol)
19
20
  next unless Contrast::Agent::Assess::Tracker.tracked?(bound_variable_value)
@@ -23,6 +24,8 @@ module ERBPropagator
23
24
  next if start_index.nil?
24
25
 
25
26
  properties.copy_from(bound_variable_value, ret, start_index)
27
+ parent_event = Contrast::Agent::Assess::Tracker.properties(bound_variable_value)&.event
28
+ parent_events << parent_event if parent_event
26
29
  end
27
30
  properties.build_event(
28
31
  patcher,
@@ -31,6 +34,8 @@ module ERBPropagator
31
34
  ret,
32
35
  preshift.args,
33
36
  1)
37
+ properties.event.instance_variable_set(:@_parent_events, parent_events)
38
+
34
39
  ret
35
40
  end
36
41
  end
@@ -45,9 +45,9 @@ module Contrast
45
45
 
46
46
  def instrument_basic_object_track
47
47
  @_instrument_basic_object_track ||= begin
48
- require 'cs__assess_basic_object/cs__assess_basic_object'
49
- true
50
- end
48
+ require 'cs__assess_basic_object/cs__assess_basic_object'
49
+ true
50
+ end
51
51
  rescue StandardError, LoadError => e
52
52
  logger.error('Error loading basic object track patch', e)
53
53
  false
@@ -55,9 +55,9 @@ module Contrast
55
55
 
56
56
  def instrument_module_track
57
57
  @_instrument_module_track ||= begin
58
- require 'cs__assess_module/cs__assess_module'
59
- true
60
- end
58
+ require 'cs__assess_module/cs__assess_module'
59
+ true
60
+ end
61
61
  rescue StandardError, LoadError => e
62
62
  logger.error('Error loading module track patch', e)
63
63
  false
@@ -27,7 +27,7 @@ module Contrast
27
27
  source,
28
28
  Kernel,
29
29
  nil,
30
- source)
30
+ [source])
31
31
  # Exec replaces the current process, if we occur in a forked process
32
32
  # our appendage of this finding will not make it to TS
33
33
  Contrast::Agent::AtExitHook.on_exit
@@ -99,9 +99,9 @@ module Contrast
99
99
 
100
100
  def instrument_fiber_track
101
101
  @_instrument_fiber_variables ||= begin
102
- require 'cs__assess_fiber_track/cs__assess_fiber_track' if Funchook.available?
103
- true
104
- end
102
+ require 'cs__assess_fiber_track/cs__assess_fiber_track' if Funchook.available?
103
+ true
104
+ end
105
105
  rescue StandardError, LoadError => e
106
106
  logger.error('Error loading fiber track patch', e)
107
107
  false
@@ -26,9 +26,9 @@ module Contrast
26
26
 
27
27
  def instrument_hash_track
28
28
  @_instrument_hash_track ||= begin
29
- require 'cs__assess_hash/cs__assess_hash'
30
- true
31
- end
29
+ require 'cs__assess_hash/cs__assess_hash'
30
+ true
31
+ end
32
32
  rescue StandardError, LoadError => e
33
33
  logger.error('Error loading hash track patch', e)
34
34
  false
@@ -45,7 +45,8 @@ module Contrast
45
45
  format_string = preshift.args[0]
46
46
  args = preshift.args[1]
47
47
 
48
- track_sprintf(ret, format_string, args)
48
+ parent_events = []
49
+ track_sprintf(ret, format_string, args, parent_events)
49
50
 
50
51
  properties.build_event(
51
52
  patcher,
@@ -54,32 +55,31 @@ module Contrast
54
55
  ret,
55
56
  preshift.args,
56
57
  1)
58
+
59
+ properties.event.instance_variable_set(:@_parent_events, parent_events)
57
60
  ret
58
61
  end
59
62
 
60
- def track_sprintf result, format_string, args
61
- handle_sprintf_value(format_string, result)
63
+ def track_sprintf result, format_string, args, parent_events
64
+ handle_sprintf_value(format_string, result, parent_events)
62
65
  case args
63
66
  when String
64
- handle_sprintf_value(args, result)
67
+ handle_sprintf_value(args, result, parent_events)
65
68
  when Hash
66
- handle_sprintf_hash(args, result)
69
+ handle_sprintf_hash(args, result, parent_events)
67
70
  when Array
68
- handle_sprintf_array args, result
71
+ handle_sprintf_array(args, result, parent_events)
69
72
  end
70
-
71
- result
72
73
  rescue StandardError => e
73
74
  logger.error(
74
75
  'Unable to track dataflow through sprintf', e)
75
- result
76
76
  end
77
77
 
78
78
  def instrument_kernel_track
79
79
  @_instrument_fiber_variables ||= begin
80
- require 'cs__assess_kernel/cs__assess_kernel'
81
- true
82
- end
80
+ require 'cs__assess_kernel/cs__assess_kernel'
81
+ true
82
+ end
83
83
  rescue StandardError, LoadError => e
84
84
  logger.error('Error loading kernel track patch', e)
85
85
  false
@@ -87,28 +87,26 @@ module Contrast
87
87
 
88
88
  private
89
89
 
90
- def handle_sprintf_value value, result
90
+ def handle_sprintf_value value, result, parent_events
91
91
  properties = Contrast::Agent::Assess::Tracker.properties(result)
92
92
  return unless properties
93
93
 
94
94
  value_properties = Contrast::Agent::Assess::Tracker.properties(value)
95
95
  return unless value_properties
96
96
 
97
- value_properties.events.each do |event|
98
- properties.events << event
99
- end
97
+ parent_events << value_properties.event if value_properties.event
100
98
  properties.splat_from(value, result)
101
99
  end
102
100
 
103
- def handle_sprintf_array args, result
101
+ def handle_sprintf_array args, result, parent_events
104
102
  args.each do |value|
105
- handle_sprintf_value(value, result)
103
+ handle_sprintf_value(value, result, parent_events)
106
104
  end
107
105
  end
108
106
 
109
- def handle_sprintf_hash args, result
107
+ def handle_sprintf_hash args, result, parent_events
110
108
  args.each_value do |value|
111
- handle_sprintf_value(value, result)
109
+ handle_sprintf_value(value, result, parent_events)
112
110
  end
113
111
  end
114
112
  end
@@ -24,6 +24,7 @@ module Contrast
24
24
  # optimization here and return when it is not tracked
25
25
  return unless Contrast::Agent::Assess::Tracker.tracked?(source)
26
26
 
27
+ args = [source]
27
28
  # source might not be all the args passed in, but it is the one we care
28
29
  # about. we could pass in all the args in the last param here if it
29
30
  # becomes an issue in rendering on TS
@@ -33,18 +34,21 @@ module Contrast
33
34
  source,
34
35
  self,
35
36
  ret,
36
- source)
37
+ args)
37
38
  properties = Contrast::Agent::Assess::Tracker.properties(ret)
38
39
  properties.copy_from(source, ret)
40
+
41
+ node = Contrast::Agent::Assess::Policy::Policy.instance.find_propagator_node('Marshal', :load, false)
42
+ properties.build_event(node, ret, self, ret, args)
39
43
  rescue StandardError => e
40
44
  logger.error('Unable to determine if a trigger occurred in Marshal.load', e)
41
45
  end
42
46
 
43
47
  def instrument_marshal_load
44
48
  @_instrument_marshal_load ||= begin
45
- require 'cs__assess_marshal_module/cs__assess_marshal_module'
46
- true
47
- end
49
+ require 'cs__assess_marshal_module/cs__assess_marshal_module'
50
+ true
51
+ end
48
52
  rescue StandardError, LoadError => e
49
53
  logger.error('Error loading marshal load patch', e)
50
54
  false
@@ -71,9 +71,9 @@ module Contrast
71
71
 
72
72
  def instrument_regexp_track
73
73
  @_instrument_regexp_track ||= begin
74
- require 'cs__assess_regexp/cs__assess_regexp'
75
- true
76
- end
74
+ require 'cs__assess_regexp/cs__assess_regexp'
75
+ true
76
+ end
77
77
  rescue StandardError, LoadError => e
78
78
  logger.error('Error loading regexp track patch', e)
79
79
  false
@@ -39,12 +39,16 @@ module Contrast
39
39
  properties = Contrast::Agent::Assess::Tracker.properties(result)
40
40
  return unless properties
41
41
 
42
+ parent_events = []
42
43
  offset = 0
43
44
  inputs.each do |input|
44
45
  properties.copy_from(input, result, offset)
45
46
  offset += input.length
47
+ parent_event = Contrast::Agent::Assess::Tracker.properties(input)&.event
48
+ parent_events << parent_event if parent_event
46
49
  end
47
50
  properties.build_event(INTERPOLATION_NODE, result, inputs, result, inputs)
51
+ properties.event.instance_variable_set(:@_parent_events, parent_events)
48
52
  end
49
53
  rescue StandardError => e
50
54
  logger.error('Unable to track interpolation', e)
@@ -52,9 +56,9 @@ module Contrast
52
56
 
53
57
  def instrument_string
54
58
  @_instrument_string ||= begin
55
- require 'cs__assess_string/cs__assess_string'
56
- true
57
- end
59
+ require 'cs__assess_string/cs__assess_string'
60
+ true
61
+ end
58
62
  rescue StandardError, LoadError => e
59
63
  logger.error('Error loading hash track patch', e)
60
64
  false
@@ -63,14 +67,12 @@ module Contrast
63
67
  def instrument_string_interpolation
64
68
  if @_instrument_string_interpolation.nil?
65
69
  @_instrument_string_interpolation = begin
66
- if AGENT.patch_interpolation? && Funchook.available?
67
- require 'cs__assess_string_interpolation26/cs__assess_string_interpolation26'
68
- end
69
- true
70
- rescue StandardError, LoadError => e
71
- logger.error('Error loading interpolation patch', e)
72
- false
73
- end
70
+ require 'cs__assess_string_interpolation26/cs__assess_string_interpolation26' if AGENT.patch_interpolation? && Funchook.available?
71
+ true
72
+ rescue StandardError, LoadError => e
73
+ logger.error('Error loading interpolation patch', e)
74
+ false
75
+ end
74
76
  end
75
77
  @_instrument_string_interpolation
76
78
  end
@@ -30,9 +30,9 @@ module Contrast
30
30
 
31
31
  def instrument
32
32
  @_instrument ||= begin
33
- require 'cs__protect_kernel/cs__protect_kernel'
34
- true
35
- end
33
+ require 'cs__protect_kernel/cs__protect_kernel'
34
+ true
35
+ end
36
36
  rescue StandardError, LoadError => e
37
37
  logger.error('Error loading kernel protect patch', e)
38
38
  false
@@ -46,7 +46,7 @@ module Contrast
46
46
  #
47
47
  # By default, and hopefully in all cases, we won't need these patches,
48
48
  # so we're allowing nil here rather than raising an exception.
49
- def before_load_patches; end
49
+ def before_load_patches!; end
50
50
 
51
51
  # Some Frameworks require specific patching for their classes to handle
52
52
  # functionality like routing. To accommodate this, this method provides
@@ -39,9 +39,9 @@ module Contrast
39
39
  # configuration.
40
40
  def before_load_patches!
41
41
  @_before_load_patches ||= begin
42
- SUPPORTED_FRAMEWORKS.each(&:before_load_patches)
43
- true
44
- end
42
+ SUPPORTED_FRAMEWORKS.each(&:before_load_patches!)
43
+ true
44
+ end
45
45
  end
46
46
 
47
47
  # Return all the After Load Patches for all the Frameworks we know, even
@@ -24,15 +24,15 @@ module Contrast
24
24
 
25
25
  def instrument
26
26
  @_instrument ||= begin
27
- ::Rack::Session::Cookie.class_eval do
28
- alias_method :cs__patched_initialize, :initialize
29
- def initialize app, options = {}
30
- Contrast::Framework::Rack::Patch::SessionCookie.analyze(options)
31
- cs__patched_initialize(app, options)
32
- end
33
- end
34
- true
35
- end
27
+ ::Rack::Session::Cookie.class_eval do
28
+ alias_method :cs__patched_initialize, :initialize
29
+ def initialize app, options = {}
30
+ Contrast::Framework::Rack::Patch::SessionCookie.analyze(options)
31
+ cs__patched_initialize(app, options)
32
+ end
33
+ end
34
+ true
35
+ end
36
36
  end
37
37
 
38
38
  def analyze options
@@ -19,19 +19,19 @@ module Contrast
19
19
 
20
20
  def instrument
21
21
  @_instrument ||= begin
22
- ::ActionController::Live::Buffer.class_eval do
23
- # normally pre->in->post filters are applied however, in a streamed response
24
- # we can run into a case where it's pre -> in -> post -> more infilters
25
- # in order to submit anything found during the infilters after the response has
26
- # been written we need to explicitly send them
27
- alias_method :cs__close, :close
28
- def close
29
- Contrast::Framework::Rails::Patch::ActionControllerLiveBuffer.send_messages
30
- cs__close
31
- end
32
- end
33
- true
34
- end
22
+ ::ActionController::Live::Buffer.class_eval do
23
+ # normally pre->in->post filters are applied however, in a streamed response
24
+ # we can run into a case where it's pre -> in -> post -> more infilters
25
+ # in order to submit anything found during the infilters after the response has
26
+ # been written we need to explicitly send them
27
+ alias_method :cs__close, :close
28
+ def close
29
+ Contrast::Framework::Rails::Patch::ActionControllerLiveBuffer.send_messages
30
+ cs__close
31
+ end
32
+ end
33
+ true
34
+ end
35
35
  end
36
36
  end
37
37
  end
@@ -13,16 +13,16 @@ module Contrast
13
13
  class RailsApplicationConfiguration
14
14
  def self.instrument
15
15
  @_instrument ||= begin
16
- ::Rails::Application::Configuration.class_eval do
17
- alias_method :cs__patched_session_store, :session_store
18
- def session_store *args
19
- ret = cs__patched_session_store(*args)
20
- Contrast::Framework::Rails::Patch::AssessConfiguration.analyze_session_store(*args)
21
- ret
22
- end
23
- end
24
- true
25
- end
16
+ ::Rails::Application::Configuration.class_eval do
17
+ alias_method :cs__patched_session_store, :session_store
18
+ def session_store *args
19
+ ret = cs__patched_session_store(*args)
20
+ Contrast::Framework::Rails::Patch::AssessConfiguration.analyze_session_store(*args)
21
+ ret
22
+ end
23
+ end
24
+ true
25
+ end
26
26
  end
27
27
  end
28
28
  end
@@ -11,7 +11,7 @@ module Contrast
11
11
  # Extension point allowing for the registration of Patches required to
12
12
  # support the Rails framework.
13
13
  module Support
14
- # (See BaseSupport#before_load_patches)
14
+ # (See BaseSupport#before_load_patches!)
15
15
  def before_load_patches!
16
16
  return unless defined?(::Rails)
17
17
 
@@ -15,17 +15,17 @@ module Contrast
15
15
  class ActionControllerRailtiesHelperInherited
16
16
  def self.instrument
17
17
  @_instrument ||= begin
18
- ::ActionController::Railties::Helpers.class_eval do
19
- alias_method :cs__patched_helper_inherited, :inherited
20
- def inherited klass # rubocop:disable Lint/MissingSuper
21
- klass&.instance_variable_set(:@cs__defining_class, true)
22
- cs__patched_helper_inherited(klass) # This calls the original inherited, which should handle super as needed.
23
- ensure
24
- klass&.instance_variable_set(:@cs__defining_class, false)
25
- end
26
- end
27
- true
28
- end
18
+ ::ActionController::Railties::Helpers.class_eval do
19
+ alias_method :cs__patched_helper_inherited, :inherited
20
+ def inherited klass # rubocop:disable Lint/MissingSuper
21
+ klass&.instance_variable_set(:@cs__defining_class, true)
22
+ cs__patched_helper_inherited(klass) # This calls the original inherited, which should handle super as needed.
23
+ ensure
24
+ klass&.instance_variable_set(:@cs__defining_class, false)
25
+ end
26
+ end
27
+ true
28
+ end
29
29
  end
30
30
  end
31
31
  end
@@ -17,20 +17,20 @@ module Contrast
17
17
  class ActiveRecordAttributeMethodsRead
18
18
  def self.instrument
19
19
  @_instrument ||= begin
20
- ::ActiveRecord::AttributeMethods::Read::ClassMethods.class_eval do
21
- alias_method :cs__patched_define_method_attribute, :define_method_attribute
20
+ ::ActiveRecord::AttributeMethods::Read::ClassMethods.class_eval do
21
+ alias_method :cs__patched_define_method_attribute, :define_method_attribute
22
22
 
23
- def define_method_attribute *args, &block
24
- ret = cs__patched_define_method_attribute(*args, &block)
25
- method_name = args[0]
26
- Contrast::Agent::Assess::Policy::Patcher.patch_assess_method(self, method_name)
27
- ret
28
- end
23
+ def define_method_attribute *args, &block
24
+ ret = cs__patched_define_method_attribute(*args, &block)
25
+ method_name = args[0]
26
+ Contrast::Agent::Assess::Policy::Patcher.patch_assess_method(self, method_name)
27
+ ret
28
+ end
29
29
 
30
- protected :cs__patched_define_method_attribute, :define_method_attribute
31
- end
32
- true
33
- end
30
+ protected :cs__patched_define_method_attribute, :define_method_attribute
31
+ end
32
+ true
33
+ end
34
34
  end
35
35
  end
36
36
  end