contrast-agent 5.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_kernel/cs__assess_kernel.c +7 -4
  3. data/ext/cs__assess_module/cs__assess_module.c +7 -7
  4. data/ext/cs__common/cs__common.c +4 -0
  5. data/ext/cs__common/cs__common.h +1 -0
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +52 -27
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +2 -0
  8. data/ext/cs__scope/cs__scope.c +747 -0
  9. data/ext/cs__scope/cs__scope.h +88 -0
  10. data/ext/cs__scope/extconf.rb +5 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +20 -13
  12. data/lib/contrast/agent/assess/contrast_object.rb +4 -1
  13. data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -5
  14. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +2 -0
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -1
  16. data/lib/contrast/agent/assess/rule/response/{autocomplete_rule.rb → auto_complete_rule.rb} +4 -3
  17. data/lib/contrast/agent/assess/rule/response/base_rule.rb +12 -79
  18. data/lib/contrast/agent/assess/rule/response/body_rule.rb +109 -0
  19. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +157 -0
  20. data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +26 -0
  21. data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +14 -15
  22. data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +5 -25
  23. data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +29 -0
  24. data/lib/contrast/agent/assess/rule/response/header_rule.rb +70 -0
  25. data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +12 -36
  26. data/lib/contrast/agent/assess/rule/response/parameters_pollution_rule.rb +2 -1
  27. data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +26 -0
  28. data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +36 -0
  29. data/lib/contrast/agent/middleware.rb +1 -0
  30. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +1 -3
  31. data/lib/contrast/agent/patching/policy/patch.rb +2 -6
  32. data/lib/contrast/agent/patching/policy/patcher.rb +1 -1
  33. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +94 -0
  34. data/lib/contrast/agent/protect/rule/base.rb +28 -1
  35. data/lib/contrast/agent/protect/rule/base_service.rb +10 -1
  36. data/lib/contrast/agent/protect/rule/cmd_injection.rb +2 -0
  37. data/lib/contrast/agent/protect/rule/deserialization.rb +6 -0
  38. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +5 -1
  39. data/lib/contrast/agent/protect/rule/no_sqli.rb +1 -0
  40. data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -0
  41. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +124 -0
  42. data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +121 -0
  43. data/lib/contrast/agent/protect/rule/sqli.rb +33 -0
  44. data/lib/contrast/agent/protect/rule/xxe.rb +4 -0
  45. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +44 -0
  46. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +115 -0
  47. data/lib/contrast/agent/reporting/input_analysis/input_type.rb +44 -0
  48. data/lib/contrast/agent/reporting/input_analysis/score_level.rb +21 -0
  49. data/lib/contrast/agent/reporting/report.rb +1 -0
  50. data/lib/contrast/agent/reporting/reporter.rb +8 -1
  51. data/lib/contrast/agent/reporting/reporting_events/finding.rb +69 -36
  52. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +88 -59
  53. data/lib/contrast/agent/reporting/reporting_events/{finding_object.rb → finding_event_object.rb} +24 -20
  54. data/lib/contrast/agent/reporting/reporting_events/finding_event_parent_object.rb +39 -0
  55. data/lib/contrast/agent/reporting/reporting_events/finding_event_property.rb +40 -0
  56. data/lib/contrast/agent/reporting/reporting_events/{finding_signature.rb → finding_event_signature.rb} +29 -24
  57. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +12 -8
  58. data/lib/contrast/agent/reporting/reporting_events/{finding_stack.rb → finding_event_stack.rb} +23 -19
  59. data/lib/contrast/agent/reporting/reporting_events/{finding_taint_range.rb → finding_event_taint_range.rb} +17 -15
  60. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +26 -53
  61. data/lib/contrast/agent/reporting/reporting_events/poll.rb +29 -0
  62. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +5 -4
  63. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +1 -0
  64. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +1 -1
  65. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +10 -3
  66. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +0 -1
  67. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -0
  68. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +28 -20
  69. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
  70. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +13 -1
  71. data/lib/contrast/agent/request_context.rb +6 -1
  72. data/lib/contrast/agent/request_context_extend.rb +85 -21
  73. data/lib/contrast/agent/scope.rb +102 -107
  74. data/lib/contrast/agent/service_heartbeat.rb +45 -2
  75. data/lib/contrast/agent/version.rb +1 -1
  76. data/lib/contrast/api/decorators/bot_blocker.rb +37 -0
  77. data/lib/contrast/api/decorators/ip_denylist.rb +37 -0
  78. data/lib/contrast/api/decorators/rasp_rule_sample.rb +29 -0
  79. data/lib/contrast/api/decorators/user_input.rb +11 -1
  80. data/lib/contrast/api/decorators/virtual_patch.rb +34 -0
  81. data/lib/contrast/components/logger.rb +5 -0
  82. data/lib/contrast/components/protect.rb +4 -2
  83. data/lib/contrast/components/scope.rb +98 -91
  84. data/lib/contrast/config/agent_configuration.rb +58 -12
  85. data/lib/contrast/config/api_configuration.rb +100 -12
  86. data/lib/contrast/config/api_proxy_configuration.rb +55 -3
  87. data/lib/contrast/config/application_configuration.rb +114 -15
  88. data/lib/contrast/config/assess_configuration.rb +106 -12
  89. data/lib/contrast/config/assess_rules_configuration.rb +44 -3
  90. data/lib/contrast/config/base_configuration.rb +1 -0
  91. data/lib/contrast/config/certification_configuration.rb +74 -3
  92. data/lib/contrast/config/exception_configuration.rb +61 -3
  93. data/lib/contrast/config/heap_dump_configuration.rb +101 -17
  94. data/lib/contrast/config/inventory_configuration.rb +64 -3
  95. data/lib/contrast/config/logger_configuration.rb +46 -3
  96. data/lib/contrast/config/protect_rule_configuration.rb +36 -9
  97. data/lib/contrast/config/protect_rules_configuration.rb +120 -17
  98. data/lib/contrast/config/request_audit_configuration.rb +68 -3
  99. data/lib/contrast/config/ruby_configuration.rb +96 -22
  100. data/lib/contrast/config/sampling_configuration.rb +76 -10
  101. data/lib/contrast/config/server_configuration.rb +56 -11
  102. data/lib/contrast/configuration.rb +6 -3
  103. data/lib/contrast/logger/cef_log.rb +151 -0
  104. data/lib/contrast/utils/hash_digest.rb +14 -6
  105. data/lib/contrast/utils/log_utils.rb +114 -0
  106. data/lib/contrast/utils/middleware_utils.rb +6 -7
  107. data/lib/contrast/utils/net_http_base.rb +12 -9
  108. data/lib/contrast/utils/patching/policy/patch_utils.rb +0 -4
  109. data/lib/contrast.rb +4 -3
  110. data/ruby-agent.gemspec +1 -1
  111. data/service_executables/VERSION +1 -1
  112. data/service_executables/linux/contrast-service +0 -0
  113. data/service_executables/mac/contrast-service +0 -0
  114. metadata +41 -21
  115. data/lib/contrast/agent/assess/rule/response/cachecontrol_rule.rb +0 -184
  116. data/lib/contrast/agent/assess/rule/response/clickjacking_rule.rb +0 -66
  117. data/lib/contrast/agent/assess/rule/response/x_content_type_rule.rb +0 -52
  118. data/lib/contrast/agent/assess/rule/response/x_xss_protection_rule.rb +0 -53
  119. data/lib/contrast/extension/kernel.rb +0 -54
@@ -16,28 +16,42 @@ module Contrast
16
16
  # relay this information in the Finding/Trace messages. These findings are used by TeamServer to construct the
17
17
  # vulnerability information for the assess feature. They represent those parts of the application, either through
18
18
  # configuration, method invocation, or dataflow, which are determined to be insecure.
19
- #
20
- # @attr_accessor events [Array<Contrast::Agent::Assess::ContrastEvent>] if a dataflow based finding, the
21
- # representation of those method calls which constitute a dangerous code path.
22
- # @attr_accessor properties [Hash<String,String>] a set of values that TeamServer can use to provide more context
23
- # to the user when rendering the finding. For some findings, a specific set of keys and values are required.
24
- # @attr_accessor request [Contrast::Agent::Request, nil] the request, if any, in which this finding occurred
25
- # @attr_accessor hash_code [String] the unique identifier of this finding.
26
- # @attr_reader rule_id [String] the name of the rule violated; must match those in TeamServer.
27
19
  class Finding < Contrast::Agent::Reporting::ReportingEvent
28
- attr_reader :rule_id, :routes, :events, :properties, :request
20
+ include Contrast::Components::Logger::InstanceMethods
21
+
22
+ # @return [Integer] the time, in ms, that this object was initialized
23
+ attr_reader :created
24
+ # @return [String] the ID of the rule associated with this finding
25
+ attr_reader :rule_id
26
+ # @return [Array<Contrast::Agent::Reporting::RouteDiscovery>] the routes associated with this finding, if the
27
+ # finding is request based.
28
+ attr_reader :routes
29
+ # @return [Array<Contrast::Agent::Reporting::FindingEvent>] the events associated with this finding, if the
30
+ # finding is event (dataflow) based.
31
+ attr_reader :events
32
+ # @return [String] the evidence associated with this finding, if the finding is event based. deprecated in
33
+ # favor of properties
34
+ # attr_reader :evidence
35
+ # @return [Hash<String,String>] properties that prove the violation of the rule for this finding
36
+ attr_reader :properties
37
+ # @return [Contrast::Agent::Reporting::FindingRequest] the request associated with this finding, if the finding
38
+ # is request based
39
+ attr_reader :request
40
+ # @return [String] the uniquely identifying hash of this finding
29
41
  attr_accessor :hash_code
30
42
 
43
+ CONFIGURATION_RULES = %w[rails-http-only-disabled secure-flag-missing session-timeout].cs__freeze
44
+ HARDCODED_RULES = %w[hardcoded-key hardcoded-password].cs__freeze
31
45
  PROPERTIES_RULES = %w[
32
46
  autocomplete-missing
33
47
  cache-controls-missing
34
48
  clickjacking-control-missing
35
- xcontenttype-header-missing
36
- hsts-header-missing
37
- xxssprotection-header-disabled
38
49
  csp-header-missing
39
50
  csp-header-insecure
51
+ hsts-header-missing
40
52
  parameter-pollution
53
+ xcontenttype-header-missing
54
+ xxssprotection-header-disabled
41
55
  ].cs__freeze
42
56
 
43
57
  class << self
@@ -57,6 +71,7 @@ module Contrast
57
71
  @routes = []
58
72
  @rule_id = Contrast::Utils::StringUtils.truncate(rule_id)
59
73
  @properties = {}
74
+ @created = Contrast::Utils::Timer.now_ms
60
75
  super()
61
76
  end
62
77
 
@@ -76,15 +91,16 @@ module Contrast
76
91
  # @param request [Contrast::Agent::Request]
77
92
  # @param args [Array<Object>] the Arguments with which the method was invoked
78
93
  def attach_data trigger_node, source, object, ret, request, *args
94
+ event_messages = Contrast::Agent::Reporting::FindingEvent.from_source(source)
95
+ events.concat(event_messages) if event_messages&.any?
79
96
  event_data = Contrast::Agent::Assess::Events::EventData.new(trigger_node, source, object, ret, args)
80
- event_msgs = Contrast::Agent::Reporting::FindingEvent.from_source(source)
81
- events.concat(event_msgs) if event_msgs
82
- if request
83
- @request = Contrast::Agent::Reporting::FindingRequest.convert(request)
84
- @routes << Contrast::Agent::Reporting::RouteDiscovery.convert(request&.route)
85
- end
86
- events << Contrast::Agent::Assess::ContrastEvent.new(event_data)
97
+ contrast_event = Contrast::Agent::Assess::ContrastEvent.new(event_data)
98
+ events << Contrast::Agent::Reporting::FindingEvent.convert(contrast_event)
87
99
  attach_properties
100
+ return unless request
101
+
102
+ @request = Contrast::Agent::Reporting::FindingRequest.convert(request)
103
+ @routes << Contrast::Agent::Reporting::RouteDiscovery.convert(request.route)
88
104
  end
89
105
 
90
106
  # Attach the data from a Contrast::Api::Dtm::Finding required for property based findings generated during
@@ -92,7 +108,6 @@ module Contrast
92
108
  #
93
109
  # @param finding_dtm [Contrast::Api::Dtm::Finding]
94
110
  def attach_property_data finding_dtm
95
- @created = Contrast::Utils::Timer.now_ms
96
111
  @hash_code = finding_dtm.hash_code
97
112
  @rule_id = finding_dtm.rule_id
98
113
  finding_dtm.properties.each_pair do |key, value|
@@ -101,7 +116,7 @@ module Contrast
101
116
  finding_dtm.routes.each do |route|
102
117
  @routes << Contrast::Agent::Reporting::RouteDiscovery.convert(route)
103
118
  end
104
- request = Contrast::Agent::REQUEST_TRACKER.current&.activity&.http_request
119
+ request = Contrast::Agent::REQUEST_TRACKER.current&.request
105
120
  @request = Contrast::Agent::Reporting::FindingRequest.convert(request) if request
106
121
  end
107
122
 
@@ -113,20 +128,20 @@ module Contrast
113
128
  def to_controlled_hash
114
129
  validate
115
130
  hsh = {
116
- created: @created,
117
- hash: hash_code,
118
- ruleId: @rule_id,
119
- session_id: @agent_session_id_value,
131
+ created: created,
132
+ hash: hash_code.to_s,
133
+ ruleId: rule_id,
134
+ session_id: @agent_session_id_value.to_s,
120
135
  version: 4
121
136
  }
122
- hsh[:events] = events if event_based?
123
- hsh[:evidence] = @evidence unless event_based? || property_based?
137
+ hsh[:events] = events.map(&:to_controlled_hash) if event_based?
138
+ # hsh[:evidence] = evidence unless event_based? || property_based?
124
139
  hsh[:properties] = properties if property_based?
125
140
  hsh[:tags] = Contrast::ASSESS.tags if Contrast::ASSESS.tags
126
- if request_based?
127
- hsh[:request] = request
128
- hsh[:routes] = routes
129
- end
141
+ return hsh unless request_based?
142
+
143
+ hsh[:request] = request.to_controlled_hash
144
+ hsh[:routes] = routes.map(&:to_controlled_hash)
130
145
  hsh
131
146
  end
132
147
 
@@ -135,14 +150,14 @@ module Contrast
135
150
  raise(ArgumentError, "#{ self } did not have a proper rule. Unable to continue.") unless @rule_id
136
151
 
137
152
  if event_based? && events.empty?
138
- raise(ArgumentError, "#{ self } did not have proper events. Unable to continue.")
153
+ raise(ArgumentError, "#{ self } did not have proper events for #{ @rule_id }. Unable to continue.")
139
154
  end
140
155
  if property_based? && properties.empty?
141
- raise(ArgumentError, "#{ self } did not have proper properties. Unable to continue.")
156
+ raise(ArgumentError, "#{ self } did not have proper properties for #{ @rule_id }. Unable to continue.")
142
157
  end
143
158
  return unless request_based? && request.nil?
144
159
 
145
- raise(ArgumentError, "#{ self } did not have a proper request. Unable to continue.")
160
+ raise(ArgumentError, "#{ self } did not have a proper request for #{ @rule_id }. Unable to continue.")
146
161
  end
147
162
 
148
163
  private
@@ -169,7 +184,7 @@ module Contrast
169
184
  #
170
185
  # @return [Boolean]
171
186
  def event_based?
172
- !property_based?
187
+ !property_based? && !config_based?
173
188
  end
174
189
 
175
190
  # Rules which are property based must have a property to be sent to TeamServer. Eventually, each rule may own
@@ -181,13 +196,31 @@ module Contrast
181
196
  PROPERTIES_RULES.include?(@rule_id)
182
197
  end
183
198
 
199
+ # Rules which are config based must have a configuration to be sent to TeamServer. Eventually, each rule may own
200
+ # its own validation, as the properties each needs are different; however, that's a refactor for after we've
201
+ # translated all rules from the Service and have had time to build proper child structure.
202
+ #
203
+ # @return [Boolean]
204
+ def config_based?
205
+ CONFIGURATION_RULES.include?(@rule_id)
206
+ end
207
+
208
+ # Rules which are hardcode based send properties to TeamServer. Eventually, each rule may own its own
209
+ # validation, as the properties each needs are different; however, that's a refactor for after we've
210
+ # translated all rules from the Service and have had time to build proper child structure.
211
+ #
212
+ # @return [Boolean]
213
+ def hardcoded?
214
+ HARDCODED_RULES.include?(@rule_id)
215
+ end
216
+
184
217
  # Rules which are request based must have a request to be sent to TeamServer. Most rules fit this category, so
185
218
  # we'll default to true for now. Eventually, this will be split out for those rules, like Hardcoded, which do
186
219
  # not need requests.
187
220
  #
188
221
  # @return [Boolean]
189
222
  def request_based?
190
- true
223
+ !config_based? && !hardcoded?
191
224
  end
192
225
  end
193
226
  end
@@ -1,44 +1,63 @@
1
1
  # Copyright (c) 2022 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/reporting/reporting_events/finding_event_object'
5
+ require 'contrast/agent/reporting/reporting_events/finding_event_parent_object'
6
+ require 'contrast/agent/reporting/reporting_events/finding_event_property'
7
+ require 'contrast/agent/reporting/reporting_events/finding_event_signature'
4
8
  require 'contrast/agent/reporting/reporting_events/finding_event_source'
5
- require 'contrast/agent/reporting/reporting_events/finding_object'
6
- require 'contrast/agent/reporting/reporting_events/finding_stack'
7
- require 'contrast/agent/reporting/reporting_events/finding_taint_range'
9
+ require 'contrast/agent/reporting/reporting_events/finding_event_stack'
10
+ require 'contrast/agent/reporting/reporting_events/finding_event_taint_range'
8
11
 
9
12
  module Contrast
10
13
  module Agent
11
14
  module Reporting
12
- # This is the new Finding class which will include all the needed information for the new reporting system to
13
- # relay this information in the Finding/Trace messages. These findings are used by TeamServer to construct the
14
- # vulnerability information for the assess feature. They represent those parts of the application, either through
15
- # configuration, method invocation, or dataflow, which are determined to be insecure.
16
- #
17
- # @attr_reader action [String] what the event did; CREATION, A2O, A2P, A2A, A2R, O2A, O2O, O2P, O2R, P2A, P2O,
18
- # P2P, P2R, TAG, TRIGGER.
19
- # @attr_reader args [Array<Contrast::Agent::Reporting::FindingObject>] the arguments passed to the method.
20
- # @attr_reader code [nil] unused.
21
- # @attr_reader event_id [Integer] the id of this event.
22
- # @attr_reader event_sources [Array<Contrast::Agent::Reporting::FindingEventSource>] the source of taint
23
- # @attr_reader field_name [nil] unused.
24
- # @attr_reader object [Contrast::Agent::Reporting::FindingEventSource] the object this method was invoked on.
25
- # @attr_reader parent_object_ids [Array<Integer>]
26
- # @attr_reader properties [Hash<String,String]
27
- # @attr_reader ret [Contrast::Agent::Reporting::FindingObject] the return of the method.
28
- # @attr_reader signature [Contrast::Agent::Reporting::FindingSignature] the signature of the method.
29
- # @attr_reader source [String] the source of the taint from the method; ^(O|R|P\d+)$
30
- # @attr_reader stack [Array<Contrast::Agent::Reporting::FindingStack>]
31
- # @attr_reader tags [Array<String>] description of what's happened to the data
32
- # @attr_reader taint_ranges [Array<Contrast::Agent::Reporting::FindingTaintRange>] the tags and spans of the
33
- # source that are tracked
34
- # @attr_reader target [String] the target of the taint from the method; ^(O|R|P\d+)$
35
- # @attr_reader thread [String] the id of the thread on which the method was invoked
36
- # @attr_reader time [Integer] the time, in ms, when the event was generated
37
- # @attr_reader type [String] the type of event; METHOD, PROPAGATION, TAG
15
+ # This is the new FindingEvent class which will include all the needed information for the new reporting system
16
+ # to relay this information in the Finding/Trace messages. These FindingEvents are used by TeamServer to
17
+ # construct the vulnerability information for the assess feature. They represent the operation the application
18
+ # underwent that transformed data during the dataflow.
38
19
  class FindingEvent
39
- attr_reader :action, :args, :code, :event_id, :event_sources, :field_name, :object, :parent_object_ids,
40
- :properties, :ret, :signature, :source, :stack, :tags, :taint_ranges, :target, :thread, :time,
41
- :type
20
+ # @return [Symbol] what the event did; CREATION, A2O, A2P, A2A, A2R, O2A, O2O, O2P, O2R, P2A, P2O, P2P, P2R,
21
+ # TAG, TRIGGER.
22
+ attr_reader :action
23
+ # @return [Array<Contrast::Agent::Reporting::FindingEventObject>] the arguments passed to the method.
24
+ attr_reader :args
25
+ # @return [nil] unused.
26
+ attr_reader :code
27
+ # @return [Integer] the id of this event.
28
+ attr_reader :event_id
29
+ # @return [Array<Contrast::Agent::Reporting::FindingEventSource>] the source of taint
30
+ attr_reader :event_sources
31
+ # @return [nil] unused.
32
+ attr_reader :field_name
33
+ # @return [Contrast::Agent::Reporting::FindingEventObject] the object this method was invoked on.
34
+ attr_reader :object
35
+ # @@return [Array<Contrast::Agent::Reporting::FindingEventParentObject>] the ids of all the events directly
36
+ # preceding this
37
+ attr_reader :parent_object_ids
38
+ # @return [Array<Contrast::Agent::Reporting::FindingEventProperty>]
39
+ attr_reader :properties
40
+ # @return [Contrast::Agent::Reporting::FindingEventObject] the return of the method.
41
+ attr_reader :ret
42
+ # @return [Contrast::Agent::Reporting::FindingEventSignature] the signature of the method.
43
+ attr_reader :signature
44
+ # @return [String] the source of the taint from the method; ^(O|R|P\d+)$
45
+ attr_reader :source
46
+ # @return [Array<Contrast::Agent::Reporting::FindingEventStack>]
47
+ attr_reader :stack
48
+ # @return [String] comma separated list of descriptions of what's happened to the data
49
+ attr_reader :tags
50
+ # @return [Array<Contrast::Agent::Reporting::FindingEventTaintRange>] the tags and spans of the source that are
51
+ # tracked
52
+ attr_reader :taint_ranges
53
+ # @return [String] the target of the taint from the method; ^(O|R|P\d+)$
54
+ attr_reader :target
55
+ # @return [String] the id of the thread on which the method was invoked
56
+ attr_reader :thread
57
+ # @return [Integer] the time, in ms, when the event was generated
58
+ attr_reader :time
59
+ # @return [String] the type of event; METHOD, PROPAGATION, TAG
60
+ attr_reader :type
42
61
 
43
62
  class << self
44
63
  # Find all the events leading up to the given source and return an array of FindingEvents
@@ -48,7 +67,7 @@ module Contrast
48
67
  def from_source source
49
68
  return unless source && (props = Contrast::Agent::Assess::Tracker.properties(source))
50
69
 
51
- build_events([], props.event) if props&.event
70
+ build_events([], props.event) if props.event
52
71
  end
53
72
 
54
73
  # @param event [Contrast::Agent::Assess::ContrastEvent]
@@ -71,7 +90,7 @@ module Contrast
71
90
  return unless event
72
91
 
73
92
  event.parent_events&.each do |parent_event|
74
- build_events(finding, parent_event)
93
+ build_events(events, parent_event)
75
94
  end
76
95
  events << convert(event)
77
96
  events
@@ -85,11 +104,11 @@ module Contrast
85
104
  def attach_data event
86
105
  @event_id = event.event_id
87
106
  @time = event.time.to_i
88
- @thread = event.thread
107
+ @thread = event.thread.to_s
89
108
  display_params!(event)
90
109
  dataflow!(event)
91
110
  event_sources!(event)
92
- @signature = Contrast::Agent::Reporting::FindingSignature.convert(event)
111
+ @signature = Contrast::Agent::Reporting::FindingEventSignature.convert(event)
93
112
  stack!(event)
94
113
  parent_ids!(event)
95
114
  properties!(event)
@@ -100,25 +119,25 @@ module Contrast
100
119
  #
101
120
  # @return [Hash]
102
121
  # @raise [ArgumentError]
103
- def to_controlled_hash
122
+ def to_controlled_hash # rubocop:disable Metrics/AbcSize
104
123
  validate
105
124
  {
106
125
  action: action,
107
- args: args,
126
+ args: args.map(&:to_controlled_hash),
108
127
  # code: code, # Unused by our agent
109
- eventId: event_id,
110
- eventSources: event_sources,
128
+ objectId: event_id,
129
+ eventSources: event_sources.map(&:to_controlled_hash),
111
130
  # fieldName: field_name, # Unused by our agent
112
- object: object,
113
- parentObjectIds: parent_object_ids,
114
- properties: properties,
115
- ret: ret,
116
- signature: signature,
117
- source: source,
118
- stack: stack,
119
- tags: tags,
120
- taintRanges: taint_ranges,
121
- target: target,
131
+ object: object.to_controlled_hash,
132
+ parentObjectIds: parent_object_ids.map(&:to_controlled_hash),
133
+ properties: properties.map(&:to_controlled_hash),
134
+ ret: ret&.to_controlled_hash,
135
+ signature: signature.to_controlled_hash,
136
+ source: source || '',
137
+ stack: stack.map(&:to_controlled_hash),
138
+ tags: tags.join(','),
139
+ taintRanges: taint_ranges.map(&:to_controlled_hash),
140
+ target: target || '',
122
141
  thread: thread,
123
142
  time: time,
124
143
  type: type
@@ -139,7 +158,14 @@ module Contrast
139
158
  # @param event [Contrast::Agent::Assess::ContrastEvent]
140
159
  def display_params! event
141
160
  @action = event.policy_node.build_action
142
- @type = event.policy_node.node_type
161
+ @type = case event.policy_node.node_type
162
+ when :TYPE_TAG
163
+ 'TAG'
164
+ when :TYPE_PROPAGATION
165
+ 'PROPAGATION'
166
+ else # :TYPE_METHOD
167
+ 'METHOD'
168
+ end
143
169
  end
144
170
 
145
171
  # Build the dataflow components of this FindingEvent.
@@ -148,9 +174,9 @@ module Contrast
148
174
  def dataflow! event
149
175
  taint_target = taint_target!(event)
150
176
  truncate_obj = Contrast::Utils::ObjectShare::OBJECT_KEY != taint_target
151
- @object = Contrast::Agent::Reporting::FindingObject.convert(event.object, truncate_obj)
177
+ @object = Contrast::Agent::Reporting::FindingEventObject.convert(event.object, truncate_obj)
152
178
  truncate_ret = Contrast::Utils::ObjectShare::RETURN_KEY != taint_target
153
- @ret = Contrast::Agent::Reporting::FindingObject.convert(event.ret, truncate_ret)
179
+ @ret = Contrast::Agent::Reporting::FindingEventObject.convert(event.ret, truncate_ret)
154
180
  event_args!(event, taint_target)
155
181
  taint_ranges!(event)
156
182
  end
@@ -164,7 +190,7 @@ module Contrast
164
190
  @args = []
165
191
  idx = 0
166
192
  while idx < event.args.length
167
- @args << Contrast::Agent::Reporting::FindingObject.convert(event.args[idx], taint_target != idx)
193
+ @args << Contrast::Agent::Reporting::FindingEventObject.convert(event.args[idx], taint_target != idx)
168
194
  idx += 1
169
195
  end
170
196
  end
@@ -176,7 +202,8 @@ module Contrast
176
202
  @event_sources = []
177
203
  return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
178
204
 
179
- event_sources << Contrast::Agent::Reporting::FindingEventSource.convert(event)
205
+ source = Contrast::Agent::Reporting::FindingEventSource.convert(event)
206
+ event_sources << source if source
180
207
  end
181
208
 
182
209
  # Convert the parent id's of the given ContrastEvent to the reportable form for this FindingEvent.
@@ -185,7 +212,7 @@ module Contrast
185
212
  def parent_ids! event
186
213
  @parent_object_ids = []
187
214
  event.parent_events&.each do |parent_event|
188
- parent_object_ids << parent_event.event_id.to_i
215
+ parent_object_ids << Contrast::Agent::Reporting::FindingEventParentObject.new(parent_event.event_id.to_i)
189
216
  end
190
217
  end
191
218
 
@@ -194,7 +221,7 @@ module Contrast
194
221
  #
195
222
  # @param _event [Contrast::Agent::Assess::ContrastEvent]
196
223
  def properties! _event
197
- @properties = {}
224
+ @properties = []
198
225
  end
199
226
 
200
227
  # Convert the stack of the given ContrastEvent to the reportable form for this FindingEvent.
@@ -203,7 +230,7 @@ module Contrast
203
230
  def stack! event
204
231
  @stack = []
205
232
  event.stack_trace.each do |stack_event|
206
- if (report = Contrast::Agent::Reporting::FindingStack.convert(stack_event))
233
+ if (report = Contrast::Agent::Reporting::FindingEventStack.convert(stack_event))
207
234
  stack << report
208
235
  end
209
236
  end
@@ -217,7 +244,9 @@ module Contrast
217
244
  @taint_ranges = []
218
245
  event&.tags&.each_pair do |tag_key, tag_ranges|
219
246
  tags << tag_key
220
- tag_ranges.each { |range| taint_ranges << Contrast::Agent::Reporting::FindingTaintRange.convert(range) }
247
+ tag_ranges.each do |range|
248
+ taint_ranges << Contrast::Agent::Reporting::FindingEventTaintRange.convert(range)
249
+ end
221
250
  end
222
251
  end
223
252
 
@@ -2,20 +2,22 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'base64'
5
+ require 'contrast/agent/assess/contrast_object'
5
6
 
6
7
  module Contrast
7
8
  module Agent
8
9
  module Reporting
9
- # This is the new FindingObject class which will include all the needed information for the new reporting system
10
- # to relay this information in the Finding/Trace messages. These FindingObjects are used by TeamServer to
11
- # construct the vulnerability information for the assess feature. They represent those parts of the objects that
12
- # were acted on in a Dataflow Finding.
13
- #
14
- # @attr_reader hash [String] the id of the Object this represents.
15
- # @attr_reader tracked [Boolean] if the Object this represents is tracked or not.
16
- # @attr_reader value [String] the base64 of the human readable representation of the Object this represents.
17
- class FindingObject
18
- attr_reader :hash, :tracked, :value
10
+ # This is the new FindingEventObject class which will include all the needed information for the new reporting
11
+ # system to relay this information in the Finding/Trace messages. These FindingEventObjects are used by
12
+ # TeamServer to construct the vulnerability information for the assess feature. They represent those parts of the
13
+ # objects that were acted on in a Dataflow Finding.
14
+ class FindingEventObject
15
+ # @return [Integer] the id of the Object this represents.
16
+ attr_reader :hash
17
+ # @return [Boolean] if the Object is tracked or not
18
+ attr_reader :tracked
19
+ # @return [String] the base64 of the human readable representation of the Object this represents.
20
+ attr_reader :value
19
21
 
20
22
  # We'll truncate any object that isn't important to the taint ranges of this event, so that we don't murder
21
23
  # TeamServer by, for instance, hypothetically sending the entire rendered HTML page >_> <_< >_>
@@ -25,8 +27,8 @@ module Contrast
25
27
 
26
28
  class << self
27
29
  # @param object [Contrast::Agent::Assess::ContrastObject] the object to translate
28
- # @param truncate [Boolean] if the value of this FindingObject should be truncated or not
29
- # @return [Contrast::Agent::Reporting::FindingObject]
30
+ # @param truncate [Boolean] if the value of this FindingEventObject should be truncated or not
31
+ # @return [Contrast::Agent::Reporting::FindingEventObject]
30
32
  def convert object, truncate
31
33
  report = new
32
34
  report.attach_data(object, truncate)
@@ -35,13 +37,13 @@ module Contrast
35
37
  end
36
38
 
37
39
  # Parse the data from a Contrast::Agent::Assess::ContrastObject to attach what is required for reporting to
38
- # TeamServer to this Contrast::Agent::Reporting::FindingObject
40
+ # TeamServer to this Contrast::Agent::Reporting::FindingEventObject
39
41
  #
40
- # @param object [Contrast::Agent::Assess::ContrastObject]
42
+ # @param object [Contrast::Agent::Assess::ContrastObject, nil]
41
43
  def attach_data object, truncate
42
- @hash = object.__id__
43
- @tracked = object.tracked?
44
- @value = reportable_value(object.object, truncate)
44
+ @hash = object&.object.__id__
45
+ @tracked = !!object&.tracked?
46
+ @value = reportable_value(object&.object, truncate)
45
47
  end
46
48
 
47
49
  # Convert the instance variables on the class, and other information, into the identifiers required for
@@ -59,9 +61,9 @@ module Contrast
59
61
  end
60
62
 
61
63
  def validate
62
- raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash && !hash.empty?
64
+ raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash
63
65
  raise(ArgumentError, "#{ self } did not have a proper tracked. Unable to continue.") if tracked.nil?
64
- return unless value && !value.empty?
66
+ return if value
65
67
 
66
68
  raise(ArgumentError, "#{ self } did not have a proper value. Unable to continue.")
67
69
  end
@@ -71,10 +73,12 @@ module Contrast
71
73
  # Parse, truncate, and translate the given value to be reported to TeamServer. The field is expected to be
72
74
  # base64 encoded.
73
75
  #
74
- # @param value [String] the contrast_string of the object this represents.
76
+ # @param value [String, nil] the contrast_string of the object this represents.
75
77
  # @param truncate [Boolean] if the string should be truncated or not.
76
78
  # @return [String]
77
79
  def reportable_value value, truncate
80
+ return Contrast::Utils::ObjectShare::NIL_STRING unless value
81
+
78
82
  if truncate && value.length > TRUNCATION_LENGTH
79
83
  tmp = []
80
84
  tmp << value[0, UNTRUNCATED_PORTION_LENGTH]
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'base64'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Reporting
9
+ # This is the new FindingEventParentObject class which will include all the needed information for the new
10
+ # reporting system to relay this information in the Finding/Trace messages. These FindingEventParentObject are
11
+ # used by TeamServer to relate this event to those that came previously. They represent the events that directly
12
+ # preceding the FindingEvent generated.
13
+ class FindingEventParentObject
14
+ # @return [Integer] the Id of the parent event
15
+ attr_reader :id
16
+
17
+ def initialize id
18
+ @id = id
19
+ end
20
+
21
+ # Convert the instance variables on the class, and other information, into the identifiers required for
22
+ # TeamServer to process the JSON form of this message.
23
+ #
24
+ # @return [Hash]
25
+ # @raise [ArgumentError]
26
+ def to_controlled_hash
27
+ validate
28
+ {
29
+ id: id
30
+ }
31
+ end
32
+
33
+ def validate
34
+ raise(ArgumentError, "#{ self } did not have a proper id. Unable to continue.") unless id
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Agent
6
+ module Reporting
7
+ # This is the new FindingEventProperty class which will include all the needed information for the new reporting
8
+ # system to relay this information in the Finding/Trace messages. Events have properties on them which are held
9
+ # as an array of key-value pairs.
10
+ class FindingEventProperty
11
+ # @return [String] the key of the property
12
+ attr_reader :key
13
+ # @return [String] the value of the source
14
+ attr_reader :value
15
+
16
+ def initialize key, value
17
+ @key = key
18
+ @value = value
19
+ end
20
+
21
+ # Convert the instance variables on the class, and other information, into the identifiers required for
22
+ # TeamServer to process the JSON form of this message.
23
+ #
24
+ # @return [Hash]
25
+ # @raise [ArgumentError]
26
+ def to_controlled_hash
27
+ validate
28
+ {
29
+ key: key,
30
+ value: value
31
+ }
32
+ end
33
+
34
+ def validate
35
+ raise(ArgumentError, "#{ self } did not have a proper key. Unable to continue.") unless key && !key.empty?
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end