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
@@ -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/components/sampling'
5
+
4
6
  module Contrast
5
7
  module Config
6
8
  # Common Configuration settings. Those in this section pertain to the
@@ -15,6 +17,10 @@ module Contrast
15
17
  attr_writer :enable_scan_response, :enable_dynamic_sources, :sampling, :rules, :stacktraces
16
18
 
17
19
  DEFAULT_STACKTRACES = 'ALL'
20
+ DEFAULT_MAX_SOURCE_EVENTS = 50_000
21
+ DEFAULT_MAX_PROPAGATION_EVENTS = 50_000
22
+ DEFAULT_MAX_RULE_REPORTED = 50_000
23
+ DEFAULT_MAX_RULE_TIME_THRESHOLD = 300_000
18
24
 
19
25
  def initialize hsh = {}
20
26
  return unless hsh
@@ -24,9 +30,13 @@ module Contrast
24
30
  @enable_scan_response = hsh[:enable_scan_response]
25
31
  @enable_dynamic_sources = hsh[:enable_dynamic_sources]
26
32
  @enable_original_object = hsh[:enable_original_object]
27
- @sampling = Contrast::Config::SamplingConfiguration.new(hsh[:sampling])
33
+ @sampling = Contrast::Components::Sampling::Interface.new(hsh[:sampling])
28
34
  @rules = Contrast::Config::AssessRulesConfiguration.new(hsh[:rules])
29
35
  @stacktraces = hsh[:stacktraces]
36
+ @max_context_source_events = hsh[:max_context_source_events]
37
+ @max_propagation_events = hsh[:max_propagation_events]
38
+ @max_rule_reported = hsh[:max_rule_reported]
39
+ @time_limit_threshold = hsh[:time_limit_threshold]
30
40
  end
31
41
 
32
42
  # @return [Boolean, true]
@@ -44,9 +54,9 @@ module Contrast
44
54
  @enable_original_object.nil? ? true : @enable_original_object
45
55
  end
46
56
 
47
- # @return [Contrast::Config::SamplingConfiguration]
57
+ # @return [Contrast::Components::Sampling::Interface]
48
58
  def sampling
49
- @sampling ||= Contrast::Config::SamplingConfiguration.new
59
+ @sampling ||= Contrast::Components::Sampling::Interface.new
50
60
  end
51
61
 
52
62
  # @return [Contrast::Config::AssessRulesConfiguration]
@@ -58,6 +68,26 @@ module Contrast
58
68
  def stacktraces
59
69
  @stacktraces ||= DEFAULT_STACKTRACES
60
70
  end
71
+
72
+ # @return [int] max number of context source events in single request
73
+ def max_context_source_events
74
+ @max_context_source_events ||= DEFAULT_MAX_SOURCE_EVENTS
75
+ end
76
+
77
+ # @return [int] max number of propagation events in single request
78
+ def max_propagation_events
79
+ @max_propagation_events ||= DEFAULT_MAX_PROPAGATION_EVENTS
80
+ end
81
+
82
+ # @return [int] max number of rules reported within time_limit_threshold
83
+ def max_rule_reported
84
+ @max_rule_reported ||= DEFAULT_MAX_RULE_REPORTED
85
+ end
86
+
87
+ # @return [int] max ms threshold for reporting rules
88
+ def time_limit_threshold
89
+ @time_limit_threshold ||= DEFAULT_MAX_RULE_TIME_THRESHOLD
90
+ end
61
91
  end
62
92
  end
63
93
  end
@@ -10,12 +10,18 @@ module Contrast
10
10
  # Configuration settings to usable Ruby classes.
11
11
  module BaseConfiguration
12
12
  extend Forwardable
13
+ AT_UNDERSCORE = '@_'
13
14
 
14
15
  def to_hash
15
16
  hsh = {}
16
17
  instance_variables.each do |iv|
17
- # strip the '@' to get the key
18
- key = iv.to_s[1..]
18
+ # strip the '@' of '@_' to get the key
19
+ string_iv = iv.to_s
20
+ key = if string_iv.include?(AT_UNDERSCORE)
21
+ string_iv[2..]
22
+ else
23
+ string_iv[1..]
24
+ end
19
25
  hsh[key] = send(key.to_sym)
20
26
  end
21
27
  hsh
@@ -1,15 +1,18 @@
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/agent'
5
+ require 'contrast/components/inventory'
6
+ require 'contrast/components/protect'
4
7
  module Contrast
5
8
  module Config
6
9
  # The base of the Common Configuration settings.
7
10
  class RootConfiguration
8
11
  include Contrast::Config::BaseConfiguration
9
12
 
10
- # @return [Contrast::Config::ApiConfiguration]
13
+ # @return [Contrast::Components::Api::Interface]
11
14
  attr_writer :api
12
- # @return [Contrast::Config::AgentConfiguration]
15
+ # @return [Contrast::Components::Agent::Interface]
13
16
  attr_writer :agent
14
17
  # @return [Contrast::Config::ApplicationConfiguration]
15
18
  attr_writer :application
@@ -17,9 +20,9 @@ module Contrast
17
20
  attr_writer :server
18
21
  # @return [Contrast::Config::AssessConfiguration]
19
22
  attr_writer :assess
20
- # @return [Contrast::Config::InventoryConfiguration]
23
+ # @return [Contrast::Components::Inventory::Interface]
21
24
  attr_writer :inventory
22
- # @return [Contrast::Config::ProtectConfiguration]
25
+ # @return [Contrast::Components::Protect::Interface]
23
26
  attr_writer :protect
24
27
  # @return [Contrast::Config::ServiceConfiguration]
25
28
  attr_writer :service
@@ -30,25 +33,25 @@ module Contrast
30
33
  def initialize hsh = {}
31
34
  raise(ArgumentError, 'Expected a hash') unless hsh.is_a?(Hash)
32
35
 
33
- @api = Contrast::Config::ApiConfiguration.new(hsh[:api])
36
+ @api = Contrast::Components::Api::Interface.new(hsh[:api])
34
37
  @enable = hsh[:enable]
35
- @agent = Contrast::Config::AgentConfiguration.new(hsh[:agent])
38
+ @agent = Contrast::Components::Agent::Interface.new(hsh[:agent])
36
39
  @application = Contrast::Config::ApplicationConfiguration.new(hsh[:application])
37
40
  @server = Contrast::Config::ServerConfiguration.new(hsh[:server])
38
41
  @assess = Contrast::Config::AssessConfiguration.new(hsh[:assess])
39
- @inventory = Contrast::Config::InventoryConfiguration.new(hsh[:inventory])
40
- @protect = Contrast::Config::ProtectConfiguration.new(hsh[:protect])
42
+ @inventory = Contrast::Components::Inventory::Interface.new(hsh[:inventory])
43
+ @protect = Contrast::Components::Protect::Interface.new(hsh[:protect])
41
44
  @service = Contrast::Config::ServiceConfiguration.new(hsh[:service])
42
45
  end
43
46
 
44
- # @return [Contrast::Config::ApiConfiguration]
47
+ # @return [Contrast::Components::Api::Interface]
45
48
  def api
46
- @api ||= Contrast::Config::ApiConfiguration.new
49
+ @api ||= Contrast::Components::Api::Interface.new
47
50
  end
48
51
 
49
- # @return [Contrast::Config::AgentConfiguration]
52
+ # @return [Contrast::Components::Agent::Interface]
50
53
  def agent
51
- @agent ||= Contrast::Config::AgentConfiguration.new
54
+ @agent ||= Contrast::Components::Agent::Interface.new
52
55
  end
53
56
 
54
57
  # @return [Contrast::Config::ApplicationConfiguration]
@@ -66,14 +69,14 @@ module Contrast
66
69
  @assess ||= Contrast::Config::AssessConfiguration.new
67
70
  end
68
71
 
69
- # @return [Contrast::Config::InventoryConfiguration]
72
+ # @return [Contrast::Components::Inventory::Interface]
70
73
  def inventory
71
- @inventory ||= Contrast::Config::InventoryConfiguration.new
74
+ @inventory ||= Contrast::Components::Inventory::Interface.new
72
75
  end
73
76
 
74
- # @return [Contrast::Config::ProtectConfiguration]
77
+ # @return [Contrast::Components::Protect::Interface]
75
78
  def protect
76
- @protect ||= Contrast::Config::ProtectConfiguration.new
79
+ @protect ||= Contrast::Components::Protect::Interface.new
77
80
  end
78
81
 
79
82
  # @return [Contrast::Config::ServiceConfiguration]
@@ -1,7 +1,7 @@
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/config/logger_configuration'
4
+ require 'contrast/components/logger'
5
5
 
6
6
  module Contrast
7
7
  module Config
@@ -31,13 +31,13 @@ module Contrast
31
31
  @host = hsh[:host]
32
32
  @port = hsh[:port]
33
33
  @socket = hsh[:socket]
34
- @logger = Contrast::Config::LoggerConfiguration.new(hsh[:logger])
34
+ @logger = Contrast::Components::Logger::Interface.new(hsh[:logger])
35
35
  @bypass = hsh[:bypass]
36
36
  end
37
37
 
38
- # @return [Contrast::Config::LoggerConfiguration]
38
+ # @return [Contrast::Components::Logger::Interface]
39
39
  def logger
40
- @logger ||= Contrast::Config::LoggerConfiguration.new
40
+ @logger ||= Contrast::Components::Logger::Interface.new
41
41
  end
42
42
 
43
43
  # @return [Boolean, false]
@@ -11,23 +11,14 @@ module Contrast
11
11
  end
12
12
 
13
13
  require 'contrast/config/base_configuration'
14
-
15
- require 'contrast/config/logger_configuration'
16
-
17
- require 'contrast/config/heap_dump_configuration'
18
14
  require 'contrast/config/service_configuration'
19
15
  require 'contrast/config/exception_configuration'
20
16
  require 'contrast/config/assess_rules_configuration'
21
17
  require 'contrast/config/protect_rule_configuration'
22
18
  require 'contrast/config/protect_rules_configuration'
23
- require 'contrast/config/sampling_configuration'
24
19
 
25
20
  require 'contrast/config/ruby_configuration'
26
- require 'contrast/config/api_configuration'
27
- require 'contrast/config/agent_configuration'
28
21
  require 'contrast/config/application_configuration'
29
22
  require 'contrast/config/server_configuration'
30
23
  require 'contrast/config/assess_configuration'
31
- require 'contrast/config/inventory_configuration'
32
- require 'contrast/config/protect_configuration'
33
24
  require 'contrast/config/root_configuration'
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ # Some developers override various methods on Object, which can often involve
5
+ # changing expected method parity/behavior which in turn prevents us from being
6
+ # able to reliably use affected methods.
7
+ # We alias these method so that we always have access to them.
8
+ #
9
+ # Because we use these methods in constructing classes (e.g., calling #freeze
10
+ # on constants within class definitions) we do this aliasing ASAP.
11
+ class Object
12
+ alias_method :cs__class, :class
13
+ alias_method :cs__freeze, :freeze
14
+ alias_method :cs__frozen?, :frozen?
15
+ alias_method :cs__is_a?, :is_a?
16
+ alias_method :cs__method, :method
17
+ alias_method :cs__respond_to?, :respond_to?
18
+ alias_method :cs__singleton_class, :singleton_class
19
+ end
@@ -135,8 +135,11 @@ module Contrast
135
135
  # @return [bool] whether the router is an engine or not.
136
136
  def engine_route? route
137
137
  return false unless route&.app&.app
138
+ return false unless route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) ||
139
+ route.app.is_a?(::ActionDispatch::Routing::RouteSet::Dispatcher)
138
140
 
139
- route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) && route.app.app < ::Rails::Engine
141
+ clazz = route.app.app.is_a?(Class) ? route.app.app : route.app.app.cs__class
142
+ clazz < ::Rails::Engine
140
143
  end
141
144
 
142
145
  # Recursively get final route traversing engines as required. Because this can only be called once, we store
@@ -134,7 +134,8 @@ module Contrast
134
134
 
135
135
  enable_trace_timing if current_level_const == ::Ougai::Logging::TRACE
136
136
 
137
- @_logger = build(path: current_path, level_const: current_level_const)
137
+ progname = Contrast::CONFIG.root.agent.logger.progname
138
+ @_logger = build(path: current_path, level_const: current_level_const, progname: progname)
138
139
  # If we're logging to a new path, then let's start it w/ our helpful
139
140
  # data gathering messages
140
141
  log_update if path_change
@@ -0,0 +1,96 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/components/logger'
5
+
6
+ module Contrast
7
+ module Utils
8
+ module Assess
9
+ # EventLimitUtils is used to check and validate the number of source, propagation, or trigger events collected
10
+ # during the reporting time frame
11
+ module EventLimitUtils
12
+ include Contrast::Components::Logger::InstanceMethods
13
+ # Checks to see if the event limit for the policy type has been met or exceeded
14
+ # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] method to check for event limit
15
+ def event_limit? method_policy
16
+ return false unless (context = Contrast::Agent::REQUEST_TRACKER.current)
17
+
18
+ if method_policy.source_node
19
+ max = (::Contrast::ASSESS.max_source_events ||
20
+ Contrast::Config::AssessConfiguration::DEFAULT_MAX_SOURCE_EVENTS)
21
+ return at_limit?(method_policy, context.source_event_count, max)
22
+
23
+ end
24
+ if method_policy.propagation_node
25
+ max = (::Contrast::ASSESS.max_propagation_events ||
26
+ Contrast::Config::AssessConfiguration::DEFAULT_MAX_PROPAGATION_EVENTS)
27
+ return at_limit?(method_policy, context.propagation_event_count, max)
28
+ end
29
+
30
+ false # policy does not have limit
31
+ end
32
+
33
+ def event_limit_for_rule? rule_id
34
+ if Contrast::Utils::Timer.now_ms > threshold_time_limit
35
+ @_rule_counts = nil
36
+ @_threshold_time_limit = nil
37
+ threshold_time_limit
38
+ end
39
+ rule_counts[rule_id] += 1
40
+ # TODO: RUBY-1680 remove default
41
+ rule_counts[rule_id] >=
42
+ (::Contrast::ASSESS.max_rule_reported || Contrast::Config::AssessConfiguration::DEFAULT_MAX_RULE_REPORTED)
43
+ end
44
+
45
+ # Increments the event count for the type of event that is being tracked
46
+ #
47
+ # @param node [Contrast::Agent::Assess::Policy::PolicyNode] policy to increment
48
+ def increment_event_count node
49
+ return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
50
+
51
+ context.source_event_count += 1 if node.cs__is_a?(Contrast::Agent::Assess::Policy::SourceNode)
52
+ context.propagation_event_count += 1 if node.cs__is_a?(Contrast::Agent::Assess::Policy::PropagationNode)
53
+ end
54
+
55
+ private
56
+
57
+ # helper method to check limit and log when necessary
58
+ def at_limit? method_policy, current_count, event_max
59
+ if current_count == event_max
60
+ logger.warn('Event Limit Reached:',
61
+ {
62
+ count: current_count,
63
+ max: event_max,
64
+ policy: method_policy.method_name,
65
+ node: method_policy
66
+ })
67
+ # increment to be over count for logging purposes
68
+ increment_event_count(method_policy)
69
+ return true
70
+ elsif current_count > event_max
71
+ # increment to be over count for logging purposes
72
+ increment_event_count(method_policy)
73
+ logger.warn('Event Limit Exceeded:',
74
+ {
75
+ count: current_count,
76
+ policy: method_policy.method_name,
77
+ node: method_policy
78
+ })
79
+ return true
80
+ end
81
+ false
82
+ end
83
+
84
+ def rule_counts
85
+ @_rule_counts ||= Hash.new { |h, k| h[k] = 0 }
86
+ end
87
+
88
+ # the time threshold for which to track rule counts resets when now >= threshold_time_limit
89
+ # @return [Integer]
90
+ def threshold_time_limit
91
+ @_threshold_time_limit ||= Contrast::Utils::Timer.now_ms + (::Contrast::ASSESS.time_limit_threshold || 0)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -92,20 +92,24 @@ module Contrast
92
92
  # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
93
93
  # the invocation of the patched method.
94
94
  # @param target [Object] the thing to which to propagate
95
+ # @param propagation_data [Contrast::Agent::Assess::Events::EventData] this will hold the
96
+ # object [Object] the Object on which the method was invoked
97
+ # args [Array<Object>] the Arguments with which the method was invoked
95
98
  # @return [Boolean]
96
- def can_propagate? propagation_node, preshift, target
99
+ def can_propagate? propagation_node, preshift, target, propagation_data
97
100
  return false unless appropriate_target?(propagation_node, target)
98
101
  return true if Contrast::Utils::Assess::TrackingUtil.tracked?(target)
99
- if propagation_node.use_original_object?
100
- # return true since we don't have preshift while using the original object.
101
- return true
102
- end
103
- return false unless preshift
102
+ return false unless appropriate_source?(propagation_node, propagation_data, preshift)
104
103
 
105
104
  propagation_node.sources.each do |source|
106
105
  case source
107
106
  when Contrast::Utils::ObjectShare::OBJECT_KEY
108
- return true if Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.object)
107
+ source_object = if propagation_node.use_original_object?
108
+ propagation_data.object
109
+ else
110
+ preshift.object
111
+ end
112
+ return true if Contrast::Utils::Assess::TrackingUtil.tracked?(source_object)
109
113
  else
110
114
  # has to be P, there's no ret source type (yet? ever?)
111
115
  return true if preshift.args && Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.args[source])
@@ -129,6 +133,22 @@ module Contrast
129
133
 
130
134
  Contrast::Agent::Assess::Tracker.trackable?(target)
131
135
  end
136
+
137
+ # A source is appropriate if it is available for propagation
138
+ #
139
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
140
+ # propagation event.
141
+ # @param propagation_data [Contrast::Agent::Assess::Events::EventData] this will hold the
142
+ # object [Object] the Object on which the method was invoked
143
+ # args [Array<Object>] the Arguments with which the method was invoked
144
+ # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
145
+ # the invocation of the patched method.
146
+ # @return [Boolean]
147
+ def appropriate_source? propagation_node, propagation_data, preshift
148
+ return true if preshift
149
+
150
+ propagation_node.use_original_object? && propagation_data&.object
151
+ end
132
152
  end
133
153
  end
134
154
  end
@@ -19,7 +19,7 @@ module Contrast
19
19
 
20
20
  private
21
21
 
22
- def build path: STDOUT_STR, level_const: DEFAULT_LEVEL
22
+ def build path: STDOUT_STR, level_const: DEFAULT_LEVEL, progname: PROGNAME
23
23
  logger = case path
24
24
  when STDOUT_STR, STDERR_STR
25
25
  ::Ougai::Logger.new(Object.cs__const_get(path))
@@ -27,7 +27,7 @@ module Contrast
27
27
  ::Ougai::Logger.new(path)
28
28
  end
29
29
  add_contrast_loggers(logger)
30
- logger.progname = PROGNAME
30
+ logger.progname = progname
31
31
  logger.level = level_const
32
32
  logger.formatter = Contrast::Logger::Format.new
33
33
  logger.formatter.datetime_format = DATE_TIME_FORMAT
@@ -131,7 +131,7 @@ module Contrast
131
131
  end
132
132
  return initialize_client if addr.host.to_s.include?('localhost') # TODO: RUBY-99999 allow http w/ localhost
133
133
 
134
- assign_cert(initialize_client) if use_custom_cert && Contrast::API.certification_enabled?
134
+ assign_cert(initialize_client) if use_custom_cert && Contrast::API.certification_enable
135
135
  initialize_client.use_ssl = true
136
136
  initialize_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
137
137
  initialize_client.verify_depth = 5
@@ -150,7 +150,7 @@ module Contrast
150
150
  def proxy_enabled?
151
151
  return @_proxy_enabled unless @_proxy_enabled.nil?
152
152
 
153
- @_proxy_enabled = Contrast::API.proxy_enabled? && !Contrast::API.proxy_url.nil?
153
+ @_proxy_enabled = Contrast::API.proxy_enable && !Contrast::API.proxy_url.nil?
154
154
  end
155
155
 
156
156
  # Retrieve the IP address from the client.
@@ -7,18 +7,6 @@ module Contrast
7
7
  # This module will include all methods for different patch applies from Patch module and some other module
8
8
  # methods from the same place, so we can ease the main module
9
9
  module PatchUtils
10
- # Method to choose which replaced return from the post_patch to actually return.
11
- #
12
- # @param propagated_ret [Object, nil] The replaced return from the propagation patch.
13
- # @param source_ret [Object, nil] The replaced return from the source patch.
14
- # @param ret [Object, nil] The original return of the patched method.
15
- # @return [Object, nil] The thing to return from the post patch.
16
- def handle_return propagated_ret, source_ret, ret
17
- safe_return = propagated_ret || source_ret || ret
18
- safe_return.rewind if Contrast::Utils::IOUtil.should_rewind?(safe_return)
19
- safe_return
20
- end
21
-
22
10
  # Given a module and method, construct an expected name for the alias by which Contrast will reference the
23
11
  # original.
24
12
  #
@@ -107,7 +95,7 @@ module Contrast
107
95
  # @param object [Object] The object on which the method is invoked, typically what would be returned by self.
108
96
  # @param args [Array<Object>] The arguments passed to the method being invoked.
109
97
  def apply_inventory method_policy, method, exception, object, args
110
- return unless ::Contrast::INVENTORY.enabled?
98
+ return unless ::Contrast::INVENTORY.enable
111
99
 
112
100
  apply_trigger_only(method_policy&.inventory_node, method, exception, object, args)
113
101
  end
@@ -123,8 +111,6 @@ module Contrast
123
111
  # @param args [Array<Object>] The arguments passed to the method being invoked.
124
112
  # @param block [Proc] The block passed to the method that was invoked.
125
113
  def apply_assess method_policy, preshift, object, ret, args, block
126
- source_ret = nil
127
- propagated_ret = nil
128
114
  return ret unless method_policy && ::Contrast::ASSESS.enabled?
129
115
 
130
116
  current_context = Contrast::Agent::REQUEST_TRACKER.current
@@ -135,27 +121,24 @@ module Contrast
135
121
  Contrast::Agent::Assess::Policy::TriggerMethod.apply_trigger_rule(trigger_node, object, ret, args)
136
122
  end
137
123
  if method_policy.source_node
138
- # If we were given a frozen return, and it was the target of a source, and we have frozen sources enabled,
139
- # we'll need to replace the return. Note, this is not the default case.
140
- source_ret = Contrast::Agent::Assess::Policy::SourceMethod.apply_source(method_policy, object, ret, args)
124
+ Contrast::Agent::Assess::Policy::SourceMethod.apply_source(method_policy, object, ret, args)
141
125
  end
142
126
  if method_policy.propagation_node
143
- propagated_ret = Contrast::Agent::Assess::Policy::PropagationMethod.apply_propagation(
127
+ Contrast::Agent::Assess::Policy::PropagationMethod.apply_propagation(
144
128
  method_policy,
145
129
  preshift,
146
130
  object,
147
- source_ret || ret,
131
+ ret,
148
132
  args,
149
133
  block)
150
134
  end
151
- handle_return(propagated_ret, source_ret, ret)
152
135
  rescue StandardError => e
153
136
  logger.error('Unable to assess method call.', e)
154
- handle_return(propagated_ret, source_ret, ret)
155
137
  rescue Exception => e # rubocop:disable Lint/RescueException
156
138
  logger.error('Unable to assess method call.', e)
157
- handle_return(propagated_ret, source_ret, ret)
158
139
  raise(e)
140
+ ensure
141
+ ret.rewind if Contrast::Utils::IOUtil.should_rewind?(ret)
159
142
  end
160
143
 
161
144
  # Generic invocation of the Inventory or Protect patch which apply to the given method.
data/lib/contrast.rb CHANGED
@@ -4,21 +4,39 @@
4
4
  # Used to prevent deprecation warnings from flooding stdout
5
5
  ENV['PB_IGNORE_DEPRECATIONS'] = 'true'
6
6
 
7
- # Some developers override various methods on Object, which can often involve
8
- # changing expected method parity/behavior which in turn prevents us from being
9
- # able to reliably use affected methods.
10
- # We alias these method so that we always have access to them.
11
- #
12
- # Because we use these methods in constructing classes (e.g., calling #freeze
13
- # on constants within class definitions) we do this aliasing ASAP.
14
- class Object
15
- alias_method :cs__class, :class
16
- alias_method :cs__freeze, :freeze
17
- alias_method :cs__frozen?, :frozen?
18
- alias_method :cs__is_a?, :is_a?
19
- alias_method :cs__method, :method
20
- alias_method :cs__respond_to?, :respond_to?
21
- alias_method :cs__singleton_class, :singleton_class
7
+ require 'contrast/extension/object'
8
+
9
+ # ActiveRecord gives access to the `String#blank?` method, which we've started using. We need to make sure that method
10
+ # actually exists.
11
+ # From https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/object/blank.rb
12
+ class String
13
+ unless cs__respond_to?(:blank?)
14
+
15
+ CS__BLANK_RE = /\A[[:space:]]*\z/.cs__freeze
16
+ # A string is blank if it's empty or contains whitespaces only:
17
+ #
18
+ # ''.blank? # => true
19
+ # ' '.blank? # => true
20
+ # "\t\n\r".blank? # => true
21
+ # ' blah '.blank? # => false
22
+ #
23
+ # Unicode whitespace is supported:
24
+ #
25
+ # "\u00a0".blank? # => true
26
+ #
27
+ # @return [true, false]
28
+ def blank?
29
+ # The regexp that matches blank strings is expensive. For the case of empty
30
+ # strings we can speed up this method (~3.5x) with an empty? call. The
31
+ # penalty for the rest of strings is marginal.
32
+ empty? ||
33
+ begin
34
+ CS__BLANK_RE.match?(self)
35
+ rescue Encoding::CompatibilityError
36
+ false
37
+ end
38
+ end
39
+ end
22
40
  end
23
41
 
24
42
  if RUBY_VERSION >= '3.0.0' && RUBY_VERSION < '3.1.0'
@@ -50,17 +68,18 @@ require 'contrast/components/settings'
50
68
  require 'contrast/utils/telemetry_hash'
51
69
  require 'contrast/utils/telemetry'
52
70
  require 'contrast/agent/telemetry/events/exceptions/telemetry_exception_event'
71
+ require 'protobuf' # TODO: RUBY-1438
53
72
 
54
73
  module Contrast
55
- API = Contrast::Components::Api::Interface.new
56
- SCOPE = Contrast::Components::Scope::Interface.new
57
74
  CONFIG = Contrast::Components::Config::Interface.new
75
+ SCOPE = Contrast::Components::Scope::Interface.new
76
+ API = CONFIG.root.api
58
77
  SETTINGS = Contrast::Components::Settings::Interface.new
59
78
  ASSESS = Contrast::Components::Assess::Interface.new
60
79
  PROTECT = Contrast::Components::Protect::Interface.new
61
- INVENTORY = Contrast::Components::Inventory::Interface.new
62
- LOGGER = Contrast::Components::Logger::Interface.new
63
- AGENT = Contrast::Components::Agent::Interface.new
80
+ INVENTORY = CONFIG.root.inventory
81
+ AGENT = CONFIG.root.agent
82
+ LOGGER = AGENT.logger
64
83
  CONTRAST_SERVICE = Contrast::Components::ContrastService::Interface.new
65
84
  APP_CONTEXT = Contrast::Components::AppContext::Interface.new
66
85
  end