contrast-agent 4.10.0 → 4.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_module/cs__assess_module.c +48 -0
  3. data/ext/cs__assess_module/cs__assess_module.h +7 -0
  4. data/ext/cs__common/cs__common.c +24 -7
  5. data/ext/cs__common/cs__common.h +12 -2
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +48 -11
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -2
  8. data/ext/cs__os_information/cs__os_information.c +31 -0
  9. data/ext/cs__os_information/cs__os_information.h +7 -0
  10. data/ext/{cs__protect_kernel → cs__os_information}/extconf.rb +0 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +1 -1
  12. data/lib/contrast/agent/assess/contrast_object.rb +1 -4
  13. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
  14. data/lib/contrast/agent/assess/policy/preshift.rb +25 -11
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
  16. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  17. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -0
  18. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +4 -4
  19. data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
  20. data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
  21. data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -107
  22. data/lib/contrast/agent/assess/policy/trigger_node.rb +52 -19
  23. data/lib/contrast/agent/assess/property/tagged.rb +15 -132
  24. data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
  25. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +2 -1
  26. data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
  27. data/lib/contrast/agent/middleware.rb +22 -0
  28. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +0 -1
  29. data/lib/contrast/agent/patching/policy/method_policy.rb +54 -9
  30. data/lib/contrast/agent/patching/policy/patch.rb +37 -238
  31. data/lib/contrast/agent/patching/policy/patcher.rb +3 -42
  32. data/lib/contrast/agent/request.rb +5 -3
  33. data/lib/contrast/agent/request_context.rb +32 -11
  34. data/lib/contrast/agent/request_handler.rb +7 -3
  35. data/lib/contrast/agent/rule_set.rb +2 -4
  36. data/lib/contrast/agent/scope.rb +32 -20
  37. data/lib/contrast/agent/startup_metrics_telemetry_event.rb +71 -0
  38. data/lib/contrast/agent/static_analysis.rb +4 -2
  39. data/lib/contrast/agent/telemetry.rb +129 -0
  40. data/lib/contrast/agent/telemetry_event.rb +34 -0
  41. data/lib/contrast/agent/thread_watcher.rb +43 -14
  42. data/lib/contrast/agent/tracepoint_hook.rb +11 -3
  43. data/lib/contrast/agent/version.rb +1 -1
  44. data/lib/contrast/agent.rb +6 -1
  45. data/lib/contrast/components/api.rb +34 -0
  46. data/lib/contrast/components/app_context.rb +24 -0
  47. data/lib/contrast/components/assess.rb +7 -0
  48. data/lib/contrast/components/config.rb +90 -11
  49. data/lib/contrast/components/contrast_service.rb +6 -0
  50. data/lib/contrast/config/api_configuration.rb +22 -0
  51. data/lib/contrast/config/assess_configuration.rb +1 -0
  52. data/lib/contrast/config/env_variables.rb +25 -0
  53. data/lib/contrast/config/root_configuration.rb +1 -0
  54. data/lib/contrast/config/service_configuration.rb +2 -1
  55. data/lib/contrast/config.rb +1 -0
  56. data/lib/contrast/configuration.rb +3 -0
  57. data/lib/contrast/framework/manager.rb +14 -12
  58. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
  59. data/lib/contrast/framework/rails/patch/support.rb +31 -29
  60. data/lib/contrast/logger/application.rb +4 -0
  61. data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
  62. data/lib/contrast/utils/assess/property/tagged_utils.rb +142 -0
  63. data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
  64. data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
  65. data/lib/contrast/utils/class_util.rb +58 -44
  66. data/lib/contrast/utils/exclude_key.rb +20 -0
  67. data/lib/contrast/utils/io_util.rb +42 -34
  68. data/lib/contrast/utils/lru_cache.rb +45 -0
  69. data/lib/contrast/utils/metrics_hash.rb +59 -0
  70. data/lib/contrast/utils/os.rb +23 -0
  71. data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
  72. data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
  73. data/lib/contrast/utils/requests_client.rb +150 -0
  74. data/lib/contrast/utils/ruby_ast_rewriter.rb +1 -1
  75. data/lib/contrast/utils/telemetry.rb +77 -0
  76. data/lib/contrast/utils/telemetry_identifier.rb +137 -0
  77. data/lib/contrast.rb +19 -1
  78. data/resources/assess/policy.json +12 -6
  79. data/resources/deadzone/policy.json +86 -5
  80. data/ruby-agent.gemspec +2 -1
  81. data/service_executables/VERSION +1 -1
  82. data/service_executables/linux/contrast-service +0 -0
  83. data/service_executables/mac/contrast-service +0 -0
  84. metadata +32 -14
  85. data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
  86. data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
  87. data/lib/contrast/extension/protect/kernel.rb +0 -29
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/utils/env_configuration_item'
5
+ require 'ougai'
5
6
  require 'contrast/configuration'
6
7
 
7
8
  module Contrast
@@ -23,17 +24,33 @@ module Contrast
23
24
  # time than to silently fail to deliver functionality.
24
25
  module Config
25
26
  CONTRAST_ENV_MARKER = 'CONTRAST__'
27
+ CONTRAST_LOG = 'contrast_agent.log'
28
+ CONTRAST_NAME = 'Contrast Agent'
26
29
 
27
30
  class Interface # :nodoc:
28
31
  def initialize
29
32
  build
30
33
  end
31
34
 
32
- def build log: true
35
+ # Basic logger for handling configuration validation logging
36
+ # the file to log is determined by the default one or set
37
+ # by the config file, if that configuration is found
38
+ def proto_logger
39
+ @_proto_logger ||= begin
40
+ @_proto_logger = ::Ougai::Logger.new(logger_path || CONTRAST_LOG)
41
+ @_proto_logger.progname = CONTRAST_NAME
42
+ @_proto_logger.level = ::Ougai::Logging::Severity::WARN
43
+ @_proto_logger.formatter = Contrast::Logger::Format.new
44
+ @_proto_logger.formatter.datetime_format = '%Y-%m-%dT%H:%M:%S.%L%z'
45
+ @_proto_logger
46
+ end
47
+ end
48
+
49
+ def build
33
50
  @_valid = nil
34
51
  @config = Contrast::Configuration.new
35
52
  env_overrides
36
- validate(log: log)
53
+ validate
37
54
  end
38
55
  alias_method :rebuild, :build
39
56
 
@@ -43,7 +60,7 @@ module Contrast
43
60
  end
44
61
 
45
62
  def valid?
46
- @_valid = validate(log: false) if @_valid.nil?
63
+ @_valid = validate if @_valid.nil?
47
64
  @_valid
48
65
  end
49
66
 
@@ -59,20 +76,28 @@ module Contrast
59
76
 
60
77
  SESSION_VARIABLES = 'Invalid configuration. '\
61
78
  "Setting both application.session_id and application.session_metadata is not allowed.\n"
62
- def validate log: false
79
+ API_URL = "Invalid configuration. Missing a required connection value 'url' is not set."
80
+ API_KEY = "Invalid configuration. Missing a required connection value 'api_key' is not set."
81
+ API_SERVICE_KEY = "Invalid configuration. Missing a required connection value 'service_tag' is not set."
82
+ API_USERNAME = "Invalid configuration. Missing a required connection value 'user_name' is not set."
83
+ def validate
63
84
  # The config has information about how to construct the logger.
64
85
  # If the config is invalid, and you want to know about it, then
65
86
  # you have a circular dependency if you try to log it,
66
- # hence `log: false`.
87
+ # so we use basic proto_logger to do this job.
67
88
  if !session_id.empty? && !session_metadata.empty?
68
- if log
69
- cs__class.log_error(SESSION_VARIABLES)
70
- else
71
- puts SESSION_VARIABLES
72
- end
89
+ proto_logger.error(SESSION_VARIABLES)
73
90
  return false
74
91
  end
75
-
92
+ if bypass
93
+ msg = []
94
+ msg << API_URL unless api_url
95
+ msg << API_KEY unless api_key
96
+ msg << API_SERVICE_KEY unless api_service_key
97
+ msg << API_USERNAME unless api_username
98
+ msg.any? { |m| proto_logger.error(m) }
99
+ return false unless msg.empty?
100
+ end
76
101
  true
77
102
  end
78
103
 
@@ -108,6 +133,60 @@ module Contrast
108
133
  def session_metadata
109
134
  @config.application.session_metadata
110
135
  end
136
+
137
+ # Typically, the following values would be accessed through Contrast::Components::AppContext
138
+ # and Contrast::Components::API, but we're too early in the initialization of the Agent to use
139
+ # that mechanism, so we look it up directly for ourselves.
140
+ #
141
+ # @return [String, nil]
142
+ def api_url
143
+ @config.api.url
144
+ end
145
+
146
+ # Typically, the following values would be accessed through Contrast::Components::AppContext
147
+ # and Contrast::Components::API, but we're too early in the initialization of the Agent to use
148
+ # that mechanism, so we look it up directly for ourselves.
149
+ #
150
+ # @return [String, nil]
151
+ def api_key
152
+ @config.api.api_key
153
+ end
154
+
155
+ # Typically, the following values would be accessed through Contrast::Components::AppContext
156
+ # and Contrast::Components::API, but we're too early in the initialization of the Agent to use
157
+ # that mechanism, so we look it up directly for ourselves.
158
+ #
159
+ # @return [String, nil]
160
+ def api_service_key
161
+ @config.api.service_key
162
+ end
163
+
164
+ # Typically, the following values would be accessed through Contrast::Components::AppContext
165
+ # and Contrast::Components::API, but we're too early in the initialization of the Agent to use
166
+ # that mechanism, so we look it up directly for ourselves.
167
+ #
168
+ # @return [String, nil]
169
+ def api_username
170
+ @config.api.user_name
171
+ end
172
+
173
+ # Typically, the following values would be accessed through Contrast::Components::AppContext
174
+ # and Contrast::Components::API, but we're too early in the initialization of the Agent to use
175
+ # that mechanism, so we look it up directly for ourselves.
176
+ #
177
+ # @return [String, nil]
178
+ def bypass
179
+ @config.root.agent.service.bypass
180
+ end
181
+
182
+ # Typically, the following values would be accessed through Contrast::Components::AppContext
183
+ # and Contrast::Components::Logger, but we're too early in the initialization of the Agent to use
184
+ # that mechanism, so we look it up directly for ourselves.
185
+ #
186
+ # @return [String, nil]
187
+ def logger_path
188
+ @config.root.agent.logger.path
189
+ end
111
190
  end
112
191
  end
113
192
  end
@@ -31,6 +31,12 @@ module Contrast
31
31
  (LOCALHOST.match?(host) || !!socket_path)
32
32
  end
33
33
 
34
+ def use_agent_communication?
35
+ return @_use_agent_communication unless @_use_agent_communication.nil?
36
+
37
+ @_use_agent_communication = true?(::Contrast::CONFIG.root.agent.service.bypass)
38
+ end
39
+
34
40
  def host
35
41
  @_host ||=
36
42
  (::Contrast::CONFIG.root.agent.service.host || Contrast::Config::ServiceConfiguration::DEFAULT_HOST).to_s
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/config/default_value'
5
+
6
+ module Contrast
7
+ module Config
8
+ # Api keys configuration
9
+ class ApiConfiguration < BaseConfiguration
10
+ URL = 'https://app.contrastsecurity.com/Contrast'
11
+ KEYS = {
12
+ api_key: EMPTY_VALUE,
13
+ url: Contrast::Config::DefaultValue.new(URL),
14
+ user_name: EMPTY_VALUE,
15
+ service_key: EMPTY_VALUE
16
+ }.cs__freeze
17
+ def initialize hsh
18
+ super(hsh, KEYS)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -10,6 +10,7 @@ module Contrast
10
10
  tags: EMPTY_VALUE,
11
11
  enable: EMPTY_VALUE,
12
12
  enable_scan_response: Contrast::Config::DefaultValue.new('true'),
13
+ enable_dynamic_sources: Contrast::Config::DefaultValue.new('true'),
13
14
  sampling: Contrast::Config::SamplingConfiguration,
14
15
  rules: Contrast::Config::AssessRulesConfiguration,
15
16
  stacktraces: Contrast::Config::DefaultValue.new('ALL')
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Config
6
+ # This module is holding all the Env Variables that we could use through the agent lifecycle
7
+ module EnvVariables
8
+ ENV_VARIABLES = {
9
+ telemetry_opt_outs: ENV['CONTRAST_AGENT_TELEMETRY_OPTOUT'].to_s || Contrast::Config::DefaultValue.new('false')
10
+ }.cs__freeze
11
+
12
+ def return_value key
13
+ return unless ENV_VARIABLES.key?(key.to_sym)
14
+
15
+ sym_key = key.downcase.to_sym
16
+ return_val = ENV_VARIABLES[sym_key]
17
+ if return_val.is_a?(Contrast::Config::DefaultValue)
18
+ return_val.value
19
+ else
20
+ return_val
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -6,6 +6,7 @@ module Contrast
6
6
  # The base of the Common Configuration settings.
7
7
  class RootConfiguration < BaseConfiguration
8
8
  KEYS = {
9
+ api: Contrast::Config::ApiConfiguration,
9
10
  enable: BaseConfiguration::EMPTY_VALUE,
10
11
  agent: Contrast::Config::AgentConfiguration,
11
12
  application: Contrast::Config::ApplicationConfiguration,
@@ -19,7 +19,8 @@ module Contrast
19
19
  host: EMPTY_VALUE,
20
20
  port: EMPTY_VALUE,
21
21
  socket: EMPTY_VALUE,
22
- logger: Contrast::Config::LoggerConfiguration
22
+ logger: Contrast::Config::LoggerConfiguration,
23
+ bypass: Contrast::Config::DefaultValue.new(false)
23
24
  }.cs__freeze
24
25
 
25
26
  def initialize hsh
@@ -24,6 +24,7 @@ require 'contrast/config/protect_rules_configuration'
24
24
  require 'contrast/config/sampling_configuration'
25
25
 
26
26
  require 'contrast/config/ruby_configuration'
27
+ require 'contrast/config/api_configuration'
27
28
  require 'contrast/config/agent_configuration'
28
29
  require 'contrast/config/application_configuration'
29
30
  require 'contrast/config/server_configuration'
@@ -7,6 +7,7 @@ require 'fileutils'
7
7
  require 'contrast/config'
8
8
  require 'contrast/utils/object_share'
9
9
  require 'contrast/components/scope'
10
+ require 'contrast/utils/exclude_key'
10
11
 
11
12
  module Contrast
12
13
  # This is how we read in the local settings for the Agent, both ENV/ CMD line
@@ -218,6 +219,8 @@ module Contrast
218
219
  case convert
219
220
  when Contrast::Config::BaseConfiguration
220
221
  convert.cs__class::KEYS.each_key do |key|
222
+ next if Contrast::Utils::ExcludeKey.excludable? key.to_s
223
+
221
224
  hash[key] = convert_to_hash(convert.send(key), {})
222
225
  end
223
226
  hash
@@ -16,9 +16,9 @@ module Contrast
16
16
  class Manager
17
17
  include Contrast::Components::Logger::InstanceMethods
18
18
 
19
- # Order here does matter as the first framework listed will be the first one we pull information from
20
- # Rack will be a special case that may involve updating some logic to handle only applying Rack if Rails/Sinatra
21
- # do not exist
19
+ # Order here does matter as the first framework listed will be the first one we pull information from Rack will
20
+ # be a special case that may involve updating some logic to handle only applying Rack if Rails/Sinatra do not
21
+ # exist
22
22
  SUPPORTED_FRAMEWORKS = [
23
23
  Contrast::Framework::Rails::Support, Contrast::Framework::Sinatra::Support,
24
24
  Contrast::Framework::Grape::Support, Contrast::Framework::Rack::Support
@@ -34,9 +34,8 @@ module Contrast
34
34
  @_frameworks.compact!
35
35
  end
36
36
 
37
- # Patches that have to be applied as early as possible to catch calls
38
- # that happen prior to the first Request, typically those around
39
- # configuration.
37
+ # Patches that have to be applied as early as possible to catch calls that happen prior to the first Request,
38
+ # typically those around configuration.
40
39
  def before_load_patches!
41
40
  @_before_load_patches ||= begin
42
41
  SUPPORTED_FRAMEWORKS.each(&:before_load_patches!)
@@ -81,8 +80,8 @@ module Contrast
81
80
 
82
81
  # Build a request from the provided env, based on the framework(s) we're currently supporting.
83
82
  #
84
- # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values
85
- # of this particular Request
83
+ # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values of
84
+ # this particular Request
86
85
  # @return [::Rack::Request] either a rack request or subclass thereof.
87
86
  def retrieve_request env
88
87
  # If we're mounted on Rails, use Rails.
@@ -99,8 +98,8 @@ module Contrast
99
98
  logger.warn('Unable to retrieve_request', e)
100
99
  end
101
100
 
102
- # @param env [Hash] the various variables stored by this and other Middlewares to know the state
103
- # and values of this particular Request
101
+ # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values of
102
+ # this particular Request
104
103
  # @return [Boolean] true if at least one framework is streaming the response; false if none are streaming
105
104
  def streaming? env
106
105
  result = false
@@ -111,8 +110,8 @@ module Contrast
111
110
  result
112
111
  end
113
112
 
114
- # Iterate through current frameworks and return the current request's route. This will be the first
115
- # non-nil result.
113
+ # Iterate through current frameworks and return the current request's route. This will be the first non-nil
114
+ # result.
116
115
  #
117
116
  # @param request [Contrast::Agent::Request] the current request.
118
117
  # @return [Contrast::Api::Dtm::RouteCoverage] the current route as a Dtm.
@@ -125,6 +124,9 @@ module Contrast
125
124
  # have support enabled, we'll enable it now. We'll also need to catch up on any other startup actions that we've
126
125
  # missed. Most likely, this is only necessary for those applications which have applications mounted on them.
127
126
  #
127
+ # TODO: RUBY-1354
128
+ # TODO: RUBY-1356
129
+ #
128
130
  # @param mod [Module] the module or class that was just loaded
129
131
  def register_late_framework mod
130
132
  return unless mod
@@ -5,10 +5,14 @@ module Contrast
5
5
  module Framework
6
6
  module Rails
7
7
  module Patch
8
- # This class acts as our patch into the ActionController::Live::Buffer
9
- # class, allowing us to track the close event on streamed responses.
8
+ # This class acts as our patch into the ActionController::Live::Buffer class, allowing us to track the close
9
+ # event on streamed responses.
10
10
  module ActionControllerLiveBuffer
11
11
  class << self
12
+ # TODO: RUBY-1353
13
+ # TODO: RUBY-1355
14
+ # TODO: RUBY-1357
15
+ # TODO: RUBY-1357
12
16
  def send_messages
13
17
  return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
14
18
 
@@ -20,10 +24,9 @@ module Contrast
20
24
  def instrument
21
25
  @_instrument ||= begin
22
26
  ::ActionController::Live::Buffer.class_eval do
23
- # normally pre->in->post filters are applied however, in a streamed response
24
- # we can run into a case where it's pre -> in -> post -> more infilters
25
- # in order to submit anything found during the infilters after the response has
26
- # been written we need to explicitly send them
27
+ # normally pre->in->post filters are applied however, in a streamed response we can run into a case
28
+ # where it's pre -> in -> post -> more infilters in order to submit anything found during the
29
+ # infilters after the response has been written we need to explicitly send them
27
30
  alias_method :cs__close, :close
28
31
  def close
29
32
  Contrast::Framework::Rails::Patch::ActionControllerLiveBuffer.send_messages
@@ -38,37 +38,39 @@ module Contrast
38
38
  instrumenting_module:
39
39
  'Contrast::Framework::Rails::Patch::RailsApplicationConfiguration')
40
40
  ])
41
- if RUBY_VERSION < '2.6.0'
42
- patches.merge([
43
- # TODO: RUBY-714 remove w/ EOL of 2.5
44
- #
45
- # @deprecated Everything past here is used for Rewriting and can
46
- # be removed once we no longer support 2.5.
47
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
48
- 'ActionController::Railties::Helper::ClassMethods',
49
- 'contrast/framework/rails/rewrite/action_controller_railties_helper_inherited',
50
- method_to_instrument: :inherited,
51
- instrumenting_module:
52
- 'Contrast::Framework::Rails::Rewrite::ActionControllerRailtiesHelperInherited'),
53
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
54
- 'ActiveRecord::AttributeMethods::Read::ClassMethods',
55
- 'contrast/framework/rails/rewrite/active_record_attribute_methods_read',
56
- instrumenting_module:
57
- 'Contrast::Framework::Rails::Rewrite::ActiveRecordAttributeMethodsRead'),
58
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
59
- 'ActiveRecord::Scoping::Named::ClassMethods',
60
- 'contrast/framework/rails/rewrite/active_record_named',
61
- instrumenting_module: 'Contrast::Framework::Rails::Rewrite::ActiveRecordNamed'),
62
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
63
- 'ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods',
64
- 'contrast/framework/rails/rewrite/active_record_time_zone_inherited',
65
- method_to_instrument: :inherited,
66
- instrumenting_module:
67
- 'Contrast::Framework::Rails::Rewrite::ActiveRecordTimeZoneInherited')
68
- ])
69
- end
41
+ patches.merge(special_after_load_patches) if RUBY_VERSION < '2.6.0'
70
42
  patches
71
43
  end
44
+
45
+ def special_after_load_patches
46
+ [
47
+ # TODO: RUBY-714 remove w/ EOL of 2.5
48
+ #
49
+ # @deprecated Everything past here is used for Rewriting and can
50
+ # be removed once we no longer support 2.5.
51
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
52
+ 'ActionController::Railties::Helper::ClassMethods',
53
+ 'contrast/framework/rails/rewrite/action_controller_railties_helper_inherited',
54
+ method_to_instrument: :inherited,
55
+ instrumenting_module:
56
+ 'Contrast::Framework::Rails::Rewrite::ActionControllerRailtiesHelperInherited'),
57
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
58
+ 'ActiveRecord::AttributeMethods::Read::ClassMethods',
59
+ 'contrast/framework/rails/rewrite/active_record_attribute_methods_read',
60
+ instrumenting_module:
61
+ 'Contrast::Framework::Rails::Rewrite::ActiveRecordAttributeMethodsRead'),
62
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
63
+ 'ActiveRecord::Scoping::Named::ClassMethods',
64
+ 'contrast/framework/rails/rewrite/active_record_named',
65
+ instrumenting_module: 'Contrast::Framework::Rails::Rewrite::ActiveRecordNamed'),
66
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
67
+ 'ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods',
68
+ 'contrast/framework/rails/rewrite/active_record_time_zone_inherited',
69
+ method_to_instrument: :inherited,
70
+ instrumenting_module:
71
+ 'Contrast::Framework::Rails::Rewrite::ActiveRecordTimeZoneInherited')
72
+ ]
73
+ end
72
74
  end
73
75
  end
74
76
  end
@@ -1,6 +1,8 @@
1
1
  # Copyright (c) 2021 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/exclude_key'
5
+
4
6
  module Contrast
5
7
  module Logger
6
8
  # Our decorator for the Ougai logger allowing for the logging of the
@@ -29,6 +31,8 @@ module Contrast
29
31
  loggable = ::Contrast::CONFIG.loggable
30
32
  info('Current configuration', configuration: loggable)
31
33
  env_keys = ENV.keys.select do |env_key|
34
+ next if Contrast::Utils::ExcludeKey.excludable? env_key.to_s
35
+
32
36
  env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER)
33
37
  end
34
38
  env_items = env_keys.map { |env_key| Contrast::Utils::EnvConfigurationItem.new(env_key, nil) }
@@ -0,0 +1,129 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Utils
6
+ module Assess
7
+ # This module will include all methods for some internal validations in the PropagationMethod module
8
+ # and some other module methods from the same place, so we can ease the main module
9
+ module PropagationMethodUtils
10
+ APPEND_ACTION = 'APPEND'
11
+ CENTER_ACTION = 'CENTER'
12
+ INSERT_ACTION = 'INSERT'
13
+ KEEP_ACTION = 'KEEP'
14
+ NEXT_ACTION = 'NEXT'
15
+ NOOP_ACTION = 'NOOP'
16
+ PREPEND_ACTION = 'PREPEND'
17
+ REPLACE_ACTION = 'REPLACE'
18
+ REMOVE_ACTION = 'REMOVE'
19
+ REVERSE_ACTION = 'REVERSE'
20
+ SPLAT_ACTION = 'SPLAT'
21
+ SPLIT_ACTION = 'SPLIT'
22
+ DB_WRITE_ACTION = 'DB_WRITE'
23
+ CUSTOM_ACTION = 'CUSTOM'
24
+
25
+ ZERO_LENGTH_ACTIONS = [DB_WRITE_ACTION, CUSTOM_ACTION, KEEP_ACTION, REPLACE_ACTION, SPLAT_ACTION].cs__freeze
26
+
27
+ PROPAGATION_ACTIONS = {
28
+ APPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Append,
29
+ CENTER_ACTION => Contrast::Agent::Assess::Policy::Propagator::Center,
30
+ INSERT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Insert,
31
+ KEEP_ACTION => Contrast::Agent::Assess::Policy::Propagator::Keep,
32
+ NEXT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Next,
33
+ NOOP_ACTION => nil,
34
+ PREPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Prepend,
35
+ REPLACE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Replace,
36
+ REMOVE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Remove,
37
+ REVERSE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Reverse,
38
+ SPLAT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Splat,
39
+ SPLIT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Split
40
+ }.cs__freeze
41
+
42
+ def determine_target propagation_node, ret, object, args
43
+ target = propagation_node.targets[0]
44
+ case target
45
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
46
+ object
47
+ when Contrast::Utils::ObjectShare::RETURN_KEY
48
+ ret
49
+ else
50
+ args[target]
51
+ end
52
+ end
53
+
54
+ # Custom actions tend to be the more complex of our propagations. Often, the method has to make decisions
55
+ # about the target based on the context with which the method was called. As such, defer determining if the
56
+ # target is valid to that method.
57
+ #
58
+ # In all other cases, a target is valid for propagation if it is not nil
59
+ #
60
+ # @param target [Object] the thing to which to propagate
61
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
62
+ # propagation event.
63
+ # @return [Boolean]
64
+ def valid_target? target, propagation_node
65
+ return true if propagation_node.action == CUSTOM_ACTION
66
+
67
+ !!target
68
+ end
69
+
70
+ # If the action required needs a length and the target does not have one, the length is not valid
71
+ #
72
+ # @param target [Object] the thing to which to propagate
73
+ # @param action [String] the name of the action taken during this propagation
74
+ # @return [Boolean]
75
+ def valid_length? target, action
76
+ return true if ZERO_LENGTH_ACTIONS.include?(action)
77
+
78
+ if Contrast::Utils::DuckUtils.quacks_to?(target, :length)
79
+ target.length != 0 # rubocop:disable Style/ZeroLengthPredicate
80
+ else
81
+ !target.to_s.empty?
82
+ end
83
+ end
84
+
85
+ # Before we do any work, we should check if we even need to. If the source and target of this patcher are
86
+ # not tracked, there's no need to do anything. A copy of nothing is still nothing.
87
+ #
88
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
89
+ # propagation event.
90
+ # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
91
+ # the invocation of the patched method.
92
+ # @param target [Object] the thing to which to propagate
93
+ # @return [Boolean]
94
+ def can_propagate? propagation_node, preshift, target
95
+ return false unless appropriate_target?(propagation_node, target)
96
+ return true if Contrast::Utils::Assess::TrackingUtil.tracked?(target)
97
+ return false unless preshift
98
+
99
+ propagation_node.sources.each do |source|
100
+ case source
101
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
102
+ return true if Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.object)
103
+ else
104
+ # has to be P, there's no ret source type (yet? ever?)
105
+ return true if preshift.args && Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.args[source])
106
+ end
107
+ end
108
+ false
109
+ end
110
+
111
+ # We cannot propagate to frozen things that have not been updated to work with our property tracking,
112
+ # unless they're duplicable and the return. We probably shouldn't propagate to frozen things at all, as
113
+ # they're supposed to be immutable, but third parties do jenky things, so allow it as long as it is safe to
114
+ # do.
115
+ #
116
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
117
+ # propagation event.
118
+ # @param target [Object] the Target to which to propagate.
119
+ # @return [Boolean] if the target can be propagated to
120
+ def appropriate_target? propagation_node, target
121
+ # special handle Returns b/c we can do unfreezing magic during propagation
122
+ return true if propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
123
+
124
+ Contrast::Agent::Assess::Tracker.trackable?(target)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end