contrast-agent 6.3.0 → 6.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/.simplecov +1 -0
  4. data/Rakefile +0 -27
  5. data/ext/cs__contrast_patch/cs__contrast_patch.c +14 -1
  6. data/lib/contrast/agent/assess/finalizers/hash.rb +1 -0
  7. data/lib/contrast/agent/assess/policy/propagation_method.rb +5 -3
  8. data/lib/contrast/agent/assess/policy/propagator/custom.rb +4 -0
  9. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +5 -0
  10. data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -0
  11. data/lib/contrast/agent/assess/policy/source_method.rb +5 -0
  12. data/lib/contrast/agent/assess/policy/trigger_method.rb +9 -3
  13. data/lib/contrast/agent/assess/tracker.rb +12 -0
  14. data/lib/contrast/agent/inventory/dependency_analysis.rb +2 -2
  15. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -1
  16. data/lib/contrast/agent/inventory/policy/datastores.rb +1 -1
  17. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  18. data/lib/contrast/agent/patching/policy/method_policy.rb +3 -3
  19. data/lib/contrast/agent/protect/rule/base.rb +1 -1
  20. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +1 -3
  21. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +17 -21
  22. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +1 -1
  23. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +26 -3
  24. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +5 -5
  25. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -1
  26. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
  27. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
  28. data/lib/contrast/agent/request_context.rb +8 -0
  29. data/lib/contrast/agent/service_heartbeat.rb +2 -3
  30. data/lib/contrast/agent/static_analysis.rb +1 -1
  31. data/lib/contrast/agent/version.rb +1 -1
  32. data/lib/contrast/agent/worker_thread.rb +10 -0
  33. data/lib/contrast/api/communication/response_processor.rb +1 -1
  34. data/lib/contrast/api/dtm.pb.rb +1 -1
  35. data/lib/contrast/api/settings.pb.rb +1 -1
  36. data/lib/contrast/components/agent.rb +52 -14
  37. data/lib/contrast/components/api.rb +60 -23
  38. data/lib/contrast/components/assess.rb +16 -0
  39. data/lib/contrast/components/contrast_service.rb +1 -1
  40. data/lib/contrast/components/heap_dump.rb +51 -1
  41. data/lib/contrast/components/inventory.rb +19 -13
  42. data/lib/contrast/components/logger.rb +18 -0
  43. data/lib/contrast/components/protect.rb +41 -1
  44. data/lib/contrast/components/sampling.rb +29 -0
  45. data/lib/contrast/config/assess_configuration.rb +33 -3
  46. data/lib/contrast/config/base_configuration.rb +8 -2
  47. data/lib/contrast/config/root_configuration.rb +19 -16
  48. data/lib/contrast/config/service_configuration.rb +4 -4
  49. data/lib/contrast/config.rb +0 -9
  50. data/lib/contrast/extension/object.rb +19 -0
  51. data/lib/contrast/framework/rails/support.rb +4 -1
  52. data/lib/contrast/logger/log.rb +2 -1
  53. data/lib/contrast/utils/assess/event_limit_utils.rb +96 -0
  54. data/lib/contrast/utils/assess/propagation_method_utils.rb +27 -7
  55. data/lib/contrast/utils/log_utils.rb +2 -2
  56. data/lib/contrast/utils/net_http_base.rb +2 -2
  57. data/lib/contrast/utils/patching/policy/patch_utils.rb +6 -23
  58. data/lib/contrast.rb +39 -20
  59. data/lib/protobuf/code_generator.rb +129 -0
  60. data/lib/protobuf/decoder.rb +28 -0
  61. data/lib/protobuf/deprecation.rb +117 -0
  62. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +79 -0
  63. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +360 -0
  64. data/lib/protobuf/descriptors.rb +3 -0
  65. data/lib/protobuf/encoder.rb +11 -0
  66. data/lib/protobuf/enum.rb +365 -0
  67. data/lib/protobuf/exceptions.rb +9 -0
  68. data/lib/protobuf/field/base_field.rb +380 -0
  69. data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
  70. data/lib/protobuf/field/bool_field.rb +64 -0
  71. data/lib/protobuf/field/bytes_field.rb +67 -0
  72. data/lib/protobuf/field/double_field.rb +25 -0
  73. data/lib/protobuf/field/enum_field.rb +56 -0
  74. data/lib/protobuf/field/field_array.rb +102 -0
  75. data/lib/protobuf/field/field_hash.rb +122 -0
  76. data/lib/protobuf/field/fixed32_field.rb +25 -0
  77. data/lib/protobuf/field/fixed64_field.rb +28 -0
  78. data/lib/protobuf/field/float_field.rb +43 -0
  79. data/lib/protobuf/field/int32_field.rb +21 -0
  80. data/lib/protobuf/field/int64_field.rb +34 -0
  81. data/lib/protobuf/field/integer_field.rb +23 -0
  82. data/lib/protobuf/field/message_field.rb +51 -0
  83. data/lib/protobuf/field/sfixed32_field.rb +27 -0
  84. data/lib/protobuf/field/sfixed64_field.rb +28 -0
  85. data/lib/protobuf/field/signed_integer_field.rb +29 -0
  86. data/lib/protobuf/field/sint32_field.rb +21 -0
  87. data/lib/protobuf/field/sint64_field.rb +21 -0
  88. data/lib/protobuf/field/string_field.rb +51 -0
  89. data/lib/protobuf/field/uint32_field.rb +21 -0
  90. data/lib/protobuf/field/uint64_field.rb +21 -0
  91. data/lib/protobuf/field/varint_field.rb +77 -0
  92. data/lib/protobuf/field.rb +74 -0
  93. data/lib/protobuf/generators/base.rb +85 -0
  94. data/lib/protobuf/generators/enum_generator.rb +39 -0
  95. data/lib/protobuf/generators/extension_generator.rb +27 -0
  96. data/lib/protobuf/generators/field_generator.rb +193 -0
  97. data/lib/protobuf/generators/file_generator.rb +262 -0
  98. data/lib/protobuf/generators/group_generator.rb +122 -0
  99. data/lib/protobuf/generators/message_generator.rb +104 -0
  100. data/lib/protobuf/generators/option_generator.rb +17 -0
  101. data/lib/protobuf/generators/printable.rb +160 -0
  102. data/lib/protobuf/generators/service_generator.rb +50 -0
  103. data/lib/protobuf/lifecycle.rb +33 -0
  104. data/lib/protobuf/logging.rb +39 -0
  105. data/lib/protobuf/message/fields.rb +233 -0
  106. data/lib/protobuf/message/serialization.rb +85 -0
  107. data/lib/protobuf/message.rb +241 -0
  108. data/lib/protobuf/optionable.rb +72 -0
  109. data/lib/protobuf/tasks/compile.rake +80 -0
  110. data/lib/protobuf/tasks.rb +1 -0
  111. data/lib/protobuf/varint.rb +20 -0
  112. data/lib/protobuf/varint_pure.rb +31 -0
  113. data/lib/protobuf/version.rb +3 -0
  114. data/lib/protobuf/wire_type.rb +10 -0
  115. data/lib/protobuf.rb +91 -0
  116. data/proto/dynamic_discovery.proto +46 -0
  117. data/proto/google/protobuf/compiler/plugin.proto +183 -0
  118. data/proto/google/protobuf/descriptor.proto +911 -0
  119. data/proto/rpc.proto +71 -0
  120. data/resources/assess/policy.json +15 -12
  121. data/resources/deadzone/policy.json +132 -19
  122. data/ruby-agent.gemspec +3 -1
  123. metadata +112 -28
  124. data/lib/contrast/config/agent_configuration.rb +0 -63
  125. data/lib/contrast/config/api_configuration.rb +0 -56
  126. data/lib/contrast/config/heap_dump_configuration.rb +0 -59
  127. data/lib/contrast/config/inventory_configuration.rb +0 -33
  128. data/lib/contrast/config/logger_configuration.rb +0 -26
  129. data/lib/contrast/config/protect_configuration.rb +0 -33
  130. data/lib/contrast/config/sampling_configuration.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b7fbfe09d8f01268274ec60a3861fc4377278e0e33aafadd29a13aec59348bb
4
- data.tar.gz: caf12a68dca338f4e1ed0e04483857cd7c55140d8698f1aa55b82c6f85d210c6
3
+ metadata.gz: 958ef5b303e23294af4b85759012ab4c80cb0000d97088b4c8f495560701b91e
4
+ data.tar.gz: 714c35c53e89cc2f6335fda57827c5eba620e2ee42ede41d586f072f074defb8
5
5
  SHA512:
6
- metadata.gz: cd9eb2d8fd9faf79ad16e0d32889806c93178f696ad64aa58cdc046e46c1cb5e0ff463e54cf81e491518fd732a664f5838147f83c791c869d3689695b1255384
7
- data.tar.gz: '097d53ac525825b0b3b305dafe1be2cc178f7198e057f292561e7ffa4801476dcf151d9a51d6277033dbaf149f6ed0d9b52606aebebeffc7636564eea59bbf74'
6
+ metadata.gz: a3e1c9a23238e9c3a6727fcd19e1ddf177ac699fcb2200eb1ae190f6a9022a3c5b8e0ee109c90793634cbeb54245f07e3b043e244664782f4a83d34896270baa
7
+ data.tar.gz: 79b54ad82d5df30ff7c95499d0862947703a6f6dadab29d3f946072d6c91cd84e3a4207dad71e2be9584b720471325d8eb8fb7d124c4bf4136c4b01a225560bf
data/.gitignore CHANGED
@@ -56,8 +56,5 @@ contrast-agent-*.gem
56
56
  .ruby-gemset
57
57
  service_executables/*-*
58
58
 
59
- # Generated Protobuf files
60
- /lib/contrast/api/*.pb.rb
61
-
62
59
  # IDE stuff
63
60
  tags
data/.simplecov CHANGED
@@ -4,5 +4,6 @@
4
4
  SimpleCov.minimum_coverage(line: 94)
5
5
  SimpleCov.start do
6
6
  add_filter '/spec/'
7
+ add_filter '/lib/protobuf/'
7
8
  enable_coverage :branch
8
9
  end
data/Rakefile CHANGED
@@ -17,30 +17,3 @@ Dir['ext/cs__*'].each do |extension|
17
17
  ext.lib_dir = "lib/#{ name }"
18
18
  end
19
19
  end
20
-
21
- desc 'compile the protobuf files for the agent, translating them to .rb classes'
22
- task :contrast_pb_compile do
23
- # do some stuff before compile
24
-
25
- # Invoke the protobuf compile task with your sensible defaults
26
- ::Rake::Task['protobuf:compile'].invoke('lib', './agent-service-api/protobuf ./agent-service-api/protobuf/dtm.proto',
27
- 'lib/contrast/api',
28
- nil)
29
-
30
- ::Rake::Task['protobuf:compile'].reenable
31
-
32
- ::Rake::Task['protobuf:compile'].invoke('lib',
33
- './agent-service-api/protobuf ./agent-service-api/protobuf/settings.proto',
34
- 'lib/contrast/api',
35
- nil)
36
-
37
- ['dtm.pb.rb', 'settings.pb.rb'].each do |target_file|
38
- target_path = File.absolute_path(File.join(__dir__, "./lib/contrast/api/#{ target_file }"))
39
- unless File.exist?(target_path)
40
- puts "File not found #{ target_path }"
41
- exit 1
42
- end
43
- end
44
-
45
- puts 'Protobuf copied successfully'
46
- end
@@ -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)
@@ -58,8 +61,6 @@ module Contrast
58
61
  # ret [Object] the Return of the invoked method
59
62
  # args [Array<Object>] the Arguments with which the method was invoked
60
63
  # @param block [Block] the Block passed to the original method
61
- # @return [Object, nil] the tracked Return or nil if no changes were made; will replace the return of the
62
- # original function if not nil
63
64
  def apply_propagator propagation_node, preshift, target, propagation_data, block
64
65
  return unless propagation_possible?(propagation_node, target)
65
66
 
@@ -186,7 +187,7 @@ module Contrast
186
187
  # @param _block [Block] the Block passed to the original method
187
188
  def handle_cs_properties_propagation propagation_node, preshift, target, propagation_data, _block
188
189
  return if propagation_node.action == NOOP_ACTION
189
- return unless can_propagate?(propagation_node, preshift, target)
190
+ return unless can_propagate?(propagation_node, preshift, target, propagation_data)
190
191
  return unless (propagation_class = find_propagation_class(propagation_node))
191
192
 
192
193
  # If we are using the original object tracking, the preshift object is not created.
@@ -194,6 +195,7 @@ module Contrast
194
195
  source = propagation_node.use_original_object? ? propagation_data.object : preshift
195
196
  handle_propagation(propagation_class, propagation_node, source, target)
196
197
  update_properties(propagation_node, target, propagation_data)
198
+ increment_event_count(propagation_node)
197
199
  end
198
200
 
199
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
@@ -211,7 +217,7 @@ module Contrast
211
217
 
212
218
  build_events(finding, properties.event) if properties.event
213
219
 
214
- # Google::Protobuf::Map doesn't support merge!, so we have to do this long form
220
+ # CSGoogle::Protobuf::Map doesn't support merge!, so we have to do this long form
215
221
  source_props = properties.properties
216
222
  return unless source_props
217
223
 
@@ -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
@@ -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.enabled
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