contrast-agent 6.8.0 → 6.9.0
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/lib/contrast/agent/assess/policy/trigger_method.rb +1 -1
- data/lib/contrast/agent/assess/property/evented.rb +11 -11
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/excluder.rb +1 -1
- data/lib/contrast/agent/middleware.rb +8 -2
- data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +116 -0
- data/lib/contrast/agent/protect/rule/base.rb +2 -2
- data/lib/contrast/agent/protect/rule/bot_blocker.rb +1 -1
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +8 -7
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_chained_command.rb +0 -5
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_dangerous_path.rb +0 -5
- data/lib/contrast/agent/protect/rule/path_traversal.rb +4 -3
- data/lib/contrast/agent/protect/rule/sqli.rb +4 -3
- data/lib/contrast/agent/protect/rule/xss.rb +1 -0
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +1 -1
- data/lib/contrast/agent/reporting/report.rb +1 -0
- data/lib/contrast/agent/reporting/reporter.rb +34 -0
- data/lib/contrast/agent/reporting/reporter_heartbeat.rb +3 -9
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +12 -7
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -1
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +239 -93
- data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +10 -23
- data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +10 -9
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +12 -0
- data/lib/contrast/agent/reporting/reporting_events/server_reporting_event.rb +8 -0
- data/lib/contrast/agent/reporting/reporting_events/server_settings.rb +40 -0
- data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +6 -0
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +43 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -4
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +58 -4
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +4 -3
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +76 -16
- data/lib/contrast/agent/reporting/server_settings_worker.rb +44 -0
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +14 -2
- data/lib/contrast/agent/reporting/settings/helpers.rb +7 -0
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +39 -2
- data/lib/contrast/agent/reporting/settings/rule_definition.rb +3 -0
- data/lib/contrast/agent/reporting/settings/security_logger.rb +77 -0
- data/lib/contrast/agent/reporting/settings/server_features.rb +9 -0
- data/lib/contrast/agent/reporting/settings/syslog.rb +34 -5
- data/lib/contrast/agent/request.rb +1 -0
- data/lib/contrast/agent/request_handler.rb +5 -10
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +1 -1
- data/lib/contrast/agent/thread_watcher.rb +35 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +6 -0
- data/lib/contrast/api/communication/connection_status.rb +15 -0
- data/lib/contrast/components/agent.rb +34 -0
- data/lib/contrast/components/api.rb +23 -0
- data/lib/contrast/components/app_context.rb +23 -3
- data/lib/contrast/components/assess.rb +34 -4
- data/lib/contrast/components/assess_rules.rb +18 -0
- data/lib/contrast/components/base.rb +40 -0
- data/lib/contrast/components/config/sources.rb +95 -0
- data/lib/contrast/components/config.rb +18 -1
- data/lib/contrast/components/heap_dump.rb +10 -0
- data/lib/contrast/components/inventory.rb +15 -2
- data/lib/contrast/components/logger.rb +18 -0
- data/lib/contrast/components/polling.rb +36 -0
- data/lib/contrast/components/protect.rb +48 -1
- data/lib/contrast/components/ruby_component.rb +15 -0
- data/lib/contrast/components/sampling.rb +70 -13
- data/lib/contrast/components/security_logger.rb +13 -0
- data/lib/contrast/components/settings.rb +74 -7
- data/lib/contrast/config/certification_configuration.rb +14 -0
- data/lib/contrast/config/config.rb +46 -0
- data/lib/contrast/config/diagnostics.rb +114 -0
- data/lib/contrast/config/diagnostics_tools.rb +98 -0
- data/lib/contrast/config/effective_config.rb +65 -0
- data/lib/contrast/config/effective_config_value.rb +32 -0
- data/lib/contrast/config/exception_configuration.rb +12 -0
- data/lib/contrast/config/protect_rule_configuration.rb +1 -1
- data/lib/contrast/config/protect_rules_configuration.rb +8 -7
- data/lib/contrast/config/request_audit_configuration.rb +13 -0
- data/lib/contrast/config/server_configuration.rb +41 -2
- data/lib/contrast/configuration.rb +28 -2
- data/lib/contrast/extension/assess/erb.rb +1 -1
- data/lib/contrast/utils/assess/event_limit_utils.rb +31 -9
- data/lib/contrast/utils/assess/trigger_method_utils.rb +5 -4
- data/lib/contrast/utils/hash_digest.rb +2 -2
- data/lib/contrast/utils/input_classification_base.rb +1 -2
- data/lib/contrast/utils/reporting/application_activity_batch_utils.rb +81 -0
- data/lib/contrast/utils/routes_sent.rb +60 -0
- data/lib/contrast/utils/telemetry_client.rb +1 -2
- data/lib/contrast/utils/timer.rb +16 -0
- data/lib/contrast.rb +3 -1
- data/ruby-agent.gemspec +5 -1
- metadata +29 -20
- data/lib/contrast/agent/assess/contrast_event.rb +0 -157
- data/lib/contrast/agent/assess/events/event_factory.rb +0 -34
- data/lib/contrast/agent/assess/events/source_event.rb +0 -46
- data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +0 -36
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
require 'contrast/agent/excluder'
|
|
5
5
|
require 'contrast/agent/reporting/settings/sensitive_data_masking'
|
|
6
6
|
require 'contrast/components/config'
|
|
7
|
+
require 'contrast/components/logger'
|
|
7
8
|
|
|
8
9
|
module Contrast
|
|
9
10
|
module Components
|
|
@@ -24,7 +25,8 @@ module Contrast
|
|
|
24
25
|
SENSITIVE_DATA_MASKING_BASE = Contrast::Agent::Reporting::Settings::SensitiveDataMasking.new
|
|
25
26
|
|
|
26
27
|
# This is a class.
|
|
27
|
-
class Interface
|
|
28
|
+
class Interface # rubocop:disable Metrics/ClassLength
|
|
29
|
+
include Contrast::Components::Logger::InstanceMethods
|
|
28
30
|
extend Contrast::Components::Config
|
|
29
31
|
|
|
30
32
|
# tainted_columns are database columns that receive unsanitized input.
|
|
@@ -68,6 +70,11 @@ module Contrast
|
|
|
68
70
|
attr_reader :last_server_update_ms
|
|
69
71
|
# @return [Contrast::Agent::Excluder] a wrapper around the exclusion rules for the application
|
|
70
72
|
attr_reader :excluder
|
|
73
|
+
# @return [String] The last update but in string format used to build request header.
|
|
74
|
+
# This value should be sent be TS in the Last-Modified header to sync and save resources if the
|
|
75
|
+
# two dates are the same.
|
|
76
|
+
# format: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
|
|
77
|
+
attr_reader :server_settings_last_httpdate
|
|
71
78
|
|
|
72
79
|
def initialize
|
|
73
80
|
reset_state
|
|
@@ -83,11 +90,43 @@ module Contrast
|
|
|
83
90
|
|
|
84
91
|
log_file = server_features.log_file
|
|
85
92
|
log_level = server_features.log_level
|
|
93
|
+
# Update logger:
|
|
86
94
|
Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
# Update CEFlogger:
|
|
96
|
+
unless server_features.security_logger.settings_blank?
|
|
97
|
+
cef_logger.build_logger(server_features.security_logger.log_level, server_features.security_logger.log_file)
|
|
98
|
+
end
|
|
99
|
+
# TODO: RUBY-99999 Update Bot-Blocker from server settings - check enable value.
|
|
100
|
+
# For now all protection rules are rebuild on Application update. Bot blocker uses the default
|
|
101
|
+
# enable from the base rule, and update it's mode on app settings update.
|
|
102
|
+
# Here we receive also bots for that rule.
|
|
103
|
+
unless settings_empty?(server_features.protect.enabled?)
|
|
104
|
+
@protect_state.enabled = server_features.protect.enabled?
|
|
105
|
+
store_in_config(%i[protect enable], server_features.protect.enabled?)
|
|
106
|
+
end
|
|
107
|
+
update_assess_server_features(server_features.assess)
|
|
90
108
|
@last_server_update_ms = Contrast::Utils::Timer.now_ms
|
|
109
|
+
# update via response header. We receive header from TS with last update info, setting the
|
|
110
|
+
# next request's header with the same time will save needless update of settings if there
|
|
111
|
+
# are no new server features updates after the said time.
|
|
112
|
+
@server_settings_last_httpdate = header_last_update
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Update Assess server features
|
|
116
|
+
#
|
|
117
|
+
# @param assess [Contrast::Agent::Reporting::Settings::AssessServerFeature]
|
|
118
|
+
def update_assess_server_features assess
|
|
119
|
+
return if settings_empty?(assess.enabled?)
|
|
120
|
+
|
|
121
|
+
@assess_state.enabled = assess.enabled?
|
|
122
|
+
store_in_config(%i[assess enable], assess.enabled?)
|
|
123
|
+
@assess_state.sampling_settings = assess.sampling
|
|
124
|
+
|
|
125
|
+
Contrast::Components::Sampling::Interface::CONFIG_VALUES.each do |field|
|
|
126
|
+
lookup_field = field == 'enable' ? :enabled : field.to_sym
|
|
127
|
+
store_in_config(Contrast::Components::Sampling::Interface::CANON_NAME.split('.') + [field.to_sym],
|
|
128
|
+
assess.sampling.send(lookup_field))
|
|
129
|
+
end
|
|
91
130
|
end
|
|
92
131
|
|
|
93
132
|
# @param settings_response [Contrast::Agent::Reporting::Response]
|
|
@@ -118,12 +157,15 @@ module Contrast
|
|
|
118
157
|
|
|
119
158
|
# Rules. They add themselves on initialize.
|
|
120
159
|
Contrast::Agent::Protect::Rule::BotBlocker.new
|
|
121
|
-
Contrast::Agent::Protect::Rule::CmdInjection.new
|
|
160
|
+
cmdi = Contrast::Agent::Protect::Rule::CmdInjection.new
|
|
161
|
+
cmdi.sub_rules
|
|
122
162
|
Contrast::Agent::Protect::Rule::Deserialization.new
|
|
123
163
|
Contrast::Agent::Protect::Rule::HttpMethodTampering.new
|
|
124
164
|
Contrast::Agent::Protect::Rule::NoSqli.new
|
|
125
|
-
Contrast::Agent::Protect::Rule::PathTraversal.new
|
|
126
|
-
|
|
165
|
+
path = Contrast::Agent::Protect::Rule::PathTraversal.new
|
|
166
|
+
path.sub_rules
|
|
167
|
+
sqli = Contrast::Agent::Protect::Rule::Sqli.new
|
|
168
|
+
sqli.sub_rules
|
|
127
169
|
Contrast::Agent::Protect::Rule::UnsafeFileUpload.new
|
|
128
170
|
Contrast::Agent::Protect::Rule::Xss.new
|
|
129
171
|
Contrast::Agent::Protect::Rule::Xxe.new
|
|
@@ -174,6 +216,31 @@ module Contrast
|
|
|
174
216
|
|
|
175
217
|
false
|
|
176
218
|
end
|
|
219
|
+
|
|
220
|
+
# update via response header.
|
|
221
|
+
# Used to build the next request header.
|
|
222
|
+
#
|
|
223
|
+
# @return [String]
|
|
224
|
+
def header_last_update
|
|
225
|
+
Contrast::Agent.reporter.client.response_handler.last_server_modified
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Update the stored config values to ensure that we know about the correct values,
|
|
229
|
+
# and that the sources are correct for entries updated from the UI.
|
|
230
|
+
#
|
|
231
|
+
# @param parts [Array] the path to the setting in config
|
|
232
|
+
# @param value [String, Integer, Array, nil] the value for the configuration setting
|
|
233
|
+
def store_in_config parts, value
|
|
234
|
+
level = Contrast::CONFIG.config.loaded_config
|
|
235
|
+
parts[0...-1].each do |segment|
|
|
236
|
+
level[segment] ||= {}
|
|
237
|
+
level = level[segment]
|
|
238
|
+
end
|
|
239
|
+
return unless level.cs__is_a?(Hash)
|
|
240
|
+
|
|
241
|
+
level[parts[-1]] = value
|
|
242
|
+
Contrast::CONFIG.sources.set(parts.join('.'), Contrast::Components::Config::Sources::CONTRASTUI)
|
|
243
|
+
end
|
|
177
244
|
end
|
|
178
245
|
end
|
|
179
246
|
end
|
|
@@ -1,11 +1,17 @@
|
|
|
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 Config
|
|
6
8
|
# Certificate Configuration
|
|
7
9
|
class CertificationConfiguration
|
|
8
10
|
include Contrast::Config::BaseConfiguration
|
|
11
|
+
include Contrast::Components::ComponentBase
|
|
12
|
+
|
|
13
|
+
CANON_NAME = 'api.certification'
|
|
14
|
+
CONFIG_VALUES = %w[ca_file cert_file key_file enable].cs__freeze
|
|
9
15
|
|
|
10
16
|
# @return [String] path to CA Cert file
|
|
11
17
|
attr_accessor :ca_file
|
|
@@ -28,6 +34,14 @@ module Contrast
|
|
|
28
34
|
def enable
|
|
29
35
|
@enable.nil? ? false : @enable
|
|
30
36
|
end
|
|
37
|
+
|
|
38
|
+
# Converts current configuration to effective config values class and appends them to
|
|
39
|
+
# EffectiveConfig class.
|
|
40
|
+
#
|
|
41
|
+
# @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
|
|
42
|
+
def to_effective_config effective_config
|
|
43
|
+
add_effective_config_values(effective_config, CONFIG_VALUES, CANON_NAME, CONTRAST)
|
|
44
|
+
end
|
|
31
45
|
end
|
|
32
46
|
end
|
|
33
47
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
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/config/effective_config'
|
|
5
|
+
|
|
6
|
+
module Contrast
|
|
7
|
+
module Agent
|
|
8
|
+
module DiagnosticsConfig
|
|
9
|
+
# This class is responsible for logging to file the effective Agent configurations after startup.
|
|
10
|
+
class Config
|
|
11
|
+
attr_reader :effective_config
|
|
12
|
+
|
|
13
|
+
MESSAGE_FAIL = 'Unable to connect to Contrast, configuration details from the Contrast UI will not be included.'
|
|
14
|
+
MESSAGE_SUCCESSFUL = 'Connected to Contrast.'
|
|
15
|
+
CONN_STATUS_MSG_FAILURE = 'Unable to connect to Contrast, insufficient connection properties provided.'
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@effective_config = Contrast::Agent::DiagnosticsConfig::EffectiveConfig.new
|
|
19
|
+
@config_status = Contrast::Utils::ObjectShare::EMPTY_STRING
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# This method will set the status from the request/response cycles
|
|
23
|
+
#
|
|
24
|
+
# @param response [Contrast::Agent::Reporting::Response]
|
|
25
|
+
def determine_config_status response
|
|
26
|
+
return unless response
|
|
27
|
+
# If we encounter for some of the startup events failure - always return failure
|
|
28
|
+
return if @config_status == MESSAGE_FAIL || CONN_STATUS_MSG_FAILURE
|
|
29
|
+
|
|
30
|
+
response_code = response.code.to_s
|
|
31
|
+
@config_status = response_code.starts_with?('2') ? MESSAGE_SUCCESSFUL : MESSAGE_FAIL
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# This method will set the status message from the config validation
|
|
36
|
+
def populate_fail_connection
|
|
37
|
+
@config_status = CONN_STATUS_MSG_FAILURE
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def to_controlled_hash
|
|
41
|
+
@effective_config.to_controlled_hash.merge({ Status: @config_status })
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
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 'json'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'contrast/utils/timer'
|
|
7
|
+
require 'contrast/utils/log_utils'
|
|
8
|
+
require 'contrast/components/logger'
|
|
9
|
+
require 'contrast/utils/object_share'
|
|
10
|
+
require 'contrast/config/config'
|
|
11
|
+
require 'contrast/config/effective_config'
|
|
12
|
+
require 'contrast/config/effective_config_value'
|
|
13
|
+
|
|
14
|
+
module Contrast
|
|
15
|
+
module Agent
|
|
16
|
+
module DiagnosticsConfig
|
|
17
|
+
# This class is responsible for logging to file the effective Agent configurations after startup.
|
|
18
|
+
class Diagnostics
|
|
19
|
+
include Contrast::Components::Logger::InstanceMethods
|
|
20
|
+
include Contrast::Utils::LogUtils
|
|
21
|
+
|
|
22
|
+
# @return [String] path to write the file.
|
|
23
|
+
attr_reader :path
|
|
24
|
+
|
|
25
|
+
DEFAULT_PATH = File.join(Dir.pwd).cs__freeze
|
|
26
|
+
ERROR_MESSAGE = '[Configuration Diagnostics] Could not write effective Agent configuration to file'
|
|
27
|
+
FILE_NAME = 'contrast_connection.json'
|
|
28
|
+
WRITE = 'w+'
|
|
29
|
+
|
|
30
|
+
# @param path [String] path to write to file.
|
|
31
|
+
def initialize path
|
|
32
|
+
@path = path if path && !path.empty?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Write current settings to file.
|
|
36
|
+
#
|
|
37
|
+
# @param reset [Boolean] should we reset the config we have
|
|
38
|
+
# @return [Boolean]
|
|
39
|
+
def write_to_file reset: true
|
|
40
|
+
logger&.info('[Configuration Diagnostics] Writing Effective Configurations to file', path: dir_name)
|
|
41
|
+
status = false
|
|
42
|
+
write_to_file_logic(status, reset: reset)
|
|
43
|
+
rescue IOError => e
|
|
44
|
+
logger&.warn(ERROR_MESSAGE, e)
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def extract_settings
|
|
49
|
+
Contrast::AGENT.to_effective_config(config.effective_config)
|
|
50
|
+
Contrast::API.to_effective_config(config.effective_config)
|
|
51
|
+
Contrast::APP_CONTEXT.to_effective_config(config.effective_config)
|
|
52
|
+
Contrast::ASSESS.to_effective_config(config.effective_config)
|
|
53
|
+
Contrast::INVENTORY.to_effective_config(config.effective_config)
|
|
54
|
+
Contrast::PROTECT.to_effective_config(config.effective_config)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Determine the path of the current logger and permissions required for
|
|
58
|
+
# writing to path.
|
|
59
|
+
#
|
|
60
|
+
# @return [String] Path
|
|
61
|
+
def dir_name
|
|
62
|
+
@_dir_name ||= if write_permission?(@path)
|
|
63
|
+
File.dirname(File.absolute_path(@path))
|
|
64
|
+
else
|
|
65
|
+
DEFAULT_PATH
|
|
66
|
+
end
|
|
67
|
+
rescue Errno::EROFS => e
|
|
68
|
+
logger.warn(ERROR_MESSAGE, e)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Returns effective configurations of the agent.
|
|
72
|
+
#
|
|
73
|
+
# @return [Contrast::Agent::DiagnosticsConfig::Config]
|
|
74
|
+
def config
|
|
75
|
+
@_config ||= Contrast::Agent::DiagnosticsConfig::Config.new
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Reset the state of the current effective config.
|
|
79
|
+
def reset_config
|
|
80
|
+
@_config = Contrast::Agent::DiagnosticsConfig::Config.new
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def to_controlled_hash
|
|
84
|
+
{
|
|
85
|
+
ReportCreate: report_create_time,
|
|
86
|
+
Config: config.to_controlled_hash
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def write_to_file_logic status, reset: true
|
|
91
|
+
# We need to reset the config before updating it's values
|
|
92
|
+
reset_config if reset
|
|
93
|
+
extract_settings
|
|
94
|
+
File.open(File.join(dir_name, FILE_NAME), WRITE) do |file|
|
|
95
|
+
file.truncate(0)
|
|
96
|
+
file.write(JSON.pretty_generate(to_controlled_hash, { space: Contrast::Utils::ObjectShare::EMPTY_STRING }))
|
|
97
|
+
status = true if file
|
|
98
|
+
file.close
|
|
99
|
+
end
|
|
100
|
+
status
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# Return current time in iso8601 format.
|
|
106
|
+
#
|
|
107
|
+
# @return [String] Time of creation
|
|
108
|
+
def report_create_time
|
|
109
|
+
Contrast::Utils::Timer.time_now
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
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/utils/object_share'
|
|
5
|
+
|
|
6
|
+
module Contrast
|
|
7
|
+
module Agent
|
|
8
|
+
module DiagnosticsConfig
|
|
9
|
+
# Diagnostics tools to be included in config components.
|
|
10
|
+
module DiagnosticsTools
|
|
11
|
+
CHECK = 'd'
|
|
12
|
+
|
|
13
|
+
# Converts current configuration for array of values to effective config values class and appends them to
|
|
14
|
+
# EffectiveConfig class. Must be used inside Config Components only.
|
|
15
|
+
#
|
|
16
|
+
# @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
|
|
17
|
+
# @param config_values [Array<String>] array of the names of values.
|
|
18
|
+
# @param canonical_prefix [String] starting of the path to config => api.proxy...
|
|
19
|
+
# @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
|
|
20
|
+
def add_effective_config_values effective_config, config_values, canonical_prefix, name_prefix
|
|
21
|
+
return if config_values.to_s.empty?
|
|
22
|
+
|
|
23
|
+
config_values.each do |config|
|
|
24
|
+
Contrast::Agent::DiagnosticsConfig::EffectiveConfigValue.new.tap do |value|
|
|
25
|
+
next if (config_val = send(config.to_sym)).to_s.empty?
|
|
26
|
+
|
|
27
|
+
config_name = assign_name(config)
|
|
28
|
+
value.canonical_name = "#{ canonical_prefix }.#{ config_name }"
|
|
29
|
+
value.name = "#{ name_prefix }.#{ config_name }"
|
|
30
|
+
value.value = config_val
|
|
31
|
+
value.source = Contrast::CONFIG.sources.get(value.canonical_name)
|
|
32
|
+
if value.source == Contrast::Components::Config::Sources::YAML
|
|
33
|
+
value.filename = Contrast::CONFIG.config_file_path
|
|
34
|
+
end
|
|
35
|
+
effective_config.values << value
|
|
36
|
+
rescue StandardError => e
|
|
37
|
+
log_error(e)
|
|
38
|
+
next
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Converts current configuration for single value to effective config values class and appends them to
|
|
44
|
+
# EffectiveConfig class. Must be used inside Config Components only.
|
|
45
|
+
#
|
|
46
|
+
# @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
|
|
47
|
+
# @param config_name [String] name of the config.
|
|
48
|
+
# @param config_value [String, Boolean] value of the config.
|
|
49
|
+
# @param canonical_prefix [String] starting of the path to config => api.proxy...
|
|
50
|
+
# @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
|
|
51
|
+
def add_single_effective_value effective_config, config_name, config_value, canonical_prefix, name_prefix
|
|
52
|
+
Contrast::Agent::DiagnosticsConfig::EffectiveConfigValue.new.tap do |value|
|
|
53
|
+
break if config_value.to_s.empty?
|
|
54
|
+
|
|
55
|
+
value.value = config_value
|
|
56
|
+
value.canonical_name = "#{ canonical_prefix }.#{ config_name }"
|
|
57
|
+
value.name = "#{ name_prefix }.#{ config_name }"
|
|
58
|
+
value.source = Contrast::CONFIG.sources.get(value.canonical_name)
|
|
59
|
+
if value.source == Contrast::Components::Config::Sources::YAML
|
|
60
|
+
value.filename = Contrast::CONFIG.config_file_path
|
|
61
|
+
end
|
|
62
|
+
effective_config.values << value
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
log_error(e)
|
|
65
|
+
next
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Assigns a proper name for the config removing '?' out of method names.
|
|
72
|
+
#
|
|
73
|
+
# @param config [String] name of the configuration
|
|
74
|
+
# @return [String]
|
|
75
|
+
def assign_name config
|
|
76
|
+
return Contrast::Utils::ObjectShare::EMPTY_STRING unless config
|
|
77
|
+
|
|
78
|
+
name = config.dup
|
|
79
|
+
if name.end_with?(Contrast::Utils::ObjectShare::QUESTION_MARK)
|
|
80
|
+
# check and remove '?' : start_bundled_service? => start_bundled_service
|
|
81
|
+
name.delete!(Contrast::Utils::ObjectShare::QUESTION_MARK)
|
|
82
|
+
name.chop! if name.end_with?(CHECK)
|
|
83
|
+
name
|
|
84
|
+
end
|
|
85
|
+
name
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Logs any caught error.
|
|
89
|
+
#
|
|
90
|
+
# @param error [StandardError]
|
|
91
|
+
def log_error error
|
|
92
|
+
Contrast::CONFIG.proto_logger.warn('Could not write effective config to file: ',
|
|
93
|
+
error: error, backtrace: error.backtrace)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
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/config/effective_config_value'
|
|
5
|
+
|
|
6
|
+
module Contrast
|
|
7
|
+
module Agent
|
|
8
|
+
module DiagnosticsConfig
|
|
9
|
+
# The current effective config received from all authorized configuration channels.
|
|
10
|
+
class EffectiveConfig
|
|
11
|
+
# Value of effective agent configurations
|
|
12
|
+
#
|
|
13
|
+
# @return [Array]
|
|
14
|
+
attr_reader :values
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@values = []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_controlled_hash
|
|
21
|
+
{
|
|
22
|
+
EffectiveConfig: { values: @values&.map(&:to_controlled_hash) },
|
|
23
|
+
File: yaml_config_settings,
|
|
24
|
+
Environment: environment_settings,
|
|
25
|
+
CommandLine: command_line_settings,
|
|
26
|
+
ContrastUI: contrast_ui_settings
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def yaml_config_settings
|
|
33
|
+
{
|
|
34
|
+
Path: Contrast::CONFIG.config_file_path,
|
|
35
|
+
Values: Contrast::CONFIG.sources.for(Contrast::Components::Config::Sources::YAML)
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def command_line_settings
|
|
40
|
+
flatten_settings(Contrast::CONFIG.sources.for(Contrast::Components::Config::Sources::CLI))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def contrast_ui_settings
|
|
44
|
+
flatten_settings(Contrast::CONFIG.sources.for(Contrast::Components::Config::Sources::CONTRASTUI))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def flatten_settings data, path = []
|
|
48
|
+
data.each_with_object([]) do |(k, v), entries|
|
|
49
|
+
if v.cs__is_a?(Hash)
|
|
50
|
+
entries.concat(flatten_settings(v, path.dup.append(k.to_sym)))
|
|
51
|
+
else
|
|
52
|
+
entries << { "#{ path.join('.') }.#{ k }" => Contrast::CONFIG.config.loaded_config.dig(*path, k) }
|
|
53
|
+
end
|
|
54
|
+
end.flatten # rubocop:disable Style/MethodCalledOnDoEndBlock
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def environment_settings
|
|
58
|
+
ENV.select { |e| e.to_s.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER) }.
|
|
59
|
+
to_a.
|
|
60
|
+
map { |e| Hash[*e] }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
module Contrast
|
|
5
|
+
module Agent
|
|
6
|
+
module DiagnosticsConfig
|
|
7
|
+
# All In effect config values stored in a easy to write representation.
|
|
8
|
+
class EffectiveConfigValue
|
|
9
|
+
# @return [String] Name of the config starting form root of yaml config.
|
|
10
|
+
attr_accessor :canonical_name
|
|
11
|
+
# @return [String] Name of the config.
|
|
12
|
+
attr_accessor :name
|
|
13
|
+
# @return [String] Value set for the config.
|
|
14
|
+
attr_accessor :value
|
|
15
|
+
# @return [String] The source for the entry in the config.
|
|
16
|
+
attr_accessor :source
|
|
17
|
+
# @return [String,nil] The filename for the source of the config, if the source was "yaml".
|
|
18
|
+
attr_accessor :filename
|
|
19
|
+
|
|
20
|
+
def to_controlled_hash
|
|
21
|
+
{
|
|
22
|
+
canonical_name: canonical_name,
|
|
23
|
+
name: name, # rubocop:disable Security/Module/Name
|
|
24
|
+
value: value,
|
|
25
|
+
Source: source,
|
|
26
|
+
Filename: filename
|
|
27
|
+
}.compact
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -1,20 +1,32 @@
|
|
|
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 Config
|
|
6
8
|
# Common Configuration settings. Those in this section pertain to the exception handling in Ruby, allowing for the
|
|
7
9
|
# override of Response Code and Message when Security Exceptions are raised.
|
|
8
10
|
class ExceptionConfiguration
|
|
9
11
|
include Contrast::Config::BaseConfiguration
|
|
12
|
+
include Contrast::Components::ComponentBase
|
|
13
|
+
|
|
14
|
+
CANON_NAME = 'agent.ruby.exception'
|
|
15
|
+
CONFIG_VALUES = %w[capture override_status override_message].cs__freeze
|
|
10
16
|
|
|
11
17
|
# @return [Integer] the HTTP status code override
|
|
12
18
|
attr_accessor :override_status
|
|
13
19
|
# @return [String] the message text override
|
|
14
20
|
attr_accessor :override_message
|
|
15
21
|
attr_writer :capture
|
|
22
|
+
# @return [String]
|
|
23
|
+
attr_reader :canon_name
|
|
24
|
+
# @return [Array]
|
|
25
|
+
attr_reader :config_values
|
|
16
26
|
|
|
17
27
|
def initialize hsh = {}
|
|
28
|
+
@config_values = CONFIG_VALUES
|
|
29
|
+
@canon_name = CANON_NAME
|
|
18
30
|
return unless hsh
|
|
19
31
|
|
|
20
32
|
@capture = hsh[:capture]
|
|
@@ -11,13 +11,13 @@ module Contrast
|
|
|
11
11
|
|
|
12
12
|
attr_accessor :disabled_rules
|
|
13
13
|
attr_reader :bot_blocker,
|
|
14
|
-
:cmd_injection, :
|
|
14
|
+
:cmd_injection, :cmd_injection_command_backdoors,
|
|
15
15
|
:cmd_injection_semantic_chained_commands, :cmd_injection_semantic_dangerous_paths,
|
|
16
16
|
:method_tampering,
|
|
17
17
|
:nosql_injection,
|
|
18
18
|
:path_traversal, :path_traversal_semantic_file_security_bypass,
|
|
19
19
|
:reflected_xss,
|
|
20
|
-
:sql_injection, :
|
|
20
|
+
:sql_injection, :sql_injection_semantic_dangerous_functions,
|
|
21
21
|
:unsafe_file_upload,
|
|
22
22
|
:untrusted_deserialization,
|
|
23
23
|
:xxe,
|
|
@@ -28,15 +28,16 @@ module Contrast
|
|
|
28
28
|
def initialize hsh = {} # rubocop:disable Metrics/AbcSize
|
|
29
29
|
return unless hsh
|
|
30
30
|
|
|
31
|
+
# IVs must be with the same name as rule_id
|
|
31
32
|
@disabled_rules = hsh[:disabled_rules]
|
|
32
33
|
@rule_base = Contrast::Config::ProtectRuleConfiguration.new(hsh[BASE_RULE.to_sym])
|
|
33
34
|
@bot_blocker = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'bot-blocker'])
|
|
34
35
|
@cmd_injection = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'cmd-injection'])
|
|
35
|
-
@
|
|
36
|
+
@cmd_injection_command_backdoors =
|
|
36
37
|
Contrast::Config::ProtectRuleConfiguration.new(hsh[:'cmd-injection-command-backdoors'])
|
|
37
|
-
@
|
|
38
|
+
@cmd_injection_semantic_chained_commands =
|
|
38
39
|
Contrast::Config::ProtectRuleConfiguration.new(hsh[:'cmd-injection-semantic-chained-commands'])
|
|
39
|
-
@
|
|
40
|
+
@cmd_injection_semantic_dangerous_paths =
|
|
40
41
|
Contrast::Config::ProtectRuleConfiguration.new(hsh[:'cmd-injection-semantic-dangerous-paths'])
|
|
41
42
|
@method_tampering = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'method-tampering'])
|
|
42
43
|
@nosql_injection = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'nosql-injection'])
|
|
@@ -45,11 +46,11 @@ module Contrast
|
|
|
45
46
|
Contrast::Config::ProtectRuleConfiguration.new(hsh[:'path-traversal-semantic-file-security-bypass'])
|
|
46
47
|
@reflected_xss = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'reflected-xss'])
|
|
47
48
|
@sql_injection = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'sql-injection'])
|
|
48
|
-
@
|
|
49
|
+
@sql_injection_semantic_dangerous_functions =
|
|
49
50
|
Contrast::Config::ProtectRuleConfiguration.new(hsh[:'sql-injection-semantic-dangerous-functions'])
|
|
50
51
|
@unsafe_file_upload = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'unsafe-file-upload'])
|
|
51
52
|
@untrusted_deserialization = Contrast::Config::ProtectRuleConfiguration.new(hsh[:'untrusted-deserialization'])
|
|
52
|
-
@xxe = Contrast::Config::ProtectRuleConfiguration.new(hsh[
|
|
53
|
+
@xxe = Contrast::Config::ProtectRuleConfiguration.new(hsh['xxe'])
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def []= key, value
|
|
@@ -1,13 +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/base'
|
|
5
|
+
|
|
4
6
|
module Contrast
|
|
5
7
|
module Config
|
|
6
8
|
# This class holds the Common Settings for the hidden functionality of the TS
|
|
7
9
|
class RequestAuditConfiguration
|
|
8
10
|
include Contrast::Config::BaseConfiguration
|
|
11
|
+
include Contrast::Components::ComponentBase
|
|
9
12
|
|
|
10
13
|
DEFAULT_PATH = './messages'
|
|
14
|
+
CANON_NAME = 'api.request_audit'
|
|
15
|
+
CONFIG_VALUES = %w[enable requests responses path].cs__freeze
|
|
11
16
|
|
|
12
17
|
attr_writer :enable, :requests, :responses, :path
|
|
13
18
|
|
|
@@ -39,6 +44,14 @@ module Contrast
|
|
|
39
44
|
def path
|
|
40
45
|
@path.nil? ? DEFAULT_PATH : @path
|
|
41
46
|
end
|
|
47
|
+
|
|
48
|
+
# Converts current configuration to effective config values class and appends them to
|
|
49
|
+
# EffectiveConfig class.
|
|
50
|
+
#
|
|
51
|
+
# @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
|
|
52
|
+
def to_effective_config effective_config
|
|
53
|
+
add_effective_config_values(effective_config, CONFIG_VALUES, CANON_NAME, "#{ CONTRAST }.#{ CANON_NAME }")
|
|
54
|
+
end
|
|
42
55
|
end
|
|
43
56
|
end
|
|
44
57
|
end
|