contrast-agent 4.0.0 → 4.3.2

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -0
  3. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +22 -10
  4. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +4 -3
  5. data/lib/contrast/agent/assess/contrast_event.rb +49 -130
  6. data/lib/contrast/agent/assess/contrast_object.rb +51 -0
  7. data/lib/contrast/agent/assess/events/source_event.rb +4 -9
  8. data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
  9. data/lib/contrast/agent/assess/policy/policy_node.rb +31 -59
  10. data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
  11. data/lib/contrast/agent/assess/policy/propagation_method.rb +13 -19
  12. data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
  13. data/lib/contrast/agent/assess/policy/propagator/append.rb +1 -2
  14. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
  15. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  16. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -3
  17. data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -2
  18. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
  19. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
  20. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
  21. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
  22. data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
  23. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
  24. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
  25. data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -4
  26. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -4
  27. data/lib/contrast/agent/assess/policy/propagator/split.rb +73 -117
  28. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +11 -11
  29. data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
  30. data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
  31. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -8
  32. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  33. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
  34. data/lib/contrast/agent/assess/property/tagged.rb +21 -15
  35. data/lib/contrast/agent/assess/rule/redos.rb +1 -1
  36. data/lib/contrast/agent/assess/tracker.rb +16 -18
  37. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
  38. data/lib/contrast/agent/middleware.rb +50 -1
  39. data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
  40. data/lib/contrast/agent/patching/policy/patch.rb +6 -0
  41. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
  42. data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
  43. data/lib/contrast/agent/protect/rule/base.rb +63 -14
  44. data/lib/contrast/agent/protect/rule/cmd_injection.rb +12 -28
  45. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
  46. data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
  47. data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
  48. data/lib/contrast/agent/protect/rule/sqli.rb +3 -3
  49. data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
  50. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
  51. data/lib/contrast/agent/reaction_processor.rb +1 -1
  52. data/lib/contrast/agent/response.rb +5 -5
  53. data/lib/contrast/agent/rewriter.rb +3 -3
  54. data/lib/contrast/agent/scope.rb +81 -55
  55. data/lib/contrast/agent/static_analysis.rb +13 -7
  56. data/lib/contrast/agent/version.rb +1 -1
  57. data/lib/contrast/api/decorators/library.rb +1 -0
  58. data/lib/contrast/api/decorators/library_usage_update.rb +1 -0
  59. data/lib/contrast/api/decorators/trace_event.rb +19 -31
  60. data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
  61. data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
  62. data/lib/contrast/api/decorators/user_input.rb +2 -1
  63. data/lib/contrast/common_agent_configuration.rb +2 -1
  64. data/lib/contrast/components/assess.rb +36 -0
  65. data/lib/contrast/components/interface.rb +5 -3
  66. data/lib/contrast/components/scope.rb +72 -6
  67. data/lib/contrast/components/settings.rb +6 -3
  68. data/lib/contrast/config/assess_configuration.rb +2 -1
  69. data/lib/contrast/extension/assess/array.rb +2 -3
  70. data/lib/contrast/extension/assess/erb.rb +1 -3
  71. data/lib/contrast/extension/assess/exec_trigger.rb +1 -4
  72. data/lib/contrast/extension/assess/fiber.rb +2 -3
  73. data/lib/contrast/extension/assess/hash.rb +4 -2
  74. data/lib/contrast/extension/assess/kernel.rb +1 -2
  75. data/lib/contrast/extension/assess/marshal.rb +34 -26
  76. data/lib/contrast/extension/assess/regexp.rb +3 -8
  77. data/lib/contrast/extension/assess/string.rb +1 -2
  78. data/lib/contrast/framework/base_support.rb +51 -53
  79. data/lib/contrast/framework/manager.rb +3 -2
  80. data/lib/contrast/framework/rack/patch/session_cookie.rb +2 -2
  81. data/lib/contrast/framework/rack/support.rb +2 -1
  82. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
  83. data/lib/contrast/framework/rails/patch/assess_configuration.rb +1 -1
  84. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
  85. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +1 -1
  86. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +1 -1
  87. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +1 -1
  88. data/lib/contrast/framework/rails/support.rb +2 -1
  89. data/lib/contrast/framework/sinatra/support.rb +3 -2
  90. data/lib/contrast/logger/application.rb +0 -3
  91. data/lib/contrast/utils/duck_utils.rb +1 -1
  92. data/lib/contrast/utils/heap_dump_util.rb +1 -1
  93. data/lib/contrast/utils/object_share.rb +3 -3
  94. data/lib/contrast/utils/preflight_util.rb +1 -1
  95. data/lib/contrast/utils/prevent_serialization.rb +1 -1
  96. data/lib/contrast/utils/resource_loader.rb +1 -1
  97. data/lib/contrast/utils/sha256_builder.rb +2 -2
  98. data/lib/contrast/utils/string_utils.rb +1 -1
  99. data/lib/contrast/utils/tag_util.rb +9 -13
  100. data/resources/assess/policy.json +9 -9
  101. data/resources/deadzone/policy.json +150 -0
  102. data/resources/protect/policy.json +12 -0
  103. data/ruby-agent.gemspec +10 -6
  104. data/service_executables/VERSION +1 -1
  105. data/service_executables/linux/contrast-service +0 -0
  106. data/service_executables/mac/contrast-service +0 -0
  107. metadata +69 -25
@@ -18,12 +18,16 @@ module Contrast
18
18
  end
19
19
 
20
20
  def external_entity?
21
- @_external_entity ||= begin
22
- return external_id?(@system_id) if @system_id
23
- return external_id?(@public_id) if @public_id
24
-
25
- false
21
+ if @_external_entity.nil?
22
+ @_external_entity ||= if @system_id
23
+ external_id?(@system_id)
24
+ elsif @public_id
25
+ external_id?(@public_id)
26
+ else
27
+ false
28
+ end
26
29
  end
30
+ @_external_entity
27
31
  end
28
32
 
29
33
  # <!ENTITY name SYSTEM "URI">
@@ -48,7 +52,7 @@ module Contrast
48
52
  UP_DIR_LINUX = '../'
49
53
  UP_DIR_WIN = '..\\'
50
54
  # we only use this against lowercase strings, removed A-Z for speed
51
- FILE_PATTERN_WINDOWS = /^[\\\\]*[a-z]{1,3}:.*/.cs__freeze
55
+ FILE_PATTERN_WINDOWS = /^\\*[a-z]{1,3}:.*/.cs__freeze
52
56
  def external_id? entity_id
53
57
  return false unless entity_id
54
58
 
@@ -22,7 +22,7 @@ module Contrast
22
22
  # @param application_settings [Contrast::Api::Settings::ApplicationSettings]
23
23
  # those settings which the Service has relayed from TeamServer.
24
24
  def self.process application_settings
25
- return nil unless application_settings&.reactions&.any?
25
+ return unless application_settings&.reactions&.any?
26
26
 
27
27
  application_settings.reactions.each do |reaction|
28
28
  # the enums are all uppercase, we need to downcase them before attempting to log
@@ -118,16 +118,16 @@ module Contrast
118
118
  # holds, wraps, or is the body of the Response
119
119
  # @return [nil, String] the content of the body
120
120
  def extract_body body
121
- return nil unless body
121
+ return unless body
122
122
 
123
123
  if defined?(Rack::File) && body.is_a?(Rack::File)
124
124
  # not sure what to do in this situation, so don't do anything.
125
125
  nil
126
126
  elsif body.is_a?(Rack::BodyProxy)
127
127
  handle_rack_body_proxy(body)
128
- elsif defined?(ActionDispatch::Response::RackBody) && body.is_a?(ActionDispatch::Response::RackBody)
129
- extract_body(body.body)
130
- elsif body.is_a?(Rack::Response)
128
+ elsif (defined?(ActionDispatch::Response::RackBody) && body.is_a?(ActionDispatch::Response::RackBody)) ||
129
+ body.is_a?(Rack::Response)
130
+
131
131
  extract_body(body.body)
132
132
  elsif Contrast::Utils::DuckUtils.quacks_to?(body, :each)
133
133
  acc = []
@@ -152,7 +152,7 @@ module Contrast
152
152
  end
153
153
 
154
154
  def read_or_string obj
155
- return nil unless obj
155
+ return unless obj
156
156
 
157
157
  if Contrast::Utils::DuckUtils.quacks_to?(obj, :read)
158
158
  tmp = obj.read
@@ -96,11 +96,11 @@ module Contrast
96
96
  return unless method_exists?(clazz, method_name, type)
97
97
 
98
98
  method_instance = method_instance(clazz, method_name, type)
99
- return nil if method_instance.nil?
99
+ return if method_instance.nil?
100
100
 
101
101
  location = method_instance.source_location
102
- return nil unless location_available?(location)
103
- return nil if opener.written_from_location?(location)
102
+ return unless location_available?(location)
103
+ return if opener.written_from_location?(location)
104
104
 
105
105
  opener.written_from_location!(location)
106
106
  opener.source_code(location, method_name)
@@ -15,67 +15,93 @@ 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
- SCOPE_LIST = %i[contrast deserialization].cs__freeze
27
-
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
18
+ SCOPE_LIST = %i[contrast deserialization split].cs__freeze
19
+
20
+ def initialize
21
+ @contrast_scope = 0
22
+ @deserialization_scope = 0
23
+ @split_scope = 0
33
24
  end
34
25
 
35
- SCOPE_LIST.each do |name|
36
- iv_sym = :"@#{ name }_scope"
26
+ def in_contrast_scope?
27
+ @contrast_scope.positive?
28
+ end
37
29
 
38
- define_method "in_#{ name }_scope?" do
39
- instance_variable_get(iv_sym).positive?
40
- end
30
+ def in_deserialization_scope?
31
+ @deserialization_scope.positive?
32
+ end
41
33
 
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
34
+ def in_split_scope?
35
+ @split_scope.positive?
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_contrast_scope!
39
+ @contrast_scope += 1
40
+ end
72
41
 
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
42
+ def enter_deserialization_scope!
43
+ @deserialization_scope += 1
44
+ end
45
+
46
+ def enter_split_scope!
47
+ @split_scope += 1
48
+ end
49
+
50
+ def split_scope_depth
51
+ @split_scope
52
+ end
53
+
54
+ # Scope Exits...
55
+ # by design, can go below zero.
56
+ # every exit/enter pair (regardless of series)
57
+ # should cancel each other out.
58
+ #
59
+ # so we prefer this sequence:
60
+ # scope = 0
61
+ # exit = -1
62
+ # enter = 0
63
+ # enter = 1
64
+ # exit = 0
65
+ # scope = 0
66
+ #
67
+ # over this sequence:
68
+ # scope = 0
69
+ # exit = 0
70
+ # enter = 1
71
+ # enter = 2
72
+ # exit = 1
73
+ # scope = 1
74
+ def exit_contrast_scope!
75
+ @contrast_scope -= 1
76
+ end
77
+
78
+ def exit_deserialization_scope!
79
+ @deserialization_scope -= 1
80
+ end
81
+
82
+ def exit_split_scope!
83
+ @split_scope -= 1
84
+ end
85
+
86
+ def with_contrast_scope
87
+ enter_contrast_scope!
88
+ yield
89
+ ensure
90
+ exit_contrast_scope!
91
+ end
92
+
93
+ def with_deserialization_scope
94
+ enter_deserialization_scope!
95
+ yield
96
+ ensure
97
+ exit_deserialization_scope!
98
+ end
99
+
100
+ def with_split_scope
101
+ enter_split_scope!
102
+ yield
103
+ ensure
104
+ exit_split_scope!
79
105
  end
80
106
 
81
107
  # Dynamic versions of the above.
@@ -17,14 +17,9 @@ module Contrast
17
17
  # report the already-loaded gems.
18
18
  def catchup
19
19
  @_catchup ||= begin
20
- with_contrast_scope do
21
- Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.catchup
22
- send_inventory_message
23
- true
24
- end
20
+ threaded_analysis!
21
+ true
25
22
  end
26
- rescue StandardError => e
27
- logger.warn('Unable to run post-initialization static analysis', e)
28
23
  end
29
24
 
30
25
  def send_inventory_message
@@ -35,6 +30,17 @@ module Contrast
35
30
  Contrast::Utils::InventoryUtil.append_db_config(app_update_msg)
36
31
  Contrast::Agent.messaging_queue.send_event_eventually(app_update_msg)
37
32
  end
33
+
34
+ private
35
+
36
+ def threaded_analysis!
37
+ Contrast::Agent::Thread.new do
38
+ Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.catchup
39
+ send_inventory_message
40
+ rescue StandardError => e
41
+ logger.warn('Unable to run post-initialization static analysis', e)
42
+ end
43
+ end
38
44
  end
39
45
  end
40
46
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '4.0.0'
6
+ VERSION = '4.3.2'
7
7
  end
8
8
  end
@@ -13,6 +13,7 @@ module Contrast
13
13
  def self.included klass
14
14
  klass.extend(ClassMethods)
15
15
  end
16
+
16
17
  # Used to add class methods to the Library class on inclusion of the decorator
17
18
  module ClassMethods
18
19
  def build digest, gem_specification
@@ -11,6 +11,7 @@ module Contrast
11
11
  def self.included klass
12
12
  klass.extend(ClassMethods)
13
13
  end
14
+
14
15
  # Used to add class methods to the LibraryUsageUpdate class on inclusion of the decorator
15
16
  module ClassMethods
16
17
  def build digest, files
@@ -15,44 +15,30 @@ module Contrast
15
15
 
16
16
  # Wrapper around build_event_object for the args array. Handles
17
17
  # tainting the correct argument.
18
- def build_event_args contrast_event, taint_target
18
+ def build_event_args! contrast_event, taint_target
19
19
  contrast_event.args.each_index do |idx|
20
20
  truncate_arg = taint_target != idx
21
21
  event_arg = Contrast::Api::Dtm::TraceEventObject.build(contrast_event.args[idx], truncate_arg)
22
22
  args << event_arg
23
23
  end
24
+ self
24
25
  end
25
26
 
26
- # TeamServer only supports one object's taint ranges at a time.
27
- # We'll find the taint ranges for the target and return those
28
- def build_taint_ranges contrast_event, taint_target
27
+ # TeamServer only supports one object's taint ranges at a time. We keep
28
+ # those tags on the event directly, so we just need to convert them to
29
+ # their DTM form in order to report this.
30
+ #
31
+ # @param contrast_event [Contrast::Agent::AssessContrastEvent]
32
+ def build_taint_ranges! contrast_event
29
33
  # If there's no taint_target, this isn't a dataflow trace, but a
30
34
  # trigger one
31
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless taint_target
35
+ return self unless contrast_event&.tags
32
36
 
33
- properties = case taint_target
34
- when Contrast::Utils::ObjectShare::OBJECT_KEY
35
- Contrast::Agent::Assess::Tracker.properties(contrast_event.object)
36
- when Contrast::Utils::ObjectShare::RETURN_KEY
37
- Contrast::Agent::Assess::Tracker.properties(contrast_event.ret)
38
- else
39
- target = contrast_event.args[taint_target]
40
- if target.is_a?(Hash)
41
- if contrast_event.policy_node&.targets&.any?
42
- Contrast::Agent::Assess::Tracker.properties(target[contrast_event.policy_node.targets[0]])
43
- else
44
- Contrast::Agent::Assess::Tracker.properties(target[contrast_event.policy_node.sources[0]])
45
- end
46
- else
47
- Contrast::Agent::Assess::Tracker.properties(target)
48
- end
49
- end
50
- return unless properties.tracked?
51
-
52
- self.taint_ranges += properties.tags_to_dtm
37
+ self.taint_ranges += Contrast::Api::Dtm::TraceTaintRange.build_for_event(contrast_event.tags)
38
+ self
53
39
  end
54
40
 
55
- def build_parent_ids contrast_event
41
+ def build_parent_ids! contrast_event
56
42
  contrast_event&.parent_events&.each do |event|
57
43
  next unless event
58
44
 
@@ -60,12 +46,14 @@ module Contrast
60
46
  parent.id = event.event_id.to_i
61
47
  parent_object_ids << parent
62
48
  end
49
+ self
63
50
  end
64
51
 
65
- def build_stack contrast_event
52
+ def build_stack! contrast_event
66
53
  # We delayed doing this as long as possible b/c it's expensive
67
54
  stack_dtms = Contrast::Utils::StackTraceUtils.build_assess_stack_array(contrast_event.stack_trace)
68
55
  self.stack += stack_dtms
56
+ self
69
57
  end
70
58
 
71
59
  # Class methods for TraceEvent
@@ -85,10 +73,10 @@ module Contrast
85
73
  event_dtm.object = Contrast::Api::Dtm::TraceEventObject.build(contrast_event.object, truncate_obj)
86
74
  truncate_ret = Contrast::Utils::ObjectShare::RETURN_KEY != taint_target
87
75
  event_dtm.ret = Contrast::Api::Dtm::TraceEventObject.build(contrast_event.ret, truncate_ret)
88
- event_dtm.build_event_args(contrast_event, taint_target)
89
- event_dtm.build_parent_ids(contrast_event)
90
- event_dtm.build_taint_ranges(contrast_event, taint_target)
91
- event_dtm.build_stack(contrast_event)
76
+ event_dtm.build_event_args!(contrast_event, taint_target)
77
+ event_dtm.build_parent_ids!(contrast_event)
78
+ event_dtm.build_taint_ranges!(contrast_event)
79
+ event_dtm.build_stack!(contrast_event)
92
80
  event_dtm.object_id = contrast_event.event_id.to_i
93
81
  event_dtm.signature = Contrast::Api::Dtm::TraceEventSignature.build(contrast_event.ret, contrast_event.policy_node, contrast_event.args)
94
82
  event_dtm
@@ -29,13 +29,21 @@ module Contrast
29
29
  ELLIPSIS = '...'
30
30
  UNTRUNCATED_PORTION_LENGTH = 25
31
31
  TRUNCATION_LENGTH = (UNTRUNCATED_PORTION_LENGTH * 2) + ELLIPSIS.length
32
- def build object, truncate
32
+
33
+ # Convert the given Object into a Contrast::Api::Dtm::TraceEventObject
34
+ #
35
+ # @param contrast_object [Contrast::Agent::Assess::ContrastObject, nil]
36
+ # the thing to convert, if any
37
+ # @param truncate [Boolean] if the converted object can/should be
38
+ # truncated.
39
+ # @return [Contrast::Api::Dtm::TraceEventObject]
40
+ def build contrast_object, truncate
33
41
  event_object = new
34
42
  with_contrast_scope do
35
- obj_string = Contrast::Utils::StringUtils.force_utf8(object)
43
+ obj_string = Contrast::Utils::StringUtils.force_utf8(contrast_object&.object)
36
44
  obj_string = truncate(obj_string) if truncate && obj_string.length > TRUNCATION_LENGTH
37
45
  event_object.value = Base64.encode64(obj_string)
38
- event_object.tracked = Contrast::Utils::Assess::TrackingUtil.tracked?(object)
46
+ event_object.tracked = contrast_object&.tracked?
39
47
  end
40
48
  event_object
41
49
  end
@@ -17,27 +17,49 @@ module Contrast
17
17
 
18
18
  # Class methods for TraceEventSignature
19
19
  module ClassMethods
20
+ # Convert the given composites into components that TeamServer can
21
+ # understand in order to build a representation of the method
22
+ # invoked.
23
+ #
24
+ # @param ret_obj [Contrast::Agent::Assess::ContrastObject, nil] the
25
+ # return value of the method call.
26
+ # @param policy_node [Contrast::Agent::Assess::Policy::PolicyNode]
27
+ # the policy which pertains to the method invoked.
28
+ # @param args [Array<Contrast::Agent::Assess::ContrastObject>, nil]
29
+ # @return [Contrast::Api::Dtm::TraceEventSignature]
20
30
  def build ret_obj, policy_node, args
21
31
  signature = new
22
- return_type = ret_obj ? ret_obj.cs__class.name : Contrast::Utils::ObjectShare::NIL_STRING
23
- signature.return_type = Contrast::Utils::StringUtils.force_utf8(return_type)
32
+ signature.return_type = Contrast::Utils::StringUtils.force_utf8(type_name(ret_obj))
24
33
  signature.class_name = Contrast::Utils::StringUtils.force_utf8(policy_node.class_name)
25
34
  signature.method_name = Contrast::Utils::StringUtils.force_utf8(policy_node.method_name)
26
35
  if args
27
36
  args&.each do |arg|
28
- arg_type = arg ? arg.cs__class.name : Contrast::Utils::ObjectShare::NIL_STRING
29
- signature.arg_types << Contrast::Utils::StringUtils.force_utf8(arg_type)
37
+ signature.arg_types << Contrast::Utils::StringUtils.force_utf8(type_name(arg))
30
38
  end
31
39
  end
32
40
  signature.constructor = policy_node.method_name == :new
33
41
  # if there's a ret, then this method isn't nil. not 100% full proof since you can
34
42
  # return nil, but this is the best we've got currently.
35
- signature.void_method = ret_obj.nil?
43
+ signature.void_method = ret_obj.nil? || ret_obj.object.nil?
36
44
  # 8 is STATIC in Java... we have to placate them for now
37
45
  # it has been requested that flags be removed since it isn't used
38
46
  signature.flags = 8 unless policy_node.instance_method?
39
47
  signature
40
48
  end
49
+
50
+ private
51
+
52
+ # While Ruby signatures do not require neither a return type and can
53
+ # return anything depending on inputs, code paths, etc, nor constant
54
+ # parameter types, TeamServer was designed with strongly typed
55
+ # languages (Java) in mind, so we need types in our signatures.
56
+ #
57
+ # @param contrast_object [Contrast::Agent::Assess::ContrastObject, nil]
58
+ # the object to find the type of
59
+ # @return [String] the name of the module of the ret_obj, or `nil`
60
+ def type_name contrast_object
61
+ contrast_object ? contrast_object.object_type : Contrast::Utils::ObjectShare::NIL_STRING
62
+ end
41
63
  end
42
64
  end
43
65
  end