contrast-agent 6.4.0 → 6.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) 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/dependency_analysis.rb +2 -2
  12. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -1
  13. data/lib/contrast/agent/inventory/policy/datastores.rb +1 -1
  14. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  15. data/lib/contrast/agent/patching/policy/method_policy.rb +3 -3
  16. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +1 -3
  17. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +17 -21
  18. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +1 -1
  19. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +26 -3
  20. data/lib/contrast/agent/request_context.rb +8 -0
  21. data/lib/contrast/agent/service_heartbeat.rb +2 -3
  22. data/lib/contrast/agent/static_analysis.rb +1 -1
  23. data/lib/contrast/agent/version.rb +1 -1
  24. data/lib/contrast/agent/worker_thread.rb +10 -0
  25. data/lib/contrast/components/agent.rb +51 -13
  26. data/lib/contrast/components/assess.rb +16 -0
  27. data/lib/contrast/components/contrast_service.rb +1 -1
  28. data/lib/contrast/components/heap_dump.rb +51 -1
  29. data/lib/contrast/components/inventory.rb +19 -13
  30. data/lib/contrast/components/logger.rb +18 -0
  31. data/lib/contrast/config/assess_configuration.rb +28 -0
  32. data/lib/contrast/config/base_configuration.rb +8 -2
  33. data/lib/contrast/config/root_configuration.rb +11 -8
  34. data/lib/contrast/config/service_configuration.rb +4 -4
  35. data/lib/contrast/config.rb +0 -6
  36. data/lib/contrast/extension/object.rb +19 -0
  37. data/lib/contrast/framework/rails/support.rb +4 -1
  38. data/lib/contrast/logger/log.rb +2 -1
  39. data/lib/contrast/utils/assess/event_limit_utils.rb +96 -0
  40. data/lib/contrast/utils/assess/propagation_method_utils.rb +27 -7
  41. data/lib/contrast/utils/log_utils.rb +2 -2
  42. data/lib/contrast/utils/patching/policy/patch_utils.rb +1 -1
  43. data/lib/contrast.rb +4 -19
  44. data/resources/assess/policy.json +4 -12
  45. data/ruby-agent.gemspec +2 -0
  46. metadata +43 -17
  47. data/lib/contrast/config/agent_configuration.rb +0 -63
  48. data/lib/contrast/config/heap_dump_configuration.rb +0 -59
  49. data/lib/contrast/config/inventory_configuration.rb +0 -33
  50. data/lib/contrast/config/logger_configuration.rb +0 -26
@@ -3,6 +3,8 @@
3
3
 
4
4
  require 'rubygems/version'
5
5
  require 'contrast/agent/rule_set'
6
+ require 'contrast/components/logger'
7
+ require 'contrast/components/heap_dump'
6
8
 
7
9
  module Contrast
8
10
  module Components
@@ -13,9 +15,50 @@ module Contrast
13
15
  class Interface
14
16
  include Contrast::Components::ComponentBase
15
17
 
18
+ def initialize hsh = {}
19
+ return unless hsh
20
+
21
+ @_enable = hsh[:enable]
22
+ @_start_bundled_service = hsh[:start_bundled_service]
23
+ @_omit_body = hsh[:omit_body]
24
+ @_service = Contrast::Config::ServiceConfiguration.new(hsh[:service])
25
+ @_logger = Contrast::Components::Logger::Interface.new(hsh[:logger])
26
+ @_ruby = Contrast::Config::RubyConfiguration.new(hsh[:ruby])
27
+ @_heap_dump = Contrast::Components::HeapDump::Interface.new(hsh[:heap_dump])
28
+ end
29
+
30
+ # @return [Boolean, true]
31
+ def start_bundled_service?
32
+ @_start_bundled_service.nil? ? true : @_start_bundled_service
33
+ end
34
+
35
+ def service
36
+ return @_service unless @_service.nil?
37
+
38
+ @_service = Contrast::Config::ServiceConfiguration.new
39
+ end
40
+
41
+ def logger
42
+ return @_logger unless @_logger.nil?
43
+
44
+ @_logger = Contrast::Components::Logger::Interface.new
45
+ end
46
+
47
+ def ruby
48
+ return @_ruby unless @_ruby.nil?
49
+
50
+ @_ruby = Contrast::Config::RubyConfiguration.new
51
+ end
52
+
53
+ def heap_dump
54
+ return @_heap_dump unless @_heap_dump.nil?
55
+
56
+ @_heap_dump = Contrast::Components::HeapDump::Interface.new
57
+ end
58
+
16
59
  def enabled?
17
- @_enabled = !false?(::Contrast::CONFIG.root.enable) if @_enabled.nil?
18
- @_enabled
60
+ @_enable = !false?(::Contrast::CONFIG.root.enable) if @_enable.nil?
61
+ @_enable
19
62
  end
20
63
 
21
64
  def disabled?
@@ -23,11 +66,11 @@ module Contrast
23
66
  end
24
67
 
25
68
  def enable!
26
- @_enabled = true
69
+ @_enable = true
27
70
  end
28
71
 
29
72
  def disable!
30
- @_enabled = false
73
+ @_enable = false
31
74
  Contrast::Agent::TracePointHook.disable
32
75
  Contrast::Agent.thread_watcher&.shutdown!
33
76
  end
@@ -41,8 +84,7 @@ module Contrast
41
84
  end
42
85
 
43
86
  def patch_yield?
44
- @_patch_yield = !false?(::Contrast::CONFIG.root.agent.ruby.propagate_yield) if @_patch_yield.nil?
45
- @_patch_yield
87
+ !false?(ruby.propagate_yield)
46
88
  end
47
89
 
48
90
  def interpolation_enabled?
@@ -52,18 +94,14 @@ module Contrast
52
94
  end
53
95
 
54
96
  def omit_body?
55
- @_omit_body = true?(::Contrast::CONFIG.root.agent.omit_body) if @_omit_body.nil?
56
97
  @_omit_body
57
98
  end
58
99
 
59
100
  def exception_control
60
101
  @_exception_control ||= {
61
- enable: true?(::Contrast::CONFIG.root.agent.ruby.exceptions.capture),
62
- status:
63
- ::Contrast::CONFIG.root.agent.ruby.exceptions.override_status || 403,
64
- message:
65
- ::Contrast::CONFIG.root.agent.ruby.exceptions.override_message ||
66
- Contrast::Utils::ObjectShare::OVERRIDE_MESSAGE
102
+ enable: true?(ruby.exceptions.capture),
103
+ status: ruby.exceptions.override_status || 403,
104
+ message: ruby.exceptions.override_message || Contrast::Utils::ObjectShare::OVERRIDE_MESSAGE
67
105
  }
68
106
  end
69
107
 
@@ -118,6 +118,22 @@ module Contrast
118
118
  ::Contrast::SETTINGS.assess_state.session_id
119
119
  end
120
120
 
121
+ def max_source_events
122
+ ::Contrast::CONFIG.root.assess.max_context_source_events
123
+ end
124
+
125
+ def max_propagation_events
126
+ ::Contrast::CONFIG.root.assess.max_propagation_events
127
+ end
128
+
129
+ def time_limit_threshold
130
+ ::Contrast::CONFIG.root.assess.time_limit_threshold
131
+ end
132
+
133
+ def max_rule_reported
134
+ ::Contrast::CONFIG.root.assess.max_rule_reported
135
+ end
136
+
121
137
  private
122
138
 
123
139
  def forcibly_enabled?
@@ -29,7 +29,7 @@ module Contrast
29
29
 
30
30
  # Requirement says "must be true" but that
31
31
  # should be "must not be false" -- oops.
32
- @_use_bundled_service ||= !false?(::Contrast::CONFIG.root.agent.start_bundled_service) &&
32
+ @_use_bundled_service ||= !false?(::Contrast::CONFIG.root.agent.start_bundled_service?) &&
33
33
  # Either a valid host or a valid socket
34
34
  # Path validity is the service's problem
35
35
  (LOCALHOST.match?(host) || !!socket_path)
@@ -2,11 +2,61 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/components/base'
5
- require 'contrast/components/heap_dump'
5
+ require 'contrast/config/base_configuration'
6
6
 
7
7
  module Contrast
8
8
  module Components
9
9
  module HeapDump
10
+ # Interface used to build the HeapDump settings and component.
11
+ class Interface
12
+ include Contrast::Config::BaseConfiguration
13
+
14
+ DEFAULT_PATH = 'contrast_heap_dumps' # saved
15
+ DEFAULT_MS = 10_000
16
+ DEFAULT_COUNT = 5
17
+
18
+ def initialize hsh = {}
19
+ return unless hsh
20
+
21
+ @_enable = hsh[:enable]
22
+ @_path = hsh[:path]
23
+ @_delay_ms = hsh[:delay_ms]
24
+ @_window_ms = hsh[:window_ms]
25
+ @_count = hsh[:count]
26
+ @_clean = hsh[:clean]
27
+ end
28
+
29
+ # @return [Boolean, String] should dumps be taken
30
+ def enable
31
+ @_enable.nil? ? Contrast::Utils::ObjectShare::FALSE : @_enable
32
+ end
33
+
34
+ # @return [String, DEFAULT_PATH] dir to which dumps should be
35
+ def path
36
+ @_path ||= DEFAULT_PATH
37
+ end
38
+
39
+ # @return [Integer, DEFAULT_MS] time, in ms, after initialization
40
+ def delay_ms
41
+ @_delay_ms ||= DEFAULT_MS
42
+ end
43
+
44
+ # @return [Integer, DEFAULT_MS] ms between each dump
45
+ def window_ms
46
+ @_window_ms ||= DEFAULT_MS
47
+ end
48
+
49
+ # @return [Integer, DEFAULT_COUNT] number of dumps to take
50
+ def count
51
+ @_count ||= DEFAULT_COUNT
52
+ end
53
+
54
+ # @return [Boolean, String] remove temporary objects or not
55
+ def clean
56
+ @_clean.nil? ? Contrast::Utils::ObjectShare::FALSE : @_clean
57
+ end
58
+ end
59
+
10
60
  # A wrapper build around the Common Agent Configuration project to allow
11
61
  # for access of the values contained in its
12
62
  # parent_configuration_spec.yaml.
@@ -1,29 +1,35 @@
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/base'
5
+
4
6
  module Contrast
5
7
  module Components
6
8
  module Inventory
7
- # A wrapper build around the Common Agent Configuration project to allow
8
- # for access of the values contained in its
9
- # parent_configuration_spec.yaml.
10
- # Specifically, this allows for querying the state of the Inventory
11
- # product.
9
+ # Interface component for Inventory settings used to store the values from
10
+ # settings file and assert state with check methods.
12
11
  class Interface
13
12
  include Contrast::Components::ComponentBase
14
13
 
15
- def enabled?
16
- @_enabled = !false?(::Contrast::CONFIG.root.inventory.enable) if @_enabled.nil?
17
- @_enabled
14
+ # @return [Array, nil] tags
15
+ attr_accessor :tags
16
+
17
+ def initialize hsh = {}
18
+ return unless hsh
19
+
20
+ @enable = !false?(hsh[:enable])
21
+ @analyze_libraries = !false?(hsh[:analyze_libraries])
22
+ @tags = hsh[:tags]
18
23
  end
19
24
 
20
- def analyze_libraries?
21
- @_analyze_libraries = !false?(::Contrast::CONFIG.root.inventory.analyze_libraries) if @_analyze_libraries.nil?
22
- @_analyze_libraries
25
+ # return [Boolean]
26
+ def enable
27
+ @enable.nil? ? true : @enable
23
28
  end
24
29
 
25
- def tags
26
- ::Contrast::CONFIG.root.inventory&.tags
30
+ # return [Boolean]
31
+ def analyze_libraries
32
+ @analyze_libraries.nil? ? true : @analyze_libraries
27
33
  end
28
34
  end
29
35
  end
@@ -28,8 +28,26 @@ module Contrast
28
28
  end
29
29
  end
30
30
 
31
+ # So This class here follows the update for the configuration
32
+ # and from know on ( if it's as we planned it to be) it will hold the
33
+ # instance methods and will initialize new instances for where they're needed
31
34
  class Interface
32
35
  include InstanceMethods
36
+
37
+ # @return [String, nil]
38
+ attr_accessor :path
39
+ # @return [String, nil]
40
+ attr_accessor :level
41
+ # @return [String, nil]
42
+ attr_accessor :progname
43
+
44
+ def initialize hsh = {}
45
+ return unless hsh
46
+
47
+ @path = hsh[:path]
48
+ @level = hsh[:level]
49
+ @progname = hsh[:progname]
50
+ end
33
51
  end
34
52
  end
35
53
  end
@@ -15,6 +15,10 @@ module Contrast
15
15
  attr_writer :enable_scan_response, :enable_dynamic_sources, :sampling, :rules, :stacktraces
16
16
 
17
17
  DEFAULT_STACKTRACES = 'ALL'
18
+ DEFAULT_MAX_SOURCE_EVENTS = 50_000
19
+ DEFAULT_MAX_PROPAGATION_EVENTS = 50_000
20
+ DEFAULT_MAX_RULE_REPORTED = 50_000
21
+ DEFAULT_MAX_RULE_TIME_THRESHOLD = 300_000
18
22
 
19
23
  def initialize hsh = {}
20
24
  return unless hsh
@@ -27,6 +31,10 @@ module Contrast
27
31
  @sampling = Contrast::Config::SamplingConfiguration.new(hsh[:sampling])
28
32
  @rules = Contrast::Config::AssessRulesConfiguration.new(hsh[:rules])
29
33
  @stacktraces = hsh[:stacktraces]
34
+ @max_context_source_events = hsh[:max_context_source_events]
35
+ @max_propagation_events = hsh[:max_propagation_events]
36
+ @max_rule_reported = hsh[:max_rule_reported]
37
+ @time_limit_threshold = hsh[:time_limit_threshold]
30
38
  end
31
39
 
32
40
  # @return [Boolean, true]
@@ -58,6 +66,26 @@ module Contrast
58
66
  def stacktraces
59
67
  @stacktraces ||= DEFAULT_STACKTRACES
60
68
  end
69
+
70
+ # @return [int] max number of context source events in single request
71
+ def max_context_source_events
72
+ @max_context_source_events ||= DEFAULT_MAX_SOURCE_EVENTS
73
+ end
74
+
75
+ # @return [int] max number of propagation events in single request
76
+ def max_propagation_events
77
+ @max_propagation_events ||= DEFAULT_MAX_PROPAGATION_EVENTS
78
+ end
79
+
80
+ # @return [int] max number of rules reported within time_limit_threshold
81
+ def max_rule_reported
82
+ @max_rule_reported ||= DEFAULT_MAX_RULE_REPORTED
83
+ end
84
+
85
+ # @return [int] max ms threshold for reporting rules
86
+ def time_limit_threshold
87
+ @time_limit_threshold ||= DEFAULT_MAX_RULE_TIME_THRESHOLD
88
+ end
61
89
  end
62
90
  end
63
91
  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,6 +1,9 @@
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
+
4
7
  module Contrast
5
8
  module Config
6
9
  # The base of the Common Configuration settings.
@@ -9,7 +12,7 @@ module Contrast
9
12
 
10
13
  # @return [Contrast::Config::ApiConfiguration]
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,7 +20,7 @@ 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
25
  # @return [Contrast::Config::ProtectConfiguration]
23
26
  attr_writer :protect
@@ -32,11 +35,11 @@ module Contrast
32
35
 
33
36
  @api = Contrast::Config::ApiConfiguration.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])
42
+ @inventory = Contrast::Components::Inventory::Interface.new(hsh[:inventory])
40
43
  @protect = Contrast::Config::ProtectConfiguration.new(hsh[:protect])
41
44
  @service = Contrast::Config::ServiceConfiguration.new(hsh[:service])
42
45
  end
@@ -46,9 +49,9 @@ module Contrast
46
49
  @api ||= Contrast::Config::ApiConfiguration.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,9 +69,9 @@ 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
77
  # @return [Contrast::Config::ProtectConfiguration]
@@ -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,10 +11,6 @@ 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'
@@ -24,10 +20,8 @@ require 'contrast/config/sampling_configuration'
24
20
 
25
21
  require 'contrast/config/ruby_configuration'
26
22
  require 'contrast/config/api_configuration'
27
- require 'contrast/config/agent_configuration'
28
23
  require 'contrast/config/application_configuration'
29
24
  require 'contrast/config/server_configuration'
30
25
  require 'contrast/config/assess_configuration'
31
- require 'contrast/config/inventory_configuration'
32
26
  require 'contrast/config/protect_configuration'
33
27
  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