contrast-agent 6.4.0 → 6.6.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__contrast_patch/cs__contrast_patch.c +14 -1
  3. data/lib/contrast/agent/assess/finalizers/hash.rb +1 -0
  4. data/lib/contrast/agent/assess/policy/propagation_method.rb +5 -1
  5. data/lib/contrast/agent/assess/policy/propagator/custom.rb +4 -0
  6. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +5 -0
  7. data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -0
  8. data/lib/contrast/agent/assess/policy/source_method.rb +5 -0
  9. data/lib/contrast/agent/assess/policy/trigger_method.rb +8 -2
  10. data/lib/contrast/agent/assess/tracker.rb +12 -0
  11. data/lib/contrast/agent/inventory/database_config.rb +2 -1
  12. data/lib/contrast/agent/inventory/dependency_analysis.rb +2 -2
  13. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -1
  14. data/lib/contrast/agent/inventory/policy/datastores.rb +1 -1
  15. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  16. data/lib/contrast/agent/patching/policy/method_policy.rb +3 -3
  17. data/lib/contrast/agent/protect/rule/base.rb +1 -1
  18. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +1 -3
  19. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +17 -21
  20. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +1 -1
  21. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +26 -3
  22. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +5 -5
  23. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -1
  24. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
  25. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
  26. data/lib/contrast/agent/request_context.rb +8 -0
  27. data/lib/contrast/agent/service_heartbeat.rb +2 -3
  28. data/lib/contrast/agent/static_analysis.rb +1 -1
  29. data/lib/contrast/agent/version.rb +1 -1
  30. data/lib/contrast/agent/worker_thread.rb +10 -0
  31. data/lib/contrast/api/communication/response_processor.rb +1 -1
  32. data/lib/contrast/components/agent.rb +52 -14
  33. data/lib/contrast/components/api.rb +60 -23
  34. data/lib/contrast/components/assess.rb +16 -0
  35. data/lib/contrast/components/contrast_service.rb +1 -1
  36. data/lib/contrast/components/heap_dump.rb +51 -1
  37. data/lib/contrast/components/inventory.rb +19 -13
  38. data/lib/contrast/components/logger.rb +18 -0
  39. data/lib/contrast/components/protect.rb +41 -1
  40. data/lib/contrast/components/sampling.rb +29 -0
  41. data/lib/contrast/config/assess_configuration.rb +33 -3
  42. data/lib/contrast/config/base_configuration.rb +8 -2
  43. data/lib/contrast/config/root_configuration.rb +19 -16
  44. data/lib/contrast/config/service_configuration.rb +4 -4
  45. data/lib/contrast/config.rb +0 -9
  46. data/lib/contrast/extension/object.rb +19 -0
  47. data/lib/contrast/framework/rails/support.rb +7 -3
  48. data/lib/contrast/logger/log.rb +2 -1
  49. data/lib/contrast/utils/assess/event_limit_utils.rb +96 -0
  50. data/lib/contrast/utils/assess/propagation_method_utils.rb +27 -7
  51. data/lib/contrast/utils/log_utils.rb +2 -2
  52. data/lib/contrast/utils/net_http_base.rb +2 -2
  53. data/lib/contrast/utils/patching/policy/patch_utils.rb +1 -1
  54. data/lib/contrast.rb +6 -21
  55. data/resources/assess/policy.json +15 -12
  56. data/resources/deadzone/policy.json +139 -19
  57. data/ruby-agent.gemspec +2 -0
  58. data/service_executables/VERSION +1 -1
  59. data/service_executables/linux/contrast-service +0 -0
  60. data/service_executables/mac/contrast-service +0 -0
  61. metadata +43 -20
  62. data/lib/contrast/config/agent_configuration.rb +0 -63
  63. data/lib/contrast/config/api_configuration.rb +0 -56
  64. data/lib/contrast/config/heap_dump_configuration.rb +0 -59
  65. data/lib/contrast/config/inventory_configuration.rb +0 -33
  66. data/lib/contrast/config/logger_configuration.rb +0 -26
  67. data/lib/contrast/config/protect_configuration.rb +0 -33
  68. data/lib/contrast/config/sampling_configuration.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d74206aa94d644cbe7c47523a69efdb9c0f27d59604125287fd98b123f0d20d6
4
- data.tar.gz: d4c28074b8d3f11e968d9c3875bfd16d3de41bdd63452bec470cea65a6c83089
3
+ metadata.gz: b9901918f58625ea7f9366f73110afcdd5f05e119261ba9a08f24f36903fe897
4
+ data.tar.gz: c618ebc74b006529e2317cd62ba59fabbba9d0c8fbf24c7988dfd98ec627b04e
5
5
  SHA512:
6
- metadata.gz: 3dd36988b29722b7961e919d2685a8cafbf001917f54f5e43107cf51fddb7c105cfea368e04f819f01dc388dd75f5bf739f9bb0c58301784c90bf48188c43bd8
7
- data.tar.gz: 18a2cbb1e4a4e56d64d3a3821c47ec3aca5709f64673ffb0024b26173de7d1ed4aa8213c851c202c1d30c118d73a4f373b14563d3aa3d9f017ea8caa24ead71d
6
+ metadata.gz: d2f584a6658ab0e316b41021575888d9ecc1eaacf79d971492fd2e6317609bfd0cb3a762a64d620b08fc18c76e2434d05cc3d82f8d587c86cdae5116df6cae61
7
+ data.tar.gz: df8f941fe730188be0bc3b69bd9a9d6d60f9695be8ee8e8eb0b6e35ab1207d97debba28220ff796bec702ad3b158f53f00f87a58f1c14939092ed843bef66715
@@ -101,7 +101,18 @@ VALUE rescue_func(VALUE arg1) {
101
101
  return Qnil;
102
102
  }
103
103
 
104
+ /**
105
+ * In the event that the original_method call throws an exception we need to ensure that contrast_post_patch is called
106
+ * to report that error. However, if there is no error we will call post_patch with the original_return instead of
107
+ * Qnil.
108
+ *
109
+ **/
104
110
  VALUE contrast_patch_call_ensure(const VALUE *args) {
111
+ // we do not need to ensure that post patch is called if no error was thrown
112
+ if(!RTEST(rb_errinfo())) {
113
+ return Qnil;
114
+ }
115
+
105
116
  int argc;
106
117
  VALUE object, preshift, method_policy, method;
107
118
  VALUE *argv;
@@ -125,6 +136,7 @@ VALUE ensure_wrapper(const VALUE *args) {
125
136
  original_args = (VALUE)args[1];
126
137
  ensure_args = (VALUE)args[2];
127
138
 
139
+ //this ensure if being treated as a rescue due to issues surrounding Kernel#throw
128
140
  return rb_ensure(original_method, original_args, contrast_patch_call_ensure,
129
141
  (VALUE)ensure_args);
130
142
  }
@@ -220,13 +232,14 @@ VALUE contrast_run_patches(const VALUE *wrapped_args) {
220
232
  * If the original method threw an exception, contrast_patch_call_rescue
221
233
  * re-raises the original exception, which unwinds the stack back to the
222
234
  * call site. This means the rest of this function is not executed.
235
+ * post_patch is called in the ensure_wrapper on exception. rb_rescue
236
+ * raises the exception so the below will not be executed in that event.
223
237
  */
224
238
 
225
239
  /* Invoke Contrast post-call patching. */
226
240
  contrast_call_post_patch(method_policy, preshift, object,
227
241
  original_ret, argc, argv);
228
242
 
229
- /* Special case for tracking frozen sources */
230
243
  return original_ret;
231
244
  }
232
245
 
@@ -14,6 +14,7 @@ module Contrast
14
14
  FROZEN_FINALIZED_IDS = Set.new
15
15
 
16
16
  def []= key, obj
17
+ return unless obj
17
18
  return unless ::Contrast::AGENT.enabled? && ::Contrast::ASSESS.enabled?
18
19
 
19
20
  # We can't finalize frozen things, so only act on those that went through .pre_freeze
@@ -8,6 +8,7 @@ require 'contrast/components/logger'
8
8
  require 'contrast/utils/object_share'
9
9
  require 'contrast/utils/sha256_builder'
10
10
  require 'contrast/utils/assess/propagation_method_utils'
11
+ require 'contrast/utils/assess/event_limit_utils'
11
12
  require 'contrast/agent/assess/events/event_data'
12
13
  require 'contrast/utils/assess/object_store'
13
14
 
@@ -21,6 +22,7 @@ module Contrast
21
22
  module PropagationMethod
22
23
  extend Contrast::Components::Logger::InstanceMethods
23
24
  extend Contrast::Utils::Assess::PropagationMethodUtils
25
+ extend Contrast::Utils::Assess::EventLimitUtils
24
26
 
25
27
  @properties = Contrast::Utils::Assess::ObjectStore.new
26
28
 
@@ -38,6 +40,7 @@ module Contrast
38
40
  def apply_propagation method_policy, preshift, object, ret, args, block
39
41
  return unless (propagation_node = method_policy.propagation_node)
40
42
  return unless propagation_node.use_original_object? || preshift
43
+ return if event_limit?(method_policy)
41
44
 
42
45
  target = determine_target(propagation_node, ret, object, args)
43
46
  propagation_data = Contrast::Agent::Assess::Events::EventData.new(nil, nil, object, ret, args)
@@ -184,7 +187,7 @@ module Contrast
184
187
  # @param _block [Block] the Block passed to the original method
185
188
  def handle_cs_properties_propagation propagation_node, preshift, target, propagation_data, _block
186
189
  return if propagation_node.action == NOOP_ACTION
187
- return unless can_propagate?(propagation_node, preshift, target)
190
+ return unless can_propagate?(propagation_node, preshift, target, propagation_data)
188
191
  return unless (propagation_class = find_propagation_class(propagation_node))
189
192
 
190
193
  # If we are using the original object tracking, the preshift object is not created.
@@ -192,6 +195,7 @@ module Contrast
192
195
  source = propagation_node.use_original_object? ? propagation_data.object : preshift
193
196
  handle_propagation(propagation_class, propagation_node, source, target)
194
197
  update_properties(propagation_node, target, propagation_data)
198
+ increment_event_count(propagation_node)
195
199
  end
196
200
 
197
201
  def handle_propagation propagation_class, propagation_node, source, target
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/extension/module'
5
+ require 'contrast/utils/assess/event_limit_utils'
5
6
 
6
7
  module Contrast
7
8
  module Agent
@@ -13,6 +14,8 @@ module Contrast
13
14
  # action knows the class and method it should call to preform this
14
15
  # action.
15
16
  module Custom
17
+ extend Contrast::Utils::Assess::EventLimitUtils
18
+
16
19
  class << self
17
20
  def propagate propagation_node, preshift, ret, block
18
21
  clazz = propagation_node.patch_class
@@ -26,6 +29,7 @@ module Contrast
26
29
  propagation_node.patch_class = clazz
27
30
  end
28
31
  clazz.send(method, propagation_node, preshift, ret, block)
32
+ increment_event_count(propagation_node)
29
33
  end
30
34
  end
31
35
  end
@@ -1,6 +1,8 @@
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/utils/assess/event_limit_utils'
5
+
4
6
  module Contrast
5
7
  module Agent
6
8
  module Assess
@@ -11,6 +13,8 @@ module Contrast
11
13
  # results in new source nodes to track which columns in the database
12
14
  # have been tainted.
13
15
  class DatabaseWrite < Contrast::Agent::Assess::Policy::Propagator::Base
16
+ extend Contrast::Utils::Assess::EventLimitUtils
17
+
14
18
  class << self
15
19
  def propagate propagation_node, preshift, target
16
20
  return unless Contrast::ASSESS.require_dynamic_sources?
@@ -22,6 +26,7 @@ module Contrast
22
26
  known_tainted = ::Contrast::ASSESS.tainted_columns[class_name]
23
27
  propagation_node.sources.each do |source|
24
28
  handle_write(propagation_node, source, preshift, target, known_tainted, tainted_columns)
29
+ increment_event_count(propagation_node)
25
30
  end
26
31
  return if tainted_columns.empty?
27
32
 
@@ -6,6 +6,7 @@ require 'contrast/components/agent'
6
6
  require 'contrast/components/logger'
7
7
  require 'contrast/components/scope'
8
8
  require 'contrast/utils/thread_tracker'
9
+ require 'contrast/utils/assess/event_limit_utils'
9
10
  require 'contrast/utils/assess/split_utils'
10
11
  require 'contrast/agent/assess/events/event_data'
11
12
 
@@ -20,6 +21,7 @@ module Contrast
20
21
  extend Contrast::Components::Scope::InstanceMethods
21
22
  extend Contrast::Components::Logger::InstanceMethods
22
23
  extend Contrast::Utils::Assess::SplitUtils
24
+ extend Contrast::Utils::Assess::EventLimitUtils
23
25
 
24
26
  SPLIT_TRACKER = Contrast::Utils::ThreadTracker.new
25
27
 
@@ -42,6 +44,7 @@ module Contrast
42
44
  return unless (source_properties = Contrast::Agent::Assess::Tracker.properties(source))
43
45
 
44
46
  update_element_properties(propagation_node, target, preshift, source_properties)
47
+ increment_event_count(propagation_node)
45
48
  nil
46
49
  end
47
50
 
@@ -7,6 +7,7 @@ require 'contrast/components/logger'
7
7
  require 'contrast/utils/object_share'
8
8
  require 'contrast/utils/sha256_builder'
9
9
  require 'contrast/utils/assess/source_method_utils'
10
+ require 'contrast/utils/assess/event_limit_utils'
10
11
  require 'contrast/agent/assess/events/event_data'
11
12
 
12
13
  module Contrast
@@ -19,6 +20,7 @@ module Contrast
19
20
  module SourceMethod
20
21
  extend Contrast::Components::Logger::InstanceMethods
21
22
  extend Contrast::Utils::Assess::SourceMethodUtils
23
+ extend Contrast::Utils::Assess::EventLimitUtils
22
24
 
23
25
  PARAMETER_TYPE = 'PARAMETER'
24
26
  PARAMETER_KEY_TYPE = 'PARAMETER_KEY'
@@ -37,6 +39,7 @@ module Contrast
37
39
  # @param args [Array<Object>] the Arguments with which the method was invoked
38
40
  def apply_source method_policy, object, ret, args
39
41
  return unless analyze?(method_policy, object, ret, args)
42
+ return if event_limit?(method_policy)
40
43
  return unless (source_node = method_policy.source_node)
41
44
 
42
45
  # used to hold the object and ret
@@ -66,6 +69,8 @@ module Contrast
66
69
  context = Contrast::Agent::REQUEST_TRACKER.current
67
70
  return unless context && source_node && target
68
71
 
72
+ increment_event_count(source_node)
73
+
69
74
  source_name ||= determine_source_name(source_node, source_data.object, source_data.ret, *args)
70
75
  # We know we only work on certain things.
71
76
  # Skip if this isn't one of them
@@ -25,6 +25,7 @@ module Contrast
25
25
  module TriggerMethod # rubocop:disable Metrics/ModuleLength
26
26
  extend Contrast::Components::Logger::InstanceMethods
27
27
  extend Contrast::Utils::Assess::TriggerMethodUtils
28
+ extend Contrast::Utils::Assess::EventLimitUtils
28
29
 
29
30
  # The level of TeamServer compliance our traces meet when in the abnormal condition of being dataflow rules
30
31
  # without routes.
@@ -48,6 +49,7 @@ module Contrast
48
49
  # @param args [Array<Object>] the Arguments with which the method was invoked
49
50
  def apply_trigger_rule trigger_node, object, ret, args
50
51
  return if trigger_node.nil?
52
+ return if event_limit_for_rule?(trigger_node.rule_id)
51
53
 
52
54
  context = Contrast::Agent::REQUEST_TRACKER.current
53
55
  # return if there is no context and the flag is set to default => false
@@ -102,6 +104,12 @@ module Contrast
102
104
  request = find_request(source)
103
105
  return unless reportable?(request&.env)
104
106
 
107
+ process_reportable_finding(trigger_node, source, object, ret, request, *args)
108
+ rescue StandardError => e
109
+ logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
110
+ end
111
+
112
+ def process_reportable_finding trigger_node, source, object, ret, request, *args
105
113
  if Contrast::Agent::Reporter.enabled?
106
114
  handle_new_finding(trigger_node, source, object, ret, request, *args)
107
115
  else # TODO: RUBY-1438 -- remove
@@ -112,8 +120,6 @@ module Contrast
112
120
  rule: trigger_node.rule_id)
113
121
  report_finding(finding, request)
114
122
  end
115
- rescue StandardError => e
116
- logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
117
123
  end
118
124
 
119
125
  # Given a finding, append it to an activity message and send it to the Service for processing. If an
@@ -12,6 +12,7 @@ module Contrast
12
12
  # have tightly coupled dependencies on each other.
13
13
  class Tracker
14
14
  PROPERTIES_HASH = Contrast::Agent::Assess::Finalizers::Hash.new
15
+ KEEP_AGE = 600_000.cs__freeze # 10 minutes
15
16
 
16
17
  class << self
17
18
  # Retrieve the properties of the given Object, iff they exist.
@@ -55,6 +56,17 @@ module Contrast
55
56
  def copy source, target
56
57
  PROPERTIES_HASH[target] ||= properties(source).dup
57
58
  end
59
+
60
+ # Clean PROPERTIES_HASH of any values older than KEEP_AGE ms or
61
+ # have nil properties
62
+ def cleanup!
63
+ PROPERTIES_HASH.delete_if do |_k, properties|
64
+ return true if properties.nil?
65
+ return false unless (event = properties&.event)
66
+
67
+ KEEP_AGE <= (Contrast::Utils::Timer.now_ms - event.time)
68
+ end
69
+ end
58
70
  end
59
71
  end
60
72
  end
@@ -66,7 +66,8 @@ module Contrast
66
66
  # TODO: RUBY-99999 - Remove when Rails 6.0 is not supported
67
67
  ActiveRecord::Base.connection_config
68
68
  end
69
- rescue StandardError
69
+ rescue StandardError => e
70
+ logger.error('Unable to detect db config connection', e)
70
71
  nil
71
72
  end
72
73
 
@@ -18,8 +18,8 @@ module Contrast
18
18
  # @return [Array<Contrast::Agent::Reporting::LibraryDiscovery>] direct report form of
19
19
  # Gem::Specification that have been loaded for this application.
20
20
  def library_pb_list
21
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.enabled?
22
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.analyze_libraries?
21
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.enable
22
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.analyze_libraries
23
23
 
24
24
  loaded_specs.each_with_object([]) do |(_name, spec), reported_lib_list|
25
25
  next unless spec
@@ -111,7 +111,7 @@ module Contrast
111
111
 
112
112
  # We only use this if inventory and library analysis are enabled
113
113
  def enabled?
114
- @_enabled = ::Contrast::INVENTORY.enabled? && ::Contrast::INVENTORY.analyze_libraries? if @_enabled.nil?
114
+ @_enabled = ::Contrast::INVENTORY.enable && ::Contrast::INVENTORY.analyze_libraries if @_enabled.nil?
115
115
  @_enabled
116
116
  end
117
117
  end
@@ -21,7 +21,7 @@ module Contrast
21
21
  DATA_STORE_MARKER = 'data_store'
22
22
 
23
23
  def report_data_store _method, _exception, properties, object, _args
24
- return unless ::Contrast::INVENTORY.enabled?
24
+ return unless ::Contrast::INVENTORY.enable
25
25
 
26
26
  marker = properties[DATA_STORE_MARKER]
27
27
  return unless marker
@@ -19,7 +19,7 @@ module Contrast
19
19
  end
20
20
 
21
21
  def disabled_globally?
22
- !::Contrast::INVENTORY.enabled?
22
+ !::Contrast::INVENTORY.enable
23
23
  end
24
24
 
25
25
  def node_type
@@ -17,9 +17,9 @@ module Contrast
17
17
  #
18
18
  # @param method_policy [Hash]
19
19
  # {
20
- # source_node [ Contrast::Agent::Assesss::Policy::SourceNode ]
21
- # propagation_node [ Contrast::Agent::Assesss::Policy::PropagationNode ]
22
- # trigger_node [ Contrast::Agent::Assesss::Policy::TriggerNode ]
20
+ # source_node [ Contrast::Agent::Assess::Policy::SourceNode ]
21
+ # propagation_node [ Contrast::Agent::Assess::Policy::PropagationNode ]
22
+ # trigger_node [ Contrast::Agent::Assess::Policy::TriggerNode ]
23
23
  # inventory_node [ Contrast::Agent::Inventory::Policy::TriggerNode ]
24
24
  # protect_node [ Contrast::Agent::Protect::Policy::TriggerNode ]
25
25
  # deadzone_node [ Contrast::Agent::Deadzone::Policy::DeadzoneNode ]
@@ -38,7 +38,7 @@ module Contrast
38
38
  attr_reader :mode
39
39
 
40
40
  def initialize
41
- ::Contrast::PROTECT.rules[rule_name] = self
41
+ ::Contrast::PROTECT.defend_rules[rule_name] = self
42
42
  @mode = mode_from_settings
43
43
  end
44
44
 
@@ -3,7 +3,6 @@
3
3
 
4
4
  require 'contrast/agent/worker_thread'
5
5
  require 'contrast/agent/reporting/report'
6
- require 'contrast/components/logger'
7
6
  require 'contrast/agent/inventory/dependency_usage_analysis'
8
7
  require 'contrast/agent/reporting/reporting_events/poll'
9
8
  require 'contrast/agent/reporting/reporting_events/server_activity'
@@ -14,8 +13,6 @@ module Contrast
14
13
  # reach out to get the latest settings for this application. It also sends out those messages which do not need to
15
14
  # be associated directly with a request, such as Server Activity and Library Observation.
16
15
  class ReporterHeartbeat < WorkerThread
17
- include Contrast::Components::Logger::InstanceMethods
18
-
19
16
  # TeamServer will mark an application offline after 5 minutes. Sending this every one should be more than enough
20
17
  # to satisfy our goals.
21
18
  REFRESH_INTERVAL_SEC = 60
@@ -29,6 +26,7 @@ module Contrast
29
26
  polling_events.each do |event|
30
27
  Contrast::Agent.reporter&.send_event(event)
31
28
  end
29
+ clean_properties
32
30
  sleep(REFRESH_INTERVAL_SEC)
33
31
  end
34
32
  end
@@ -56,35 +56,31 @@ module Contrast
56
56
  # @param attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
57
57
  # @param rule [String]
58
58
  def attach_existing existing_attacker_activity, attacker_activity, rule
59
- # TODO: RUBY-1663 figure out attack time mapping
60
59
  new_violation = attacker_activity.protection_rules[rule]
60
+ sample_activity = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity
61
61
  if (previously_violated = existing_attacker_activity.protection_rules[rule])
62
- if (new_blocked = new_violation.blocked&.samples)
63
- unless previously_violated.blocked
64
- previously_violated.blocked = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
65
- end
66
- previously_violated.blocked.samples.concat(new_blocked)
62
+ if (new_blocked = new_violation.blocked)
63
+ previously_violated.blocked ||= sample_activity.new
64
+ previously_violated.blocked.samples.concat(new_blocked.samples) if new_blocked.samples
65
+ previously_violated.blocked.merge_time_maps(new_blocked.time_map)
67
66
  end
68
67
 
69
- if (new_exploited = new_violation.exploited&.samples)
70
- unless previously_violated.exploited
71
- previously_violated.exploited = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
72
- end
73
- previously_violated.exploited.samples.concat(new_exploited)
68
+ if (new_exploited = new_violation.exploited)
69
+ previously_violated.exploited ||= sample_activity.new
70
+ previously_violated.exploited.samples.concat(new_exploited.samples) if new_exploited.samples
71
+ previously_violated.exploited.merge_time_maps(new_exploited.time_map)
74
72
  end
75
73
 
76
- if (new_ineffective = new_violation.ineffective&.samples)
77
- unless previously_violated.ineffective
78
- previously_violated.ineffective = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
79
- end
80
- previously_violated.ineffective.samples.concat(new_ineffective)
74
+ if (new_ineffective = new_violation.ineffective)
75
+ previously_violated.ineffective ||= sample_activity.new
76
+ previously_violated.ineffective.samples.concat(new_ineffective.samples) if new_ineffective.samples
77
+ previously_violated.ineffective.merge_time_maps(new_ineffective.time_map)
81
78
  end
82
79
 
83
- if (new_suspicious = new_violation.suspicious&.samples)
84
- unless previously_violated.suspicious
85
- previously_violated.suspicious = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
86
- end
87
- previously_violated.suspicious.samples.concat(new_suspicious)
80
+ if (new_suspicious = new_violation.suspicious)
81
+ previously_violated.suspicious ||= sample_activity.new
82
+ previously_violated.suspicious.samples.concat(new_suspicious.samples) if new_suspicious.samples
83
+ previously_violated.suspicious.merge_time_maps(new_suspicious.time_map)
88
84
  end
89
85
  else
90
86
  existing_attacker_activity.protection_rules[rule] = new_violation
@@ -16,7 +16,7 @@ module Contrast
16
16
  class ApplicationDefendAttackSample
17
17
  include Contrast::Agent::Reporting::InputType
18
18
 
19
- # @return [Hash]
19
+ # @return [Hash] => start: ms, elapsed: ms
20
20
  attr_reader :time_stamp
21
21
 
22
22
  class << self
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/components/logger'
5
+ require 'contrast/utils/timer'
5
6
  require 'contrast/agent/reporting/reporting_events/application_defend_attack_sample'
6
7
 
7
8
  module Contrast
@@ -26,7 +27,7 @@ module Contrast
26
27
 
27
28
  def initialize
28
29
  @samples = []
29
- @start_time = (Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0) / 1000
30
+ @start_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0 # in ms
30
31
  @event_type = :application_defend_attack_sample_activity
31
32
  @time_map = Hash.new { |h, k| h[k] = 0 }
32
33
  super
@@ -36,7 +37,7 @@ module Contrast
36
37
  {
37
38
  attackTimeMap: time_map,
38
39
  samples: samples.map(&:to_controlled_hash),
39
- startTime: @start_time,
40
+ startTime: @start_time, # Start time in ms.
40
41
  total: 1 # there will only ever be 1 attack sample, until batching is done
41
42
  }
42
43
  end
@@ -44,12 +45,34 @@ module Contrast
44
45
  # @param attack_result [Contrast::Api::Dtm::AttackResult]
45
46
  def attach_data attack_result
46
47
  attack_result.samples.each do |attack_sample|
48
+ base_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0
49
+ dmt_time = attack_sample.timestamp_ms.to_i
47
50
  converted = Contrast::Agent::Reporting::ApplicationDefendAttackSample.convert(attack_result, attack_sample)
48
51
  samples << converted
49
- attack_second = converted.time_stamp[:elapsed] / 1000
52
+ @start_time = if dmt_time.zero?
53
+ @start_time
54
+ else
55
+ dmt_time
56
+ end
57
+ attack_second = (@start_time - base_time) / 1000 # in seconds
50
58
  time_map[attack_second] += 1
51
59
  end
52
60
  end
61
+
62
+ # This method will merge time_maps of attack samples with same
63
+ # type.
64
+ #
65
+ # @param map [Hash<Integer,Integer>] TimeMap to append to previously_violated rule
66
+ # samples.
67
+ # @return time_map [Hash<Integer,Integer>] merged time map with updated occurrences.
68
+ def merge_time_maps map
69
+ # If the second is the same (key) if we just merge there won't be a new entry,
70
+ # so just increase the attack count.
71
+ map.each_key do |key|
72
+ @time_map[key] = @time_map.fetch(key, 0) + map[key]
73
+ end
74
+ @time_map
75
+ end
53
76
  end
54
77
  end
55
78
  end
@@ -26,12 +26,12 @@ module Contrast
26
26
  # event field of Contrast::Agent::Reporting::ReportingEvent
27
27
  # @param response_data [Net::HTTP::Response]
28
28
  def audit_event event, response_data = nil
29
- return unless ::Contrast::API.request_audit_requests? || ::Contrast::API.request_audit_responses?
29
+ return unless ::Contrast::API.request_audit_requests || ::Contrast::API.request_audit_responses
30
30
 
31
31
  file_name = event.cs__respond_to?(:file_name) ? event.file_name : event.cs__class.cs__name.to_s.downcase
32
32
  data = event.to_controlled_hash.to_json
33
33
  log_data(:request, file_name, data) if data
34
- return unless ::Contrast::API.request_audit_responses?
34
+ return unless ::Contrast::API.request_audit_responses
35
35
 
36
36
  data = response_data&.body || 'There is no available response'
37
37
  log_data(:response, file_name, data)
@@ -94,7 +94,7 @@ module Contrast
94
94
  # Retrieves the configuration value if the request audit is enabled
95
95
  # @return [Boolean]
96
96
  def enabled?
97
- ::Contrast::API.request_audit_enable?
97
+ ::Contrast::API.request_audit_enable
98
98
  end
99
99
 
100
100
  # The boolean values for the requests and the responses should be taken under
@@ -107,13 +107,13 @@ module Contrast
107
107
  # Retrieve the configuration value if the audit for requests is enabled
108
108
  # @return [Boolean]
109
109
  def enabled_for_requests?
110
- ::Contrast::API.request_audit_requests?
110
+ ::Contrast::API.request_audit_requests
111
111
  end
112
112
 
113
113
  # Retrieve the configuration value if the audit for responses is enabled
114
114
  # @return [Boolean]
115
115
  def enabled_for_responses?
116
- ::Contrast::API.request_audit_requests?
116
+ ::Contrast::API.request_audit_requests
117
117
  end
118
118
 
119
119
  # Retrieve the configuration value for the path of the audits
@@ -24,7 +24,7 @@ module Contrast
24
24
  @app_language = RUBY
25
25
  @app_path = Base64.strict_encode64(Contrast::APP_CONTEXT.path)
26
26
  @app_version = Contrast::APP_CONTEXT.app_version
27
- @authorization = Base64.strict_encode64("#{ Contrast::API.username }:#{ Contrast::API.service_key }")
27
+ @authorization = Base64.strict_encode64("#{ Contrast::API.user_name }:#{ Contrast::API.service_key }")
28
28
  @server_name = Base64.strict_encode64(Contrast::APP_CONTEXT.server_name)
29
29
  @server_path = Base64.strict_encode64(Contrast::APP_CONTEXT.server_path)
30
30
  @server_type = Base64.strict_encode64(Contrast::APP_CONTEXT.server_type)
@@ -59,7 +59,7 @@ module Contrast
59
59
 
60
60
  request = build_request(event)
61
61
  response = connection.request(request)
62
- audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable?
62
+ audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable
63
63
  process_settings_response(response)
64
64
  process_preflight_response(event, response, connection)
65
65
  response
@@ -185,7 +185,7 @@ module Contrast
185
185
  ::Contrast::SETTINGS.build_protect_rules if ::Contrast::PROTECT.enabled?
186
186
  ::Contrast::AGENT.reset_ruleset
187
187
  logger.info('Current rule settings:')
188
- ::Contrast::PROTECT.rules.each { |k, v| logger.info('Protect Rule mode set', rule: k, mode: v.mode) }
188
+ ::Contrast::PROTECT.defend_rules.each { |k, v| logger.info('Protect Rule mode set', rule: k, mode: v.mode) }
189
189
  logger.info('Disabled Assess Rules', rules: ::Contrast::ASSESS.disabled_rules)
190
190
  end
191
191
  end
@@ -43,6 +43,8 @@ module Contrast
43
43
  # @return [Contrast::Utils::Timer] when the context was created
44
44
  attr_reader :timer
45
45
 
46
+ attr_accessor :propagation_event_count, :source_event_count
47
+
46
48
  def initialize rack_request, app_loaded: true
47
49
  with_contrast_scope do
48
50
  # all requests get a timer and hash
@@ -68,6 +70,12 @@ module Contrast
68
70
  # generic holder for properties that can be set throughout this request
69
71
  @_properties = {}
70
72
 
73
+ # count of propagation events
74
+ @propagation_event_count = 0
75
+
76
+ # count of source events
77
+ @source_event_count = 0
78
+
71
79
  if ::Contrast::ASSESS.enabled?
72
80
  @sample_req, @sample_res = Contrast::Utils::Assess::SamplingUtil.instance.sample?(@request)
73
81
  end
@@ -1,7 +1,6 @@
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/components/logger'
5
4
  require 'contrast/agent/worker_thread'
6
5
  require 'contrast/agent/reporting/report'
7
6
 
@@ -10,8 +9,6 @@ module Contrast
10
9
  # The ServiceHeartbeat functions to keep the Contrast Service alive and ensure that it maintains this Agent's
11
10
  # ApplicationContext.
12
11
  class ServiceHeartbeat < WorkerThread
13
- include Contrast::Components::Logger::InstanceMethods
14
-
15
12
  # Spec recommends 30 seconds, we're going with 15.
16
13
  REFRESH_INTERVAL_SEC = 15
17
14
 
@@ -22,7 +19,9 @@ module Contrast
22
19
  @_thread = Contrast::Agent::Thread.new do
23
20
  logger.info('Starting heartbeat thread.')
24
21
  loop do
22
+ logger.info("Queue Size: #{ Contrast::Agent.messaging_queue.queue&.length }")
25
23
  Contrast::Agent.messaging_queue&.send_event_eventually(poll_message)
24
+ clean_properties
26
25
  sleep(REFRESH_INTERVAL_SEC)
27
26
  end
28
27
  end
@@ -24,7 +24,7 @@ module Contrast
24
24
  end
25
25
 
26
26
  def send_inventory_message
27
- return unless ::Contrast::INVENTORY.enabled?
27
+ return unless ::Contrast::INVENTORY.enable
28
28
 
29
29
  report = Contrast::Agent::Reporting::ApplicationUpdate.new
30
30
  # This convert here is left as it'll be easier to be replaced when the Library is being changed
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '6.4.0'
6
+ VERSION = '6.6.0'
7
7
  end
8
8
  end