contrast-agent 6.6.5 → 6.7.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.gitmodules +0 -3
  4. data/ext/cs__scope/cs__scope.c +1 -1
  5. data/lib/contrast/agent/assess/contrast_event.rb +2 -24
  6. data/lib/contrast/agent/assess/events/source_event.rb +7 -61
  7. data/lib/contrast/agent/assess/finalizers/hash.rb +11 -0
  8. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +0 -55
  9. data/lib/contrast/agent/assess/policy/policy_node.rb +3 -3
  10. data/lib/contrast/agent/assess/policy/policy_node_utils.rb +0 -1
  11. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  12. data/lib/contrast/agent/assess/policy/source_method.rb +24 -1
  13. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +7 -5
  14. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +6 -1
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +36 -132
  16. data/lib/contrast/agent/assess/policy/trigger_node.rb +3 -3
  17. data/lib/contrast/agent/assess/property/evented.rb +2 -12
  18. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +42 -84
  19. data/lib/contrast/agent/assess/rule/response/base_rule.rb +11 -27
  20. data/lib/contrast/agent/assess/rule/response/body_rule.rb +1 -3
  21. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +77 -62
  22. data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +1 -1
  23. data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +6 -1
  24. data/lib/contrast/agent/assess/rule/response/header_rule.rb +5 -5
  25. data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +1 -1
  26. data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +1 -1
  27. data/lib/contrast/agent/assess/tracker.rb +1 -7
  28. data/lib/contrast/agent/excluder.rb +206 -0
  29. data/lib/contrast/agent/exclusion_matcher.rb +6 -0
  30. data/lib/contrast/agent/inventory/database_config.rb +6 -10
  31. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +4 -0
  32. data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +1 -0
  33. data/lib/contrast/agent/protect/rule/base.rb +49 -5
  34. data/lib/contrast/agent/protect/rule/base_service.rb +1 -0
  35. data/lib/contrast/agent/protect/rule/cmd_injection.rb +18 -105
  36. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +129 -0
  37. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +169 -0
  38. data/lib/contrast/agent/protect/rule/deserialization.rb +2 -1
  39. data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +51 -0
  40. data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +67 -0
  41. data/lib/contrast/agent/protect/rule/sqli.rb +6 -31
  42. data/lib/contrast/agent/protect/rule/xxe.rb +2 -0
  43. data/lib/contrast/agent/protect/rule.rb +3 -1
  44. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +6 -0
  45. data/lib/contrast/agent/reporting/details/sqli_dangerous_functions.rb +22 -0
  46. data/lib/contrast/agent/reporting/reporter.rb +1 -2
  47. data/lib/contrast/agent/reporting/reporting_events/agent_startup.rb +2 -2
  48. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +1 -4
  49. data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +1 -1
  50. data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +0 -23
  51. data/lib/contrast/agent/reporting/reporting_events/finding.rb +19 -49
  52. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +12 -9
  53. data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +1 -1
  54. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +23 -21
  55. data/lib/contrast/agent/reporting/reporting_events/finding_event_stack.rb +5 -18
  56. data/lib/contrast/agent/reporting/reporting_events/finding_event_taint_range.rb +1 -0
  57. data/lib/contrast/{api/decorators/trace_taint_range_tags.rb → agent/reporting/reporting_events/finding_event_taint_range_tags.rb} +7 -6
  58. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +1 -1
  59. data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +1 -1
  60. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +2 -2
  61. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +10 -14
  62. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +11 -0
  63. data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +3 -1
  64. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +11 -23
  65. data/lib/contrast/agent/reporting/reporting_events/route_discovery_observation.rb +8 -26
  66. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -1
  67. data/lib/contrast/agent/reporting/reporting_utilities/build_preflight.rb +4 -7
  68. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -1
  69. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +3 -3
  70. data/lib/contrast/agent/request.rb +2 -2
  71. data/lib/contrast/agent/request_context.rb +8 -20
  72. data/lib/contrast/agent/request_context_extend.rb +15 -36
  73. data/lib/contrast/agent/request_handler.rb +0 -8
  74. data/lib/contrast/agent/response.rb +0 -18
  75. data/lib/contrast/agent/telemetry/events/event.rb +1 -1
  76. data/lib/contrast/agent/telemetry/events/metric_event.rb +1 -1
  77. data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +3 -3
  78. data/lib/contrast/agent/version.rb +1 -1
  79. data/lib/contrast/api/communication/messaging_queue.rb +2 -3
  80. data/lib/contrast/api/communication/socket_client.rb +4 -4
  81. data/lib/contrast/api/communication/speedracer.rb +4 -8
  82. data/lib/contrast/api/decorators/agent_startup.rb +5 -6
  83. data/lib/contrast/api/decorators/application_settings.rb +2 -1
  84. data/lib/contrast/api/decorators/application_startup.rb +6 -6
  85. data/lib/contrast/api/decorators/message.rb +0 -4
  86. data/lib/contrast/api/decorators/rasp_rule_sample.rb +0 -6
  87. data/lib/contrast/api/decorators.rb +0 -6
  88. data/lib/contrast/api/dtm.pb.rb +0 -489
  89. data/lib/contrast/components/agent.rb +16 -12
  90. data/lib/contrast/components/api.rb +10 -10
  91. data/lib/contrast/components/app_context.rb +3 -3
  92. data/lib/contrast/components/app_context_extend.rb +1 -1
  93. data/lib/contrast/components/assess.rb +92 -38
  94. data/lib/contrast/components/assess_rules.rb +36 -0
  95. data/lib/contrast/components/config.rb +54 -12
  96. data/lib/contrast/components/contrast_service.rb +8 -8
  97. data/lib/contrast/components/heap_dump.rb +1 -1
  98. data/lib/contrast/components/protect.rb +5 -5
  99. data/lib/contrast/components/ruby_component.rb +81 -0
  100. data/lib/contrast/components/sampling.rb +1 -1
  101. data/lib/contrast/components/security_logger.rb +23 -0
  102. data/lib/contrast/components/service.rb +55 -0
  103. data/lib/contrast/components/settings.rb +12 -4
  104. data/lib/contrast/config/base_configuration.rb +1 -1
  105. data/lib/contrast/config/protect_rules_configuration.rb +17 -3
  106. data/lib/contrast/config/server_configuration.rb +1 -1
  107. data/lib/contrast/config.rb +0 -6
  108. data/lib/contrast/configuration.rb +81 -17
  109. data/lib/contrast/extension/assess/exec_trigger.rb +3 -1
  110. data/lib/contrast/extension/assess/marshal.rb +3 -2
  111. data/lib/contrast/extension/assess/string.rb +0 -1
  112. data/lib/contrast/extension/extension.rb +1 -1
  113. data/lib/contrast/framework/base_support.rb +0 -5
  114. data/lib/contrast/framework/grape/support.rb +1 -23
  115. data/lib/contrast/framework/manager.rb +0 -10
  116. data/lib/contrast/framework/rails/support.rb +5 -58
  117. data/lib/contrast/framework/sinatra/support.rb +2 -21
  118. data/lib/contrast/logger/cef_log.rb +21 -3
  119. data/lib/contrast/logger/log.rb +1 -11
  120. data/lib/contrast/tasks/config.rb +4 -2
  121. data/lib/contrast/utils/assess/event_limit_utils.rb +5 -8
  122. data/lib/contrast/utils/assess/trigger_method_utils.rb +10 -18
  123. data/lib/contrast/utils/findings.rb +6 -5
  124. data/lib/contrast/utils/hash_digest.rb +9 -24
  125. data/lib/contrast/utils/hash_digest_extend.rb +6 -6
  126. data/lib/contrast/utils/invalid_configuration_util.rb +21 -58
  127. data/lib/contrast/utils/log_utils.rb +32 -8
  128. data/lib/contrast/utils/net_http_base.rb +2 -2
  129. data/lib/contrast/utils/patching/policy/patch_utils.rb +3 -2
  130. data/lib/contrast/utils/stack_trace_utils.rb +0 -25
  131. data/lib/contrast/utils/string_utils.rb +9 -0
  132. data/lib/contrast/utils/telemetry_client.rb +13 -7
  133. data/lib/contrast.rb +5 -10
  134. metadata +22 -28
  135. data/lib/contrast/agent/reporting/reporting_events/trace_event_source.rb +0 -30
  136. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -36
  137. data/lib/contrast/api/decorators/activity.rb +0 -33
  138. data/lib/contrast/api/decorators/architecture_component.rb +0 -36
  139. data/lib/contrast/api/decorators/finding.rb +0 -29
  140. data/lib/contrast/api/decorators/route_coverage.rb +0 -91
  141. data/lib/contrast/api/decorators/trace_event.rb +0 -120
  142. data/lib/contrast/api/decorators/trace_event_object.rb +0 -63
  143. data/lib/contrast/api/decorators/trace_event_signature.rb +0 -69
  144. data/lib/contrast/api/decorators/trace_taint_range.rb +0 -52
  145. data/lib/contrast/config/assess_configuration.rb +0 -93
  146. data/lib/contrast/config/assess_rules_configuration.rb +0 -32
  147. data/lib/contrast/config/root_configuration.rb +0 -90
  148. data/lib/contrast/config/ruby_configuration.rb +0 -81
  149. data/lib/contrast/config/service_configuration.rb +0 -49
  150. data/lib/contrast/utils/preflight_util.rb +0 -13
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'contrast/agent/assess/events/event_factory'
5
5
  require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
6
+ require 'contrast/agent/excluder'
6
7
  require 'contrast/components/logger'
7
8
  require 'contrast/utils/object_share'
8
9
  require 'contrast/utils/sha256_builder'
@@ -23,17 +24,13 @@ module Contrast
23
24
  # Contrast::Agent::Assess::Policy::TriggerNode class. Each such method will call to this module just after
24
25
  # invocation in order to determine if the call was done safely. In those cases where it was not, a Finding
25
26
  # report is issued to the Service.
26
- module TriggerMethod # rubocop:disable Metrics/ModuleLength
27
+ module TriggerMethod
27
28
  extend Contrast::Components::Logger::InstanceMethods
28
29
  extend Contrast::Utils::Assess::TriggerMethodUtils
29
30
  extend Contrast::Utils::Assess::EventLimitUtils
30
31
 
31
- # The level of TeamServer compliance our traces meet when in the abnormal condition of being dataflow rules
32
- # without routes.
33
- MINIMUM_FINDING_VERSION = 3
34
- # The level of TeamServer compliance our traces meet.
35
- CURRENT_FINDING_VERSION = 4
36
32
  # Rules that always exists outside of Request Context
33
+ # @return [Array<String>]
37
34
  NON_REQUEST_RULES = [
38
35
  'hardcoded-password', # Contrast::Agent::Assess::Rule::Provider::HardcodedPassword.NAME,
39
36
  'hardcoded-key' # Contrast::Agent::Assess::Rule::Provider::HardcodedKey.NAME
@@ -58,7 +55,9 @@ module Contrast
58
55
  # else proceed, if the flag is true we don't need to check for context we
59
56
  # go as currently.
60
57
  # When outside of a request, only track when the feature is enabled
61
- return unless Contrast::ASSESS.non_request_tracking? || context
58
+ return unless context ||
59
+ Contrast::ASSESS.non_request_tracking? ||
60
+ NON_REQUEST_RULES.include?(trigger_node.rule_id)
62
61
 
63
62
  if trigger_node.sources&.any?
64
63
  trigger_node.sources.each do |marker|
@@ -74,95 +73,57 @@ module Contrast
74
73
  apply_trigger(trigger_node, source, object, ret, *args)
75
74
  end
76
75
 
77
- def append_to_finding finding, event_data, request
78
- finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(event_data.policy_node.rule_id)
79
- finding.version = determine_compliance_version(finding)
80
- append_events(finding, event_data)
81
- append_route(finding, request)
82
- append_hash(finding, event_data.tagged, request)
83
- end
84
-
85
76
  # This converts the source of the finding, and the events leading up to it into a Finding
86
77
  #
87
78
  # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
88
79
  # trigger event
89
80
  # @param source [Object] the source of the Trigger Event
90
81
  # @param object [Object] the Object on which the method was invoked
91
- # @param ret [Object] the Return of the invoked method
82
+ # @param ret [Object, nil] the Return of the invoked method, or nil if void method
92
83
  # @param args [Array<Object>] the Arguments with which the method was invoked
93
- # @return [Contrast::Api::Dtm::Finding, nil] the Contrast::Api::Dtm::Finding to send to TeamServer or
94
- # nil if conditions were not met
84
+ # @return [Contrast::Agent::Reporting::Finding, nil] the finding to send to TeamServer if found
95
85
  def build_finding trigger_node, source, object, ret, *args
96
86
  content_type = Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type
97
-
98
87
  if content_type.nil? && trigger_node.collectable?
99
88
  Contrast::Agent::FINDINGS.collect_finding(trigger_node, source, object, ret, *args)
100
89
  return
101
90
  end
102
-
103
91
  return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
104
92
 
105
93
  request = find_request(source)
106
94
  return unless reportable?(request&.env)
95
+ return if excluded_by_url_and_rule?(request, trigger_node.rule_id)
107
96
 
108
- process_reportable_finding(trigger_node, source, object, ret, request, *args)
97
+ finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
98
+ finding.attach_data(trigger_node, source, object, ret, request, *args)
99
+ return if excluded_by_input_and_rule?(request, finding, trigger_node.rule_id)
100
+
101
+ finding.hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source, request)
102
+ finding
109
103
  rescue StandardError => e
110
104
  logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
111
- end
112
-
113
- def process_reportable_finding trigger_node, source, object, ret, request, *args
114
- if Contrast::Agent::Reporter.enabled?
115
- handle_new_finding(trigger_node, source, object, ret, request, *args)
116
- else # TODO: RUBY-1438 -- remove
117
- finding = Contrast::Api::Dtm::Finding.new
118
- event_data = Contrast::Agent::Assess::Events::EventData.new(trigger_node, source, object, ret, args)
119
- append_to_finding(finding, event_data, request)
120
- logger.trace('Finding created', node_id: trigger_node.id, source_id: source.__id__,
121
- rule: trigger_node.rule_id)
122
- # check here if we need to add that finding
123
- report_finding(finding, request) unless not_reported?(finding)
124
- end
105
+ nil
125
106
  end
126
107
 
127
108
  # Given a finding, append it to an activity message and send it to the Service for processing. If an
128
109
  # activity message does not exist, b/c we're invoked outside of a request context, build an activity and
129
110
  # immediately report it with the finding.
130
111
  #
131
- # TODO: RUBY-1351
132
- #
133
- # @param finding [Contrast::Api::Dtm::Finding] the Finding to report.
134
- # @param request [Contrast::Agent::Request] our wrapper around the Rack::Request.
135
- def report_finding finding, request = nil
136
- context = Contrast::Agent::REQUEST_TRACKER.current
137
- if context
138
- # REMOVE_DTM_ACTIVITY
139
- context.dtm_activity.findings << finding
140
- return
141
- end
112
+ # @param finding [Contrast::Agent::Reporting::Finding] the Finding to report.
113
+ def report_finding finding
114
+ return unless finding
142
115
 
143
- return unless ::Contrast::ASSESS.non_request_tracking? || NON_REQUEST_RULES.include?(finding.rule_id)
116
+ preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding)
117
+ return unless preflight
144
118
 
145
- # REMOVE_DTM_ACTIVITY Findings still using dtm - REMOVE
146
- activity = Contrast::Api::Dtm::Activity.new
147
- if request
148
- activity.request = request.dtm
149
- else
150
- logger.debug('Attempted to report finding without request', finding: finding)
151
- end
119
+ preflight_data = preflight.messages[0].data
120
+ return if Contrast::Agent::REQUEST_TRACKER.current&.reported_findings&.include?(preflight_data)
152
121
 
153
- Contrast::Agent.messaging_queue&.send_event_eventually(activity)
154
- end
155
-
156
- def handle_new_finding trigger_node, source, object, ret, request, *args
157
- # sent to reporter
158
- # here we will generate new type of finding
159
- ruby_finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
160
- ruby_finding.attach_data(trigger_node, source, object, ret, request, *args)
161
- hash_code = Contrast::Utils::HashDigest.generate_event_hash(ruby_finding, source, request)
162
- ruby_finding.hash_code = hash_code
163
-
164
- new_preflight = Contrast::Agent::Reporting::BuildPreflight.build(ruby_finding, request)
165
- Contrast::Agent.reporter&.send_event(new_preflight)
122
+ Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding
123
+ if Contrast::Agent::REQUEST_TRACKER.current
124
+ Contrast::Agent::REQUEST_TRACKER.current.reported_findings << preflight_data
125
+ end
126
+ Contrast::Agent.reporter&.send_event(preflight)
166
127
  end
167
128
 
168
129
  private
@@ -204,30 +165,6 @@ module Contrast
204
165
  end
205
166
  end
206
167
 
207
- def append_events finding, event_data
208
- append_from_source(finding, event_data.tagged)
209
- finding.events << Contrast::Agent::Assess::Events::EventFactory.build(event_data).to_dtm_event
210
- end
211
-
212
- def append_from_source finding, source
213
- return unless source
214
- return unless Contrast::Agent::Assess::Tracker.trackable?(source)
215
-
216
- properties = Contrast::Agent::Assess::Tracker.properties(source)
217
- return unless properties
218
-
219
- build_events(finding, properties.event) if properties.event
220
-
221
- # CSGoogle::Protobuf::Map doesn't support merge!, so we have to do this long form
222
- source_props = properties.properties
223
- return unless source_props
224
-
225
- source_props.each_pair do |key, value|
226
- key = Contrast::Utils::StringUtils.force_utf8(key)
227
- finding.properties[key] = Contrast::Utils::StringUtils.force_utf8(value)
228
- end
229
- end
230
-
231
168
  def build_events finding, event
232
169
  return unless event
233
170
 
@@ -239,50 +176,17 @@ module Contrast
239
176
  finding.events << event.to_dtm_event
240
177
  end
241
178
 
242
- def append_route finding, request
243
- context = Contrast::Agent::REQUEST_TRACKER.current
244
- if context
245
- finding.routes << context.route if context.route
246
- elsif request&.route
247
- finding.routes << request.route
248
- end
249
- end
250
-
251
- def append_hash finding, source, request
252
- hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source, request)
253
- finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
254
- finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
255
- end
256
-
257
- # Because our APIs are not versioned, TeamServer relies on a field, version, to tell it what, if any,
258
- # validation it can preform on the findings we send it. Examine the finding and determine the level to
259
- # which it conforms.
179
+ # Check if the finding should be excluded due to the assess exclusion rules.
260
180
  #
261
- # @param finding [Contrast::Api::Dtm::Finding]
262
- # @return [int] the version of compliance
263
- def determine_compliance_version finding
264
- return MINIMUM_FINDING_VERSION unless finding
265
- # as routes are the only variable between findings, in the case where we couldn't determine one, any
266
- # finding with a route is at maximum compliance
267
- return CURRENT_FINDING_VERSION if finding.routes.any?
268
- # any finding without events is not of a dataflow type and therefore at maximum compliance
269
- return CURRENT_FINDING_VERSION unless finding.events.any?
270
-
271
- MINIMUM_FINDING_VERSION
181
+ # @param request [Contrast::Agent::Request] a wrapper around the Rack::Request for the current request
182
+ # @param rule_id [String]
183
+ # return [Boolean]
184
+ def excluded_by_url_and_rule? request, rule_id
185
+ Contrast::SETTINGS.excluder.assess_excluded_by_url_and_rule?(request, rule_id)
272
186
  end
273
187
 
274
- # Check if the following finding was already added to context.activity
275
- # We could use this after we get rid of the MessagingQueue.
276
- #
277
- # @param finding [Contrast::Api::Dtm::Finding] Finding we're about to report
278
- # @return Boolean
279
- def not_reported? finding
280
- return false unless (current = Contrast::Agent::REQUEST_TRACKER.current)
281
-
282
- currently_added_findings = current.dtm_activity.findings
283
- return false if currently_added_findings.empty?
284
-
285
- currently_added_findings.any? { |f| f.rule_id == finding.rule_id && f.hash_code == finding.hash_code }
188
+ def excluded_by_input_and_rule? request, finding, rule_id
189
+ Contrast::SETTINGS.excluder.assess_excluded_by_input_and_rule?(request, finding, rule_id)
286
190
  end
287
191
  end
288
192
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  require 'contrast/agent/assess/policy/trigger/reflected_xss'
5
5
  require 'contrast/agent/assess/policy/trigger/xpath'
6
- require 'contrast/api/decorators/trace_taint_range_tags'
6
+ require 'contrast/agent/reporting/reporting_events/finding_event_taint_range_tags'
7
7
  require 'contrast/components/logger'
8
8
 
9
9
  module Contrast
@@ -185,8 +185,8 @@ module Contrast
185
185
 
186
186
  tags.each do |tag|
187
187
  raise(ArgumentError, "Rule #{ rule_id } had an invalid tag. #{ tag } is not a known value.") unless
188
- Contrast::Api::Decorators::TraceTaintRangeTags::VALID_TAGS.include?(tag) ||
189
- Contrast::Api::Decorators::TraceTaintRangeTags::VALID_SOURCE_TAGS.include?(tag)
188
+ Contrast::Agent::Reporting::FindingEventTaintRangeTags::VALID_TAGS.include?(tag) ||
189
+ Contrast::Agent::Reporting::FindingEventTaintRangeTags::VALID_SOURCE_TAGS.include?(tag)
190
190
  end
191
191
  end
192
192
 
@@ -42,24 +42,14 @@ module Contrast
42
42
  return unless event.source_type
43
43
  return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
44
44
 
45
- append_to_ruby_object(current_request, event)
46
- end
47
-
48
- # @param current_request [Contrast::Agent::RequestContext]
49
- # @param event [Contrast::Agent::Assess::Events::ContrastEvent]
50
- def append_to_ruby_object current_request, event
51
45
  if current_request.observed_route.sources.any? do |source|
52
- source.type == event.forced_source_type &&
53
- source.name == event.forced_source_name # rubocop:disable Security/Module/Name
46
+ source.type == event.source_type && source.name == event.source_name # rubocop:disable Security/Module/Name
54
47
  end
55
48
 
56
49
  return
57
50
  end
58
51
 
59
- event_source = event.build_event_source
60
- return unless event_source
61
-
62
- current_request.observed_route.sources << event_source
52
+ current_request.observed_route.sources << event.event_source if event.event_source
63
53
  end
64
54
  end
65
55
  end
@@ -11,9 +11,8 @@ module Contrast
11
11
  module Assess
12
12
  module Rule
13
13
  module Provider
14
- # Hardcoded rules detect if any secret value has been written
15
- # directly into the sourcecode of the application. To use this base
16
- # class, a provider must implement three methods:
14
+ # Hardcoded rules detect if any secret value has been written directly into the sourcecode of the
15
+ # application. To use this base class, a provider must implement three methods:
17
16
  # 1) name_passes? : does the constant name match a given value set
18
17
  # 2) value_node_passes? : does the value of the constant match a
19
18
  # given value set
@@ -21,23 +20,21 @@ module Contrast
21
20
  module HardcodedValueRule
22
21
  include Contrast::Components::Logger::InstanceMethods
23
22
 
23
+ # @return [Boolean]
24
24
  def disabled?
25
25
  !::Contrast::ASSESS.enabled? || ::Contrast::ASSESS.rule_disabled?(rule_id)
26
26
  end
27
27
 
28
- # Parse the file pertaining to the given TracePoint to walk its AST
29
- # to determine if a Constant is hardcoded. For our purposes, this
30
- # hard coding means directly set rather than as an interpolated
31
- # String or through a method call.
28
+ # Parse the file pertaining to the given TracePoint to walk its AST to determine if a Constant is
29
+ # hardcoded. For our purposes, this hard coding means directly set rather than as an interpolated String or
30
+ # through a method call.
32
31
  #
33
- # Note: This is a top layer check, we make no assertions about what
34
- # the methods or interpolations do. Their presence, even if only
35
- # calling a hardcoded thing, causes this check to not report.
32
+ # Note: This is a top layer check, we make no assertions about what the methods or interpolations do.
33
+ # Their presence, even if only calling a hardcoded thing, causes this check to not report.
36
34
  #
37
- # @param trace_point [TracePoint] the TracePoint event created on
38
- # the :end of a Module being loaded
39
- # @param ast [RubyVM::AbstractSyntaxTree::Node] the abstract syntax
40
- # tree of the Module defined in the TracePoint end event
35
+ # @param trace_point [TracePoint] the TracePoint event created on the :end of a Module being loaded
36
+ # @param ast [RubyVM::AbstractSyntaxTree::Node] the abstract syntax tree of the Module defined in the
37
+ # TracePoint end event
41
38
  def parse trace_point, ast
42
39
  return if disabled?
43
40
 
@@ -56,8 +53,8 @@ module Contrast
56
53
  private
57
54
 
58
55
  # @param mod [Module] the module to which this AST pertains
59
- # @param ast [RubyVM::AbstractSyntaxTree::Node, Object] a node
60
- # within the AST, which may be a leaf, so any Object
56
+ # @param ast [RubyVM::AbstractSyntaxTree::Node, Object] a node within the AST, which may be a leaf, so any
57
+ # Object
61
58
  def parse_ast mod, ast
62
59
  return unless ast.cs__is_a?(RubyVM::AbstractSyntaxTree::Node)
63
60
  return unless ast.cs__respond_to?(:children)
@@ -72,32 +69,27 @@ module Contrast
72
69
  # https://www.rubydoc.info/gems/ruby-internal/Node/CDECL
73
70
  return unless ast.type == :CDECL
74
71
 
75
- # The CDECL Node has two children, the first being the Constant
76
- # name as a symbol, the second as the value to assign to that
77
- # constant
72
+ # The CDECL Node has two children, the first being the Constant name as a symbol, the second as the value
73
+ # to assign to that constant
78
74
  children = ast.children
79
75
  name = children[0].to_s
80
76
  # If that constant name doesn't pass our checks, move on.
81
77
  return unless name_passes?(name)
82
78
 
83
79
  value = children[1]
84
- # The assignment node could be a direct value or a call of some
85
- # sort. We leave it to each rule to properly handle these nodes.
80
+ # The assignment node could be a direct value or a call of some sort. We leave it to each rule to
81
+ # properly handle these nodes.
86
82
  return unless value_node_passes?(value)
87
83
 
88
- if Contrast::Agent::Reporter.enabled?
89
- new_finding_and_reporting(mod, name)
90
- else # TODO: RUBY-1438 -- remove
91
- build_finding(mod, name)
92
- end
84
+ build_and_report(mod, name)
85
+ rescue StandardError => e
86
+ logger.error('Unable to parse AST for Hardcoded Rule analysis.', e)
93
87
  end
94
88
 
95
- # Constants can be set as frozen directly. We need to account for
96
- # this change as it means the Node given to the :CDECL call will be
97
- # a :CALL, not a constant.
89
+ # Constants can be set as frozen directly. We need to account for this change as it means the Node given to
90
+ # the :CDECL call will be a :CALL, not a constant.
98
91
  #
99
- # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to
100
- # evaluate
92
+ # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to evaluate
101
93
  # @return [Boolean] is this a freeze call or not
102
94
  def freeze_call? value_node
103
95
  return false unless value_node.type == :CALL
@@ -109,63 +101,29 @@ module Contrast
109
101
  children[1] == :freeze
110
102
  end
111
103
 
112
- def build_finding clazz, constant_string
113
- class_name = clazz.cs__name
114
-
115
- finding = assign_finding(class_name, constant_string)
116
- # TODO: RUBY-1705
117
- # The only place we still use dtm activity
118
- activity = Contrast::Api::Dtm::Activity.new
119
- activity.findings << finding
120
- Contrast::Agent.messaging_queue.send_event_eventually(activity, force: true)
121
- rescue StandardError => e
122
- logger.error('Unable to build a finding for Hardcoded Rule', e)
123
- nil
124
- end
104
+ # @param mod [Module] the module to which this AST pertains
105
+ # @param name [String] the name of the hardcoded constant
106
+ def build_and_report mod, name
107
+ finding = build_finding(mod, name)
108
+ return unless finding
125
109
 
126
- # @param class_name [String] the name of the class in which the hardcoded value is present
127
- # @param constant_string [String] the name of the constant
128
- # @return [Contrast::Api::Dtm::Finding]
129
- def assign_finding class_name, constant_string
130
- finding = Contrast::Api::Dtm::Finding.new
131
- finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(rule_id)
132
- finding.version = Contrast::Agent::Assess::Policy::TriggerMethod::CURRENT_FINDING_VERSION
133
-
134
- finding.properties[SOURCE_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(class_name)
135
- finding.properties[CONSTANT_NAME_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(constant_string)
136
- finding.properties[CODE_SOURCE_KEY] =
137
- Contrast::Utils::StringUtils.protobuf_safe_string(constant_string + redacted_marker)
138
-
139
- hash = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
140
- finding.hash_code = Contrast::Utils::StringUtils.protobuf_safe_string(hash)
141
- finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
142
- finding
143
- end
110
+ preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding)
111
+ return unless preflight
144
112
 
145
- def new_finding_and_reporting clazz, constant_string
146
- return unless Contrast::Agent::Reporter.enabled?
147
-
148
- # sent to reporter
149
- # and add logger message for the report of the preflight
150
- new_preflight = Contrast::Agent::Reporting::Preflight.new
151
- new_preflight_message = Contrast::Agent::Reporting::PreflightMessage.new
152
- new_preflight_message.hash_code = hash
153
- new_preflight_message.data = "#{ rule_id },#{ hash }"
154
- new_preflight.messages << new_preflight_message
155
-
156
- # extract to new method
157
- # here we will generate new type of finding
158
- ruby_finding = Contrast::Agent::Reporting::Finding.new(rule_id)
159
- ruby_finding.hash_code = hash
160
- ruby_finding.properties[SOURCE_KEY] = clazz.cs__name
161
- ruby_finding.properties[CONSTANT_NAME_KEY] = constant_string
162
- ruby_finding.properties[CODE_SOURCE_KEY] = constant_string + redacted_marker
163
- save_and_report_finding(ruby_finding, new_preflight)
113
+ Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding
114
+ Contrast::Agent.reporter&.send_event(preflight)
164
115
  end
165
116
 
166
- def save_and_report_finding ruby_finding, new_preflight
167
- Contrast::Agent::Reporting::ReportingStorage[hash] = ruby_finding
168
- Contrast::Agent.reporter&.send_event(new_preflight)
117
+ # @param clazz [Class] the Class or Module containing the constant
118
+ # @param constant_string [String]
119
+ # @return [Contrast::Agent::Reporting::Finding]
120
+ def build_finding clazz, constant_string
121
+ finding = Contrast::Agent::Reporting::Finding.new(rule_id)
122
+ finding.properties[SOURCE_KEY] = clazz.cs__name
123
+ finding.properties[CONSTANT_NAME_KEY] = constant_string
124
+ finding.properties[CODE_SOURCE_KEY] = constant_string + redacted_marker
125
+ finding.hash_code = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
126
+ finding
169
127
  end
170
128
  end
171
129
  end
@@ -3,10 +3,8 @@
3
3
 
4
4
  require 'rack'
5
5
  require 'json'
6
- require 'contrast/agent/reporting/reporting_utilities/dtm_message'
7
6
  require 'contrast/agent/reporting/reporting_utilities/build_preflight'
8
7
  require 'contrast/utils/hash_digest'
9
- require 'contrast/utils/preflight_util'
10
8
  require 'contrast/utils/string_utils'
11
9
 
12
10
  module Contrast
@@ -32,18 +30,11 @@ module Contrast
32
30
  finding = build_finding(violation)
33
31
  return unless finding
34
32
 
35
- if Contrast::Agent::Reporter.enabled?
36
- report = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(finding)
37
- if report.is_a?(Contrast::Agent::Reporting::Finding)
38
- request = Contrast::Agent::REQUEST_TRACKER.current&.request
39
- preflight = Contrast::Agent::Reporting::BuildPreflight.build(report, request)
40
- Contrast::Agent.reporter&.send_event(preflight)
41
- else
42
- Contrast::Agent.reporter&.send_event(report)
43
- end
44
- else
45
- Contrast::Agent::REQUEST_TRACKER.current.activity.findings << finding
46
- end
33
+ preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding)
34
+ return unless preflight
35
+
36
+ Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding
37
+ Contrast::Agent.reporter&.send_event(preflight)
47
38
  end
48
39
 
49
40
  protected
@@ -75,16 +66,15 @@ module Contrast
75
66
  # Convert the given evidence into a finding. The rule will populate this evidence with each of the
76
67
  #
77
68
  # @param evidence [Hash] the properties required to build this finding.
78
- # @return [Contrast::Api::Dtm::Finding]
69
+ # @return [Contrast::Agent::Reporting::Finding]
79
70
  def build_finding evidence
80
- finding = Contrast::Api::Dtm::Finding.new
81
- finding.rule_id = rule_id
71
+ finding = Contrast::Agent::Reporting::Finding.new(rule_id)
82
72
  context = Contrast::Agent::REQUEST_TRACKER.current
83
- finding.routes << context.route if context&.route
73
+ finding.routes << context.discovered_route if context&.discovered_route
84
74
  build_evidence(evidence, finding)
75
+ finding.request = Contrast::Agent::Reporting::FindingRequest.convert(context.request) if context&.request
85
76
  hash = Contrast::Utils::HashDigest.generate_config_hash(finding)
86
77
  finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash)
87
- finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
88
78
  finding
89
79
  end
90
80
 
@@ -92,16 +82,10 @@ module Contrast
92
82
  # Change it accordingly the rule you work on
93
83
  #
94
84
  # @param evidence [Hash] the properties required to build this finding.
95
- # @param finding [Contrast::Api::Dtm::Finding] finding to attach the evidence to
85
+ # @param finding [Contrast::Agent::Reporting::Finding] finding to attach the evidence to
96
86
  def build_evidence evidence, finding
97
87
  evidence.each_pair do |key, value|
98
- finding.properties[key] = if value.cs__is_a?(Hash)
99
- Contrast::Utils::StringUtils.protobuf_format(value.to_json)
100
- elsif value.cs__is_a?(Array)
101
- value.map { Contrast::Utils::StringUtils.protobuf_format(_1) }.to_s
102
- else
103
- Contrast::Utils::StringUtils.protobuf_format(value)
104
- end
88
+ finding.properties[key] = value
105
89
  end
106
90
  end
107
91
 
@@ -2,9 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'rack'
5
- require 'contrast/agent/reporting/reporting_utilities/dtm_message'
6
5
  require 'contrast/utils/hash_digest'
7
- require 'contrast/utils/preflight_util'
8
6
  require 'contrast/utils/string_utils'
9
7
  require 'contrast/agent/assess/rule/response/base_rule'
10
8
 
@@ -22,7 +20,7 @@ module Contrast
22
20
  START_PROP = 'start'.cs__freeze
23
21
  END_PROP = 'end'.cs__freeze
24
22
  FORM_START_REGEXP = /<form/i.cs__freeze
25
- META_TYPE = 'meta'
23
+ META_TYPE = 'META tag'
26
24
  PRAGMA = 'pragma'
27
25
 
28
26
  # Rules discern which responses they can/should analyze.