contrast-agent 7.3.1 → 7.4.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/ext/cs__scope/cs__scope.c +76 -7
- data/ext/cs__scope/cs__scope.h +4 -0
- data/lib/contrast/agent/inventory/policy/datastores.rb +0 -3
- data/lib/contrast/agent/protect/rule/base.rb +5 -1
- data/lib/contrast/agent/protect/rule/cmdi/cmd_injection.rb +17 -5
- data/lib/contrast/agent/protect/rule/input_classification/base.rb +7 -2
- data/lib/contrast/agent/protect/rule/input_classification/encoding.rb +1 -1
- data/lib/contrast/agent/protect/rule/path_traversal/path_traversal.rb +8 -1
- data/lib/contrast/agent/protect/rule/sqli/sqli.rb +8 -1
- data/lib/contrast/agent/protect/state.rb +110 -0
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +4 -10
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +11 -12
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +6 -29
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +2 -0
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +1 -0
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +4 -0
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +4 -2
- data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +2 -0
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +9 -5
- data/lib/contrast/agent/reporting/reporting_events/reportable_hash.rb +30 -6
- data/lib/contrast/agent/reporting/reporting_utilities/ng_response_extractor.rb +15 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/resend.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response.rb +0 -2
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +4 -5
- data/lib/contrast/agent/reporting/settings/protect.rb +61 -18
- data/lib/contrast/agent/reporting/settings/sampling.rb +5 -4
- data/lib/contrast/agent/reporting/settings/server_features.rb +2 -0
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/components/agent.rb +3 -5
- data/lib/contrast/components/api.rb +3 -3
- data/lib/contrast/components/assess_rules.rb +1 -2
- data/lib/contrast/components/base.rb +1 -2
- data/lib/contrast/components/config/sources.rb +23 -0
- data/lib/contrast/components/logger.rb +19 -0
- data/lib/contrast/components/protect.rb +69 -15
- data/lib/contrast/components/sampling.rb +5 -12
- data/lib/contrast/components/security_logger.rb +17 -0
- data/lib/contrast/components/settings.rb +114 -70
- data/lib/contrast/config/certification_configuration.rb +1 -1
- data/lib/contrast/config/configuration_files.rb +0 -2
- data/lib/contrast/config/diagnostics/config.rb +3 -3
- data/lib/contrast/config/diagnostics/effective_config.rb +1 -1
- data/lib/contrast/config/diagnostics/environment_variables.rb +21 -11
- data/lib/contrast/config/diagnostics/monitor.rb +1 -1
- data/lib/contrast/config/diagnostics/singleton_tools.rb +170 -0
- data/lib/contrast/config/diagnostics/source_config_value.rb +14 -9
- data/lib/contrast/config/diagnostics/tools.rb +23 -84
- data/lib/contrast/config/request_audit_configuration.rb +1 -1
- data/lib/contrast/config/server_configuration.rb +3 -15
- data/lib/contrast/configuration.rb +5 -2
- data/lib/contrast/framework/manager.rb +4 -3
- data/lib/contrast/framework/manager_extend.rb +3 -1
- data/lib/contrast/framework/rack/support.rb +11 -2
- data/lib/contrast/utils/log_utils.rb +1 -1
- data/lib/contrast/utils/reporting/application_activity_batch_utils.rb +0 -3
- data/lib/contrast/utils/request_utils.rb +1 -1
- data/lib/contrast/utils/timer.rb +1 -1
- data/lib/contrast.rb +1 -1
- metadata +4 -2
@@ -31,7 +31,7 @@ module Contrast
|
|
31
31
|
attr_reader :uri
|
32
32
|
# @return [String] the HTTP version of this request
|
33
33
|
attr_reader :version
|
34
|
-
# @return [
|
34
|
+
# @return [String]
|
35
35
|
attr_reader :ip
|
36
36
|
# @return [String] Byte representation of the body
|
37
37
|
attr_accessor :body_binary
|
@@ -40,7 +40,7 @@ module Contrast
|
|
40
40
|
|
41
41
|
class << self
|
42
42
|
# @param request [Contrast::Agent::Request]
|
43
|
-
# @return [Contrast::Agent::Reporting::FindingRequest]
|
43
|
+
# @return [Contrast::Agent::Reporting::FindingRequest, nil]
|
44
44
|
def convert request
|
45
45
|
return unless request
|
46
46
|
|
@@ -108,6 +108,8 @@ module Contrast
|
|
108
108
|
raise(ArgumentError, "#{ self } did not have a proper method. Unable to continue.")
|
109
109
|
end
|
110
110
|
raise(ArgumentError, "#{ self } did not have a proper uri. Unable to continue.") unless uri && !uri.empty?
|
111
|
+
|
112
|
+
nil
|
111
113
|
end
|
112
114
|
|
113
115
|
# @param request [Contrast::Agent::Request]
|
@@ -38,14 +38,15 @@ module Contrast
|
|
38
38
|
def to_controlled_hash
|
39
39
|
validate
|
40
40
|
{
|
41
|
+
appLanguage: Contrast::Utils::ObjectShare::RUBY,
|
42
|
+
appName: ::Contrast::APP_CONTEXT.name, # rubocop:disable Security/Module/Name
|
43
|
+
appPath: ::Contrast::APP_CONTEXT.name, # rubocop:disable Security/Module/Name
|
44
|
+
appVersion: ::Contrast::APP_CONTEXT.version,
|
41
45
|
code: CODE,
|
42
|
-
app_language: Contrast::Utils::ObjectShare::RUBY,
|
43
|
-
app_name: ::Contrast::APP_CONTEXT.name, # rubocop:disable Security/Module/Name
|
44
|
-
app_version: ::Contrast::APP_CONTEXT.version,
|
45
46
|
data: '',
|
46
47
|
key: 0,
|
47
|
-
|
48
|
-
|
48
|
+
session_id: ::Contrast::ASSESS.session_id,
|
49
|
+
routes: @routes.map(&:to_controlled_hash)
|
49
50
|
}
|
50
51
|
end
|
51
52
|
|
@@ -57,6 +58,9 @@ module Contrast
|
|
57
58
|
unless ::Contrast::ASSESS.session_id
|
58
59
|
raise(ArgumentError, "#{ cs__class } did not have a proper session id. Unable to continue.")
|
59
60
|
end
|
61
|
+
unless Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
|
62
|
+
raise(ArgumentError, "#{ cs__class } did not have a proper Application Name. Unable to continue.")
|
63
|
+
end
|
60
64
|
|
61
65
|
nil
|
62
66
|
end
|
@@ -28,12 +28,31 @@ module Contrast
|
|
28
28
|
#
|
29
29
|
# @return [String, nil] - JSON
|
30
30
|
def event_json
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
return unless valid?
|
32
|
+
|
33
|
+
to_controlled_hash.to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check before building the json if the event is valid.
|
37
|
+
# This will also log the error if the event is invalid.
|
38
|
+
# This will save some object creation in the reporter
|
39
|
+
# in the case of an invalid event, and will stop the
|
40
|
+
# reporting of the event.
|
41
|
+
#
|
42
|
+
# @return [Boolean]
|
43
|
+
# @raise [ArgumentError]
|
44
|
+
def valid?
|
45
|
+
@_valid ||= begin
|
46
|
+
validate
|
47
|
+
# Some deeply nested validations only happens on
|
48
|
+
# the to_controlled_hash method, so we need to
|
49
|
+
# invoke it.
|
50
|
+
to_controlled_hash
|
51
|
+
true
|
52
|
+
rescue ArgumentError => e
|
53
|
+
handle_validation_error(e)
|
54
|
+
false
|
55
|
+
end
|
37
56
|
end
|
38
57
|
|
39
58
|
private
|
@@ -41,6 +60,11 @@ module Contrast
|
|
41
60
|
def validation_error_message
|
42
61
|
"#{ cs__class } failed validation with: "
|
43
62
|
end
|
63
|
+
|
64
|
+
# @param error [ArgumentError]
|
65
|
+
def handle_validation_error error
|
66
|
+
logger.error(validation_error_message, error)
|
67
|
+
end
|
44
68
|
end
|
45
69
|
end
|
46
70
|
end
|
@@ -126,10 +126,23 @@ module Contrast
|
|
126
126
|
# @param response_data [Hash]
|
127
127
|
# @param res [Contrast::Agent::Reporting::Response]
|
128
128
|
def ng_extract_log_settings response_data, res
|
129
|
-
|
129
|
+
# agent_startup event defines the log level under features.
|
130
|
+
log_level = if response_data[:features]
|
131
|
+
response_data[:features][:logLevel]
|
132
|
+
else
|
133
|
+
response_data[:logLevel]
|
134
|
+
end
|
135
|
+
return unless log_level
|
130
136
|
|
131
137
|
res.server_features.log_level = log_level
|
132
|
-
|
138
|
+
log_file = if response_data[:features]
|
139
|
+
response_data[:features][:logFile]
|
140
|
+
else
|
141
|
+
response_data[:logFile]
|
142
|
+
end
|
143
|
+
return unless log_file
|
144
|
+
|
145
|
+
res.server_features.log_file = log_file
|
133
146
|
end
|
134
147
|
end
|
135
148
|
end
|
@@ -72,8 +72,8 @@ module Contrast
|
|
72
72
|
# @return response [Net::HTTP::Response, nil] response from TS if no response
|
73
73
|
def send_event event, connection
|
74
74
|
return unless connection
|
75
|
+
return unless event.valid?
|
75
76
|
|
76
|
-
response = nil
|
77
77
|
logger.debug('[Reporter] Preparing to send reporting event', event_class: event.cs__class)
|
78
78
|
request = build_request(event)
|
79
79
|
response = connection.request(request)
|
@@ -12,7 +12,7 @@ module Contrast
|
|
12
12
|
RESCUE_ATTEMPTS = 3
|
13
13
|
TIMEOUT = 5
|
14
14
|
RETRY_ERRORS = [
|
15
|
-
Net::OpenTimeout, Net::ReadTimeout,
|
15
|
+
Net::OpenTimeout, Net::ReadTimeout, EOFError, OpenSSL::SSL::SSLError, Resolv::ResolvError,
|
16
16
|
Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::ESHUTDOWN, Errno::EHOSTDOWN, NameError,
|
17
17
|
Errno::EHOSTUNREACH, Errno::EISCONN, Errno::ECONNABORTED, Errno::ENETRESET, Errno::ENETUNREACH, SocketError,
|
18
18
|
NameError, NoMethodError, Timeout::Error, RuntimeError
|
@@ -87,8 +87,6 @@ module Contrast
|
|
87
87
|
messages: messages,
|
88
88
|
features: server_features.nil? ? nil : server_features.to_controlled_hash,
|
89
89
|
settings: application_settings.nil? ? nil : application_settings.to_controlled_hash,
|
90
|
-
logLevel: server_features&.log_level,
|
91
|
-
logFile: server_features&.log_file,
|
92
90
|
reactions: server_features.nil? ? nil : reactions.map(&:to_controlled_hash)
|
93
91
|
}.compact
|
94
92
|
end
|
@@ -232,11 +232,10 @@ module Contrast
|
|
232
232
|
return unless ::Contrast::AGENT.enabled?
|
233
233
|
|
234
234
|
logger.trace_with_time('Rebuilding rule modes from TeamServer') do
|
235
|
-
::Contrast::
|
236
|
-
::Contrast::
|
237
|
-
|
238
|
-
|
239
|
-
logger.info('Disabled Assess Rules', rules: ::Contrast::ASSESS.disabled_rules)
|
235
|
+
::Contrast::PROTECT.state.update if ::Contrast::PROTECT.enabled?
|
236
|
+
if ::Contrast::ASSESS.enabled?
|
237
|
+
logger.info('Disabled Assess Rules', rules: ::Contrast::ASSESS.disabled_rules)
|
238
|
+
end
|
240
239
|
end
|
241
240
|
end
|
242
241
|
|
@@ -14,6 +14,13 @@ module Contrast
|
|
14
14
|
class Protect
|
15
15
|
# modes set by NG endpoints; block at perimeter needs to be check against the blockAtEntry boolean value
|
16
16
|
NG_PROTECT_RULES_MODE = %w[OFF MONITORING BLOCKING].cs__freeze
|
17
|
+
ACTIVE_PROTECT_RULES_LIST = %w[
|
18
|
+
bot-blocker cmd-injection cmd-injection-command-backdoors cmd-injection-semantic-chained-commands
|
19
|
+
cmd-injection-semantic-dangerous-paths untrusted-deserialization nosql-injection path-traversal
|
20
|
+
path-traversal-semantic-file-security-bypass sql-injection sql-injection-semantic-dangerous-functions
|
21
|
+
unsafe-file-upload reflected-xss xxe
|
22
|
+
].cs__freeze
|
23
|
+
|
17
24
|
# The settings for each protect rule for this application
|
18
25
|
#
|
19
26
|
# @return protection_rules [Array<protectRule>] protectRule: {
|
@@ -80,30 +87,66 @@ module Contrast
|
|
80
87
|
modes_by_id = {}
|
81
88
|
protection_rules.each do |rule|
|
82
89
|
setting_mode = rule[:mode] || rule['mode']
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
else
|
91
|
-
:BLOCK
|
92
|
-
end
|
93
|
-
else
|
94
|
-
:NO_ACTION
|
95
|
-
end
|
96
|
-
else
|
97
|
-
# modes set by newer settings endpoints are of [OFF MONITOR BLOCK BLOCK_AT_PERIMETER] and
|
98
|
-
# can just be cast to symbols
|
99
|
-
setting_mode.to_sym
|
100
|
-
end
|
90
|
+
# BlockAtEnrtry is only available for the protection_rules Array.
|
91
|
+
# It is used in both ng and non ng payloads. If the array is empty
|
92
|
+
# this method will short circuit at the very first line and return
|
93
|
+
# empty hash. this means that the #rules_settings_to_settings_hash
|
94
|
+
# will be used next to extract the settings.
|
95
|
+
bap = rule[:blockAtEntry] || rule['blockAtEntry']
|
96
|
+
api_mode = assign_mode(setting_mode, block_at_entry: !!bap == bap)
|
101
97
|
|
102
98
|
id = rule[:id] || rule['id']
|
103
99
|
modes_by_id[id] = api_mode
|
104
100
|
end
|
105
101
|
modes_by_id
|
106
102
|
end
|
103
|
+
|
104
|
+
# Converts settings into Agent Settings understandable hash {RULE_ID => MODE}
|
105
|
+
# Takes Hash<String, Contrast::Agent::Reporting::Settings::ProtectRule> and converts it
|
106
|
+
# to Hash<RULE_ID => MODE>
|
107
|
+
#
|
108
|
+
# @return rules [Hash<RULE_ID => MODE>, nil] Hash with rule_id as key and mode as value
|
109
|
+
def rules_settings_to_settings_hash
|
110
|
+
return {} if rule_settings.empty?
|
111
|
+
|
112
|
+
modes_by_id = {}
|
113
|
+
rule_settings.each do |rule_id, rule_mode|
|
114
|
+
next unless active_defend_rules.include?(rule_id.to_s)
|
115
|
+
|
116
|
+
modes_by_id[rule_id.to_s] = assign_mode(rule_mode.mode)
|
117
|
+
end
|
118
|
+
modes_by_id
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns list of actively used protection rules to be updated, or default list.
|
122
|
+
# This will be used to query the received settings for the ones used by the Agent.
|
123
|
+
def active_defend_rules
|
124
|
+
return ACTIVE_PROTECT_RULES_LIST unless defined?(Contrast::PROTECT)
|
125
|
+
|
126
|
+
current_rules = Contrast::PROTECT.defend_rules.keys
|
127
|
+
return current_rules unless current_rules.empty?
|
128
|
+
|
129
|
+
ACTIVE_PROTECT_RULES_LIST
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Assigns the received settings mode to be used as actual config.
|
135
|
+
# @param setting_mode []
|
136
|
+
def assign_mode setting_mode, block_at_entry: false
|
137
|
+
# modes set by newer settings endpoints are of [OFF MONITOR BLOCK BLOCK_AT_PERIMETER] and
|
138
|
+
# can just be cast to symbols
|
139
|
+
return setting_mode.to_sym unless NG_PROTECT_RULES_MODE.include?(setting_mode)
|
140
|
+
|
141
|
+
case setting_mode
|
142
|
+
when NG_PROTECT_RULES_MODE[1]
|
143
|
+
:MONITOR
|
144
|
+
when NG_PROTECT_RULES_MODE[2]
|
145
|
+
block_at_entry ? :BLOCK_AT_PERIMETER : :BLOCK
|
146
|
+
else
|
147
|
+
:NO_ACTION
|
148
|
+
end
|
149
|
+
end
|
107
150
|
end
|
108
151
|
end
|
109
152
|
end
|
@@ -24,10 +24,11 @@ module Contrast
|
|
24
24
|
|
25
25
|
def initialize hsh
|
26
26
|
@baseline = hsh[:baseline]
|
27
|
-
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
27
|
+
# NG endpoints vs non-ng
|
28
|
+
@enabled = hsh[:enabled] || hsh[:enable]
|
29
|
+
@request_frequency = hsh[:frequency] || hsh[:request_frequency]
|
30
|
+
@response_frequency = hsh[:responseFrequency] || hsh[:response_frequency]
|
31
|
+
@window_ms = hsh[:window] || hsh[:window_ms]
|
31
32
|
end
|
32
33
|
|
33
34
|
def to_controlled_hash
|
@@ -85,6 +85,8 @@ module Contrast
|
|
85
85
|
security_logger: security_logger.settings_blank? ? nil : security_logger.to_controlled_hash,
|
86
86
|
assessment: @_assess ? assess.to_controlled_hash : {},
|
87
87
|
defend: @_protect ? protect.to_controlled_hash : {},
|
88
|
+
logLevel: log_level,
|
89
|
+
logFile: log_file,
|
88
90
|
telemetry: telemetry
|
89
91
|
}.compact
|
90
92
|
end
|
@@ -26,6 +26,8 @@ module Contrast
|
|
26
26
|
attr_reader :config_values
|
27
27
|
# @return [Boolean]
|
28
28
|
attr_accessor :enable
|
29
|
+
# @return [Boolean]
|
30
|
+
attr_accessor :omit_body
|
29
31
|
|
30
32
|
CANON_NAME = 'agent'
|
31
33
|
CONFIG_VALUES = %w[enabled? omit_body?].cs__freeze
|
@@ -95,16 +97,12 @@ module Contrast
|
|
95
97
|
@_ruleset ||= Contrast::Agent::RuleSet.new(retrieve_protect_ruleset.values)
|
96
98
|
end
|
97
99
|
|
98
|
-
def reset_ruleset
|
99
|
-
@_ruleset = nil
|
100
|
-
end
|
101
|
-
|
102
100
|
def patch_yield?
|
103
101
|
!false?(ruby.propagate_yield)
|
104
102
|
end
|
105
103
|
|
106
104
|
def omit_body?
|
107
|
-
@_omit_body
|
105
|
+
true?(@_omit_body)
|
108
106
|
end
|
109
107
|
|
110
108
|
def disable_agent!
|
@@ -129,7 +129,7 @@ module Contrast
|
|
129
129
|
#
|
130
130
|
# @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
|
131
131
|
def to_effective_config effective_config
|
132
|
-
add_effective_config_values(effective_config, CONFIG_VALUES, CANON_NAME,
|
132
|
+
add_effective_config_values(effective_config, CONFIG_VALUES, CANON_NAME, CANON_NAME)
|
133
133
|
effective_proxy(effective_config)
|
134
134
|
request_audit&.to_effective_config(effective_config)
|
135
135
|
certificate&.to_effective_config(effective_config)
|
@@ -139,10 +139,10 @@ module Contrast
|
|
139
139
|
|
140
140
|
# @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
|
141
141
|
def effective_proxy effective_config
|
142
|
-
add_single_effective_value(effective_config, ENABLE, proxy_enable.to_s, PROXY_NAME
|
142
|
+
add_single_effective_value(effective_config, ENABLE, proxy_enable.to_s, PROXY_NAME)
|
143
143
|
return unless proxy_url && proxy_enable
|
144
144
|
|
145
|
-
add_single_effective_value(effective_config, 'url', proxy_url, PROXY_NAME
|
145
|
+
add_single_effective_value(effective_config, 'url', proxy_url, PROXY_NAME)
|
146
146
|
end
|
147
147
|
|
148
148
|
def certification_truly_enabled? config_path
|
@@ -16,7 +16,6 @@ module Contrast
|
|
16
16
|
|
17
17
|
SPEC_KEY = :disabled_rules.cs__freeze
|
18
18
|
CANON_NAME = 'assess.rules'
|
19
|
-
NAME_PREFIX = "#{ CONTRAST }.#{ CANON_NAME }".cs__freeze
|
20
19
|
|
21
20
|
# @return [Array, nil] list of disabled assess rules
|
22
21
|
attr_accessor :disabled_rules
|
@@ -36,7 +35,7 @@ module Contrast
|
|
36
35
|
#
|
37
36
|
# @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
|
38
37
|
def to_effective_config effective_config
|
39
|
-
add_single_effective_value(effective_config, SPEC_KEY.to_s, disabled_rules, canon_name
|
38
|
+
add_single_effective_value(effective_config, SPEC_KEY.to_s, disabled_rules || [], canon_name)
|
40
39
|
end
|
41
40
|
|
42
41
|
private
|
@@ -12,7 +12,6 @@ module Contrast
|
|
12
12
|
module ComponentBase
|
13
13
|
include Contrast::Config::Diagnostics::Tools
|
14
14
|
|
15
|
-
CONTRAST = 'contrast'
|
16
15
|
ENABLE = 'enable'
|
17
16
|
|
18
17
|
# Used for config diagnostics. Override per rule.
|
@@ -87,7 +86,7 @@ module Contrast
|
|
87
86
|
#
|
88
87
|
# @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
|
89
88
|
def to_effective_config effective_config
|
90
|
-
add_effective_config_values(effective_config, config_values, canon_name
|
89
|
+
add_effective_config_values(effective_config, config_values, canon_name)
|
91
90
|
end
|
92
91
|
|
93
92
|
# attempts to stringify the config value if it is an array with the join char
|
@@ -11,6 +11,8 @@ module Contrast
|
|
11
11
|
# This component encapsulates storing the source for each entry in the config,
|
12
12
|
# so that we can report on where the value was set from.
|
13
13
|
class Sources
|
14
|
+
# [ CORPORATE_RULE, COMMAND_LINE, JAVA_SYSTEM_PROPERTY, ENVIRONMENT_VARIABLE, APP_CONFIGURATION_FILE,
|
15
|
+
# USER_CONFIGURATION_FILE, CONTRAST_UI, DEFAULT_VALUE ]
|
14
16
|
ENVIRONMENT_VARIABLE = 'ENVIRONMENT_VARIABLE'
|
15
17
|
COMMAND_LINE = 'COMMAND_LINE'
|
16
18
|
CONTRAST_UI = 'CONTRAST_UI'
|
@@ -57,6 +59,27 @@ module Contrast
|
|
57
59
|
deep_select(data.dup, type, [])
|
58
60
|
end
|
59
61
|
|
62
|
+
# @param path [String] the canonical name for the config entry (such as api.proxy.enable) or source
|
63
|
+
# if the source(CONTRAST_UI, ENVIRONMENT_VARIABLE...) flag is set true.
|
64
|
+
# @param source [Boolean] flag to specify we are passing in a source, no need to look for it.
|
65
|
+
# @return [Boolean] true if the entry is from a YAML file
|
66
|
+
def configuration_file_source? path, source: false
|
67
|
+
s = source ? path : Contrast::CONFIG.sources.get(path)
|
68
|
+
return true if s.include?(APP_CONFIGURATION_EXTENSIONS[0]) || s.include?(APP_CONFIGURATION_EXTENSIONS[1])
|
69
|
+
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check to see whether the source has been overridden by local settings.
|
74
|
+
# @param path [String] the canonical name for the config entry (such as api.proxy.enable)
|
75
|
+
def source_overridden? path
|
76
|
+
source = Contrast::CONFIG.sources.get(path)
|
77
|
+
return true if [ENVIRONMENT_VARIABLE, COMMAND_LINE].include?(source)
|
78
|
+
return true if configuration_file_source?(source, source: true)
|
79
|
+
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
60
83
|
private
|
61
84
|
|
62
85
|
# @param path [String] the canonical name for a config entry (such as api.proxy.enable)
|
@@ -33,6 +33,8 @@ module Contrast
|
|
33
33
|
include InstanceMethods
|
34
34
|
include Contrast::Components::ComponentBase
|
35
35
|
|
36
|
+
DEFAULT_NAME = 'contrast.log'
|
37
|
+
DEFAULT_LEVEL = 'INFO'
|
36
38
|
CANON_NAME = 'agent.logger'
|
37
39
|
CONFIG_VALUES = %w[path level progname].cs__freeze
|
38
40
|
|
@@ -56,6 +58,23 @@ module Contrast
|
|
56
58
|
@level = hsh[:level]
|
57
59
|
@progname = hsh[:progname]
|
58
60
|
end
|
61
|
+
|
62
|
+
def to_effective_config effective_config
|
63
|
+
path_setting = nil
|
64
|
+
level_setting = nil
|
65
|
+
|
66
|
+
if defined?(Contrast::SETTINGS)
|
67
|
+
path_setting = Contrast::SETTINGS.agent_state.logger_path
|
68
|
+
level_setting = Contrast::SETTINGS.agent_state.logger_level
|
69
|
+
end
|
70
|
+
|
71
|
+
path_setting ||= DEFAULT_NAME
|
72
|
+
level_setting ||= DEFAULT_LEVEL
|
73
|
+
|
74
|
+
add_single_effective_value(effective_config, config_values[0], path || path_setting, CANON_NAME)
|
75
|
+
add_single_effective_value(effective_config, config_values[1], level || level_setting, CANON_NAME)
|
76
|
+
add_single_effective_value(effective_config, config_values[2], progname, CANON_NAME)
|
77
|
+
end
|
59
78
|
end
|
60
79
|
end
|
61
80
|
end
|
@@ -4,13 +4,15 @@
|
|
4
4
|
require 'contrast/components/base'
|
5
5
|
require 'contrast/config/exception_configuration'
|
6
6
|
require 'contrast/config/protect_rule_configuration'
|
7
|
+
require 'contrast/utils/object_share'
|
8
|
+
require 'contrast/agent/protect/state'
|
7
9
|
|
8
10
|
module Contrast
|
9
11
|
module Components
|
10
12
|
module Protect
|
11
13
|
# A wrapper build around the Common Agent Configuration project to allow for access of the values contained in
|
12
14
|
# its parent_configuration_spec.yaml. Specifically, this allows for querying the state of the Protect product.
|
13
|
-
class Interface
|
15
|
+
class Interface # rubocop:disable Metrics/ClassLength
|
14
16
|
include Contrast::Components::ComponentBase
|
15
17
|
include Contrast::Config::BaseConfiguration
|
16
18
|
|
@@ -68,7 +70,7 @@ module Contrast
|
|
68
70
|
return false if forcibly_disabled?
|
69
71
|
return true if forcibly_enabled?
|
70
72
|
|
71
|
-
|
73
|
+
state.enabled?
|
72
74
|
end
|
73
75
|
|
74
76
|
# Check to determine if the base64 decoding is required for user inputs.
|
@@ -85,12 +87,19 @@ module Contrast
|
|
85
87
|
::Contrast::CONFIG.protect.rules
|
86
88
|
end
|
87
89
|
|
90
|
+
# Current Active Protect rules and the state/mode they are in.
|
91
|
+
#
|
92
|
+
# @return [Contrast::Agent::Protect::State]
|
93
|
+
def state
|
94
|
+
@_state ||= Contrast::Agent::Protect::State.new
|
95
|
+
end
|
96
|
+
|
88
97
|
# Returns Protect array of all initialized
|
89
98
|
# protect rules.
|
90
99
|
#
|
91
100
|
# @return defend_rules[Hash<Contrast::SETTINGS.protect_state.rules>]
|
92
101
|
def defend_rules
|
93
|
-
|
102
|
+
state.rules
|
94
103
|
end
|
95
104
|
|
96
105
|
# The Contrast::CONFIG.protect.rules is object so we need to check it's
|
@@ -102,16 +111,27 @@ module Contrast
|
|
102
111
|
# @return mode [Symbol]
|
103
112
|
def rule_mode rule_id
|
104
113
|
str = rule_id.tr('-', '_')
|
105
|
-
|
106
|
-
|
107
|
-
|
114
|
+
config_mode = Contrast::CONFIG.protect.rules[str]&.applicable_mode
|
115
|
+
settings_mode = ::Contrast::SETTINGS.application_state.modes_by_id[rule_id]
|
116
|
+
|
117
|
+
if config_mode
|
118
|
+
update_config_for_rule(rule_id, config_mode)
|
119
|
+
return config_mode
|
120
|
+
end
|
121
|
+
|
122
|
+
if settings_mode
|
123
|
+
update_config_for_rule(rule_id, settings_mode, ui_source: true)
|
124
|
+
return settings_mode
|
125
|
+
end
|
126
|
+
:NO_ACTION
|
108
127
|
end
|
109
128
|
|
110
129
|
# Name of the protect rule
|
111
130
|
#
|
112
|
-
# @
|
131
|
+
# @param name [String]
|
132
|
+
# @return [Contrast::Agent::Protect::Rule::Base]
|
113
133
|
def rule name
|
114
|
-
|
134
|
+
state.rules[name]
|
115
135
|
end
|
116
136
|
|
117
137
|
def report_any_command_execution?
|
@@ -124,7 +144,8 @@ module Contrast
|
|
124
144
|
|
125
145
|
def report_custom_code_sysfile_access?
|
126
146
|
if @_report_custom_code_sysfile_access.nil?
|
127
|
-
name_changed = Contrast::Agent::Protect::Rule::PathTraversal::NAME.
|
147
|
+
name_changed = Contrast::Agent::Protect::Rule::PathTraversal::NAME.
|
148
|
+
tr(Contrast::Utils::ObjectShare::DASH, Contrast::Utils::ObjectShare::UNDERSCORE)
|
128
149
|
ctrl = rule_config[name_changed]
|
129
150
|
@_report_custom_code_sysfile_access = ctrl && true?(ctrl.detect_custom_code_accessing_system_files)
|
130
151
|
end
|
@@ -150,16 +171,15 @@ module Contrast
|
|
150
171
|
|
151
172
|
# @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
|
152
173
|
def protect_rules_to_effective_config effective_config
|
153
|
-
|
154
|
-
|
155
|
-
defend_rules.each do |key, value|
|
174
|
+
state.rules.each do |key, value|
|
156
175
|
next unless key && value
|
157
176
|
|
158
177
|
config_prefix = "#{ CANON_NAME }.#{ RULES }.#{ key }"
|
159
|
-
|
160
|
-
add_single_effective_value(effective_config, ENABLE, value.enabled?, config_prefix, name_prefix)
|
178
|
+
add_single_effective_value(effective_config, ENABLE, value.enabled?, config_prefix)
|
161
179
|
# Get the mode by checking Current Configs or Settings received:
|
162
|
-
|
180
|
+
mode = rule_mode(key)
|
181
|
+
mode = :OFF if mode == :NO_ACTION
|
182
|
+
add_single_effective_value(effective_config, MODE, mode, config_prefix)
|
163
183
|
end
|
164
184
|
end
|
165
185
|
|
@@ -168,6 +188,40 @@ module Contrast
|
|
168
188
|
|
169
189
|
@_forcibly_enabled ||= true?(::Contrast::CONFIG.protect.enable)
|
170
190
|
end
|
191
|
+
|
192
|
+
# Used for conversion to contrast metrics hash:
|
193
|
+
def forcibly_enabled
|
194
|
+
forcibly_enabled?
|
195
|
+
end
|
196
|
+
|
197
|
+
def forcibly_disabled
|
198
|
+
forcibly_disabled?
|
199
|
+
end
|
200
|
+
|
201
|
+
def report_custom_code_sysfile_access
|
202
|
+
report_custom_code_sysfile_access?
|
203
|
+
end
|
204
|
+
|
205
|
+
# @param rule_id [String] the canonical name for a config entry (such as api.proxy.enable).
|
206
|
+
# @param mode [Symbol] to set the value of the config entry.
|
207
|
+
# @param ui_source [Boolean] whether to se the source as contrast ui or not.
|
208
|
+
def update_config_for_rule rule_id, mode, ui_source: false
|
209
|
+
str = rule_id.tr('-', '_').to_sym
|
210
|
+
config = Contrast::CONFIG.config.loaded_config
|
211
|
+
return unless config
|
212
|
+
|
213
|
+
config[:protect] ||= {}
|
214
|
+
config[:protect][:rules] ||= {}
|
215
|
+
config[:protect][:rules][str] ||= {}
|
216
|
+
config[:protect][:rules][str][:mode] = mode
|
217
|
+
config[:protect][:rules][str][:enable] = (mode != :NO_ACTION)
|
218
|
+
return unless ui_source
|
219
|
+
|
220
|
+
Contrast::CONFIG.sources.set("#{ CANON_NAME }.#{ RULES }.#{ str }.mode",
|
221
|
+
Contrast::Components::Config::Sources::CONTRAST_UI)
|
222
|
+
Contrast::CONFIG.sources.set("#{ CANON_NAME }.#{ RULES }.#{ str }.enable",
|
223
|
+
Contrast::Components::Config::Sources::CONTRAST_UI)
|
224
|
+
end
|
171
225
|
end
|
172
226
|
end
|
173
227
|
end
|