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.
- checksums.yaml +4 -4
- data/ext/cs__assess_module/cs__assess_module.c +48 -0
- data/ext/cs__assess_module/cs__assess_module.h +7 -0
- data/ext/cs__common/cs__common.c +24 -7
- data/ext/cs__common/cs__common.h +12 -2
- data/ext/cs__contrast_patch/cs__contrast_patch.c +48 -11
- data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -2
- data/ext/cs__os_information/cs__os_information.c +31 -0
- data/ext/cs__os_information/cs__os_information.h +7 -0
- data/ext/{cs__protect_kernel → cs__os_information}/extconf.rb +0 -0
- data/lib/contrast/agent/assess/contrast_event.rb +1 -1
- data/lib/contrast/agent/assess/contrast_object.rb +1 -4
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
- data/lib/contrast/agent/assess/policy/preshift.rb +25 -11
- data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
- data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -0
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +4 -4
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
- data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
- data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -107
- data/lib/contrast/agent/assess/policy/trigger_node.rb +52 -19
- data/lib/contrast/agent/assess/property/tagged.rb +15 -132
- data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +2 -1
- data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
- data/lib/contrast/agent/middleware.rb +22 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +0 -1
- data/lib/contrast/agent/patching/policy/method_policy.rb +54 -9
- data/lib/contrast/agent/patching/policy/patch.rb +37 -238
- data/lib/contrast/agent/patching/policy/patcher.rb +3 -42
- data/lib/contrast/agent/request.rb +5 -3
- data/lib/contrast/agent/request_context.rb +32 -11
- data/lib/contrast/agent/request_handler.rb +7 -3
- data/lib/contrast/agent/rule_set.rb +2 -4
- data/lib/contrast/agent/scope.rb +32 -20
- data/lib/contrast/agent/startup_metrics_telemetry_event.rb +71 -0
- data/lib/contrast/agent/static_analysis.rb +4 -2
- data/lib/contrast/agent/telemetry.rb +129 -0
- data/lib/contrast/agent/telemetry_event.rb +34 -0
- data/lib/contrast/agent/thread_watcher.rb +43 -14
- data/lib/contrast/agent/tracepoint_hook.rb +11 -3
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +6 -1
- data/lib/contrast/components/api.rb +34 -0
- data/lib/contrast/components/app_context.rb +24 -0
- data/lib/contrast/components/assess.rb +7 -0
- data/lib/contrast/components/config.rb +90 -11
- data/lib/contrast/components/contrast_service.rb +6 -0
- data/lib/contrast/config/api_configuration.rb +22 -0
- data/lib/contrast/config/assess_configuration.rb +1 -0
- data/lib/contrast/config/env_variables.rb +25 -0
- data/lib/contrast/config/root_configuration.rb +1 -0
- data/lib/contrast/config/service_configuration.rb +2 -1
- data/lib/contrast/config.rb +1 -0
- data/lib/contrast/configuration.rb +3 -0
- data/lib/contrast/framework/manager.rb +14 -12
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
- data/lib/contrast/framework/rails/patch/support.rb +31 -29
- data/lib/contrast/logger/application.rb +4 -0
- data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
- data/lib/contrast/utils/assess/property/tagged_utils.rb +142 -0
- data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
- data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
- data/lib/contrast/utils/class_util.rb +58 -44
- data/lib/contrast/utils/exclude_key.rb +20 -0
- data/lib/contrast/utils/io_util.rb +42 -34
- data/lib/contrast/utils/lru_cache.rb +45 -0
- data/lib/contrast/utils/metrics_hash.rb +59 -0
- data/lib/contrast/utils/os.rb +23 -0
- data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
- data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
- data/lib/contrast/utils/requests_client.rb +150 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +1 -1
- data/lib/contrast/utils/telemetry.rb +77 -0
- data/lib/contrast/utils/telemetry_identifier.rb +137 -0
- data/lib/contrast.rb +19 -1
- data/resources/assess/policy.json +12 -6
- data/resources/deadzone/policy.json +86 -5
- data/ruby-agent.gemspec +2 -1
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +32 -14
- data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
- data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
- 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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
#
|
87
|
+
# so we use basic proto_logger to do this job.
|
67
88
|
if !session_id.empty? && !session_metadata.empty?
|
68
|
-
|
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
|
data/lib/contrast/config.rb
CHANGED
@@ -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
|
-
#
|
21
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
25
|
-
#
|
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
|