contrast-agent 7.3.2 → 7.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/middleware/middleware.rb +1 -1
  3. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +9 -11
  4. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +55 -20
  5. data/lib/contrast/agent/protect/policy/rule_applicator.rb +1 -4
  6. data/lib/contrast/agent/protect/rule/base.rb +61 -26
  7. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker.rb +12 -4
  8. data/lib/contrast/agent/protect/rule/cmdi/cmd_injection.rb +19 -15
  9. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +2 -4
  10. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +2 -1
  11. data/lib/contrast/agent/protect/rule/deserialization/deserialization.rb +4 -4
  12. data/lib/contrast/agent/protect/rule/input_classification/base.rb +7 -2
  13. data/lib/contrast/agent/protect/rule/input_classification/encoding.rb +1 -1
  14. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli.rb +5 -2
  15. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal.rb +20 -8
  16. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_semantic_security_bypass.rb +2 -2
  17. data/lib/contrast/agent/protect/rule/sqli/sqli.rb +8 -1
  18. data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +2 -3
  19. data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +3 -4
  20. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload.rb +3 -0
  21. data/lib/contrast/agent/protect/rule/utils/builders.rb +3 -4
  22. data/lib/contrast/agent/protect/rule/utils/filters.rb +32 -16
  23. data/lib/contrast/agent/protect/rule/xss/xss.rb +80 -0
  24. data/lib/contrast/agent/protect/rule/xxe/xxe.rb +9 -2
  25. data/lib/contrast/agent/protect/state.rb +110 -0
  26. data/lib/contrast/agent/reporting/details/xss_match.rb +17 -0
  27. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +32 -0
  28. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +2 -0
  29. data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +2 -0
  30. data/lib/contrast/agent/reporting/reporting_events/finding.rb +1 -4
  31. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +4 -0
  32. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +2 -0
  33. data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +2 -0
  34. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +9 -8
  35. data/lib/contrast/agent/reporting/reporting_events/reportable_hash.rb +30 -6
  36. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
  37. data/lib/contrast/agent/reporting/reporting_utilities/resend.rb +1 -1
  38. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -5
  39. data/lib/contrast/agent/reporting/settings/protect.rb +3 -3
  40. data/lib/contrast/agent/reporting/settings/sampling.rb +5 -4
  41. data/lib/contrast/agent/request/request_context_extend.rb +0 -2
  42. data/lib/contrast/agent/version.rb +1 -1
  43. data/lib/contrast/components/agent.rb +3 -5
  44. data/lib/contrast/components/api.rb +3 -3
  45. data/lib/contrast/components/assess.rb +4 -0
  46. data/lib/contrast/components/assess_rules.rb +1 -2
  47. data/lib/contrast/components/base.rb +1 -2
  48. data/lib/contrast/components/config/sources.rb +23 -0
  49. data/lib/contrast/components/logger.rb +19 -0
  50. data/lib/contrast/components/protect.rb +55 -14
  51. data/lib/contrast/components/sampling.rb +5 -12
  52. data/lib/contrast/components/security_logger.rb +17 -0
  53. data/lib/contrast/components/settings.rb +110 -76
  54. data/lib/contrast/config/certification_configuration.rb +1 -1
  55. data/lib/contrast/config/configuration_files.rb +0 -2
  56. data/lib/contrast/config/diagnostics/config.rb +3 -3
  57. data/lib/contrast/config/diagnostics/effective_config.rb +1 -1
  58. data/lib/contrast/config/diagnostics/environment_variables.rb +21 -11
  59. data/lib/contrast/config/diagnostics/monitor.rb +1 -1
  60. data/lib/contrast/config/diagnostics/singleton_tools.rb +170 -0
  61. data/lib/contrast/config/diagnostics/source_config_value.rb +14 -9
  62. data/lib/contrast/config/diagnostics/tools.rb +23 -84
  63. data/lib/contrast/config/request_audit_configuration.rb +1 -1
  64. data/lib/contrast/config/server_configuration.rb +3 -15
  65. data/lib/contrast/configuration.rb +5 -2
  66. data/lib/contrast/framework/manager.rb +4 -3
  67. data/lib/contrast/framework/manager_extend.rb +3 -1
  68. data/lib/contrast/framework/rack/support.rb +11 -2
  69. data/lib/contrast/framework/rails/support.rb +2 -2
  70. data/lib/contrast/logger/cef_log.rb +30 -4
  71. data/lib/contrast/utils/io_util.rb +3 -0
  72. data/lib/contrast/utils/log_utils.rb +22 -11
  73. data/lib/contrast/utils/request_utils.rb +1 -1
  74. data/lib/contrast/utils/timer.rb +1 -1
  75. metadata +4 -2
@@ -422,6 +422,8 @@ module Contrast
422
422
  raise(ArgumentError, "#{ self } did not have a proper thread. Unable to continue.") unless thread
423
423
  raise(ArgumentError, "#{ self } did not have a proper time. Unable to continue.") unless time
424
424
  raise(ArgumentError, "#{ self } did not have a proper type. Unable to continue.") unless type
425
+
426
+ nil
425
427
  end
426
428
 
427
429
  # @raise [ArgumentError]
@@ -435,6 +437,8 @@ module Contrast
435
437
  raise(ArgumentError, "#{ self } did not have a proper signature. Unable to continue.") unless signature
436
438
  raise(ArgumentError, "#{ self } did not have a proper stack. Unable to continue.") unless stack
437
439
  raise(ArgumentError, "#{ self } did not have a proper tags. Unable to continue.") unless reportable_tags
440
+
441
+ nil
438
442
  end
439
443
  end
440
444
  end
@@ -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]
@@ -30,6 +30,8 @@ module Contrast
30
30
 
31
31
  def validate
32
32
  raise(ArgumentError, "#{ self } did not have observations. Unable to continue.") if observations.empty?
33
+
34
+ nil
33
35
  end
34
36
  end
35
37
  end
@@ -38,15 +38,16 @@ 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
- routes: @routes.map(&:to_controlled_hash),
48
- session_id: ::Contrast::ASSESS.session_id
49
- }
48
+ session_id: ::Contrast::ASSESS.session_id,
49
+ routes: @routes.map(&:to_controlled_hash)
50
+ }.compact
50
51
  end
51
52
 
52
53
  # @raise [ArgumentError]
@@ -54,8 +55,8 @@ module Contrast
54
55
  unless Contrast::Utils::StringUtils.present?(data)
55
56
  raise(ArgumentError, "#{ cs__class } did not have a proper data. Unable to continue.")
56
57
  end
57
- unless ::Contrast::ASSESS.session_id
58
- raise(ArgumentError, "#{ cs__class } did not have a proper session id. Unable to continue.")
58
+ unless Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
59
+ raise(ArgumentError, "#{ cs__class } did not have a proper Application Name. Unable to continue.")
59
60
  end
60
61
 
61
62
  nil
@@ -28,12 +28,31 @@ module Contrast
28
28
  #
29
29
  # @return [String, nil] - JSON
30
30
  def event_json
31
- hsh = to_controlled_hash
32
- hsh.to_json
33
- rescue ArgumentError => e
34
- # Log the error and raise it for the client to handle:
35
- logger.error(validation_error_message, e)
36
- raise(e)
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
@@ -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, ArgumentError, EOFError, OpenSSL::SSL::SSLError, Resolv::ResolvError,
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
@@ -232,11 +232,7 @@ module Contrast
232
232
  return unless ::Contrast::AGENT.enabled?
233
233
 
234
234
  logger.trace_with_time('Rebuilding rule modes from TeamServer') do
235
- # TODO: RUBY-999999 Make sure when updating Protect rules to reflect the mode source (always DEFAULT_VALUE)
236
- ::Contrast::AGENT.reset_ruleset
237
- ::Contrast::SETTINGS.build_protect_rules if ::Contrast::PROTECT.enabled?
238
- logger.info('Current rule settings:')
239
- ::Contrast::PROTECT.defend_rules.each { |k, v| logger.info('Protect Rule mode set', rule: k, mode: v.mode) }
235
+ ::Contrast::PROTECT.state.update if ::Contrast::PROTECT.enabled?
240
236
  if ::Contrast::ASSESS.enabled?
241
237
  logger.info('Disabled Assess Rules', rules: ::Contrast::ASSESS.disabled_rules)
242
238
  end
@@ -121,10 +121,10 @@ module Contrast
121
121
  # Returns list of actively used protection rules to be updated, or default list.
122
122
  # This will be used to query the received settings for the ones used by the Agent.
123
123
  def active_defend_rules
124
- return ACTIVE_PROTECT_RULES_LIST unless defined?(Contrast::SETTINGS)
124
+ return ACTIVE_PROTECT_RULES_LIST unless defined?(Contrast::PROTECT)
125
125
 
126
- current_rules = (Contrast::PROTECT&.defend_rules&.keys if defined?(Contrast::PROTECT))
127
- return current_rules unless current_rules&.empty?
126
+ current_rules = Contrast::PROTECT.defend_rules.keys
127
+ return current_rules unless current_rules.empty?
128
128
 
129
129
  ACTIVE_PROTECT_RULES_LIST
130
130
  end
@@ -24,10 +24,11 @@ module Contrast
24
24
 
25
25
  def initialize hsh
26
26
  @baseline = hsh[:baseline]
27
- @enabled = hsh[:enabled]
28
- @request_frequency = hsh[:frequency]
29
- @response_frequency = hsh[:responseFrequency]
30
- @window_ms = hsh[:window]
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
@@ -52,8 +52,6 @@ module Contrast
52
52
  if (ia = Contrast::Agent::Protect::InputAnalyzer.analyse(request))
53
53
  # Handle prefilter
54
54
  Contrast::Agent::Protect::InputAnalyzer.input_classification(ia, prefilter: true)
55
- # Reflected xss infilter
56
- Contrast::Agent::Protect::InputAnalyzer.input_classification_for('reflected-xss', ia)
57
55
  @agent_input_analysis = ia
58
56
  else
59
57
  logger.trace('Analysis from Agent was empty.')
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '7.3.2'
6
+ VERSION = '7.4.1'
7
7
  end
8
8
  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, CONTRAST)
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, "#{ CONTRAST }.proxy")
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, "#{ CONTRAST }.proxy")
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
@@ -237,6 +237,10 @@ module Contrast
237
237
 
238
238
  # The id for this process, based on the session metadata or id provided by the user, as indicated in
239
239
  # application startup.
240
+ #
241
+ # The ID of the current application run, as returned by the application settings endpoint or set by
242
+ # application.session_id. If there is no session associated with this run, this field should be omitted
243
+ # when reporting to TS.
240
244
  def session_id
241
245
  ::Contrast::SETTINGS.assess_state.session_id
242
246
  end
@@ -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, NAME_PREFIX)
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, "#{ CONTRAST }.#{ 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,6 +4,8 @@
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
@@ -68,7 +70,7 @@ module Contrast
68
70
  return false if forcibly_disabled?
69
71
  return true if forcibly_enabled?
70
72
 
71
- ::Contrast::SETTINGS.protect_state.enabled == true
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
- ::Contrast::SETTINGS.protect_state.rules
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
- ::Contrast::CONFIG.protect.rules[str]&.applicable_mode ||
106
- ::Contrast::SETTINGS.application_state.modes_by_id[rule_id] ||
107
- :NO_ACTION
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
- # @return [String]
131
+ # @param name [String]
132
+ # @return [Contrast::Agent::Protect::Rule::Base]
113
133
  def rule name
114
- ::Contrast::SETTINGS.protect_state.rules[name]
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.tr('-', '_')
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
- return unless defend_rules
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
- name_prefix = "#{ CONTRAST }.#{ CANON_NAME }.#{ RULES }.#{ key }"
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
- add_single_effective_value(effective_config, MODE, rule_mode(key), config_prefix, name_prefix)
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
 
@@ -181,6 +201,27 @@ module Contrast
181
201
  def report_custom_code_sysfile_access
182
202
  report_custom_code_sysfile_access?
183
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
184
225
  end
185
226
  end
186
227
  end
@@ -111,7 +111,6 @@ module Contrast
111
111
  include Contrast::Config::BaseConfiguration
112
112
 
113
113
  CANON_NAME = 'assess.sampling'
114
- NAME_PREFIX = "#{ CONTRAST }.#{ CANON_NAME }".cs__freeze
115
114
  CONFIG_VALUES = %w[enable baseline request_frequency response_frequency window_ms].cs__freeze
116
115
 
117
116
  # @return [Boolean, nil]
@@ -145,19 +144,13 @@ module Contrast
145
144
  def to_effective_config effective_config
146
145
  confirm_sources
147
146
 
148
- add_single_effective_value(effective_config, 'enable', sampling_control[:enabled], canon_name, NAME_PREFIX)
149
- add_single_effective_value(effective_config, 'baseline', sampling_control[:baseline], canon_name, NAME_PREFIX)
150
- add_single_effective_value(effective_config, 'window_ms', sampling_control[:window], canon_name, NAME_PREFIX)
147
+ add_single_effective_value(effective_config, 'enable', sampling_control[:enabled], canon_name)
148
+ add_single_effective_value(effective_config, 'baseline', sampling_control[:baseline], canon_name)
149
+ add_single_effective_value(effective_config, 'window_ms', sampling_control[:window], canon_name)
151
150
  add_single_effective_value(effective_config,
152
- 'request_frequency',
153
- sampling_control[:request_frequency],
154
- canon_name,
155
- NAME_PREFIX)
151
+ 'request_frequency', sampling_control[:request_frequency], canon_name)
156
152
  add_single_effective_value(effective_config,
157
- 'response_frequency',
158
- sampling_control[:response_frequency],
159
- canon_name,
160
- NAME_PREFIX)
153
+ 'response_frequency', sampling_control[:response_frequency], canon_name)
161
154
  end
162
155
 
163
156
  private
@@ -10,6 +10,7 @@ module Contrast
10
10
  class Interface
11
11
  include Contrast::Components::ComponentBase
12
12
 
13
+ DEFAULT_CEF_NAME = 'security.log'
13
14
  CANON_NAME = 'agent.security_logger'
14
15
  CONFIG_VALUES = %w[path level].cs__freeze
15
16
 
@@ -30,6 +31,22 @@ module Contrast
30
31
  @path = hsh[:path]
31
32
  @level = hsh[:level]
32
33
  end
34
+
35
+ def to_effective_config effective_config
36
+ path_setting = nil
37
+ level_setting = nil
38
+
39
+ if defined?(Contrast::SETTINGS)
40
+ path_setting = Contrast::SETTINGS.agent_state.cef_logger_path
41
+ level_setting = Contrast::SETTINGS.agent_state.cef_logger_level
42
+ end
43
+
44
+ path_setting ||= DEFAULT_CEF_NAME
45
+ level_setting ||= Contrast::CONFIG.agent.logger.level
46
+
47
+ add_single_effective_value(effective_config, config_values[0], path || path_setting, CANON_NAME)
48
+ add_single_effective_value(effective_config, config_values[1], level || level_setting, CANON_NAME)
49
+ end
33
50
  end
34
51
  end
35
52
  end