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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/assess/policy/trigger_method.rb +1 -1
  3. data/lib/contrast/agent/assess/property/evented.rb +11 -11
  4. data/lib/contrast/agent/assess.rb +0 -1
  5. data/lib/contrast/agent/excluder.rb +1 -1
  6. data/lib/contrast/agent/middleware.rb +8 -2
  7. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +116 -0
  8. data/lib/contrast/agent/protect/rule/base.rb +2 -2
  9. data/lib/contrast/agent/protect/rule/bot_blocker.rb +1 -1
  10. data/lib/contrast/agent/protect/rule/cmd_injection.rb +8 -7
  11. data/lib/contrast/agent/protect/rule/cmdi/cmdi_chained_command.rb +0 -5
  12. data/lib/contrast/agent/protect/rule/cmdi/cmdi_dangerous_path.rb +0 -5
  13. data/lib/contrast/agent/protect/rule/path_traversal.rb +4 -3
  14. data/lib/contrast/agent/protect/rule/sqli.rb +4 -3
  15. data/lib/contrast/agent/protect/rule/xss.rb +1 -0
  16. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +1 -1
  17. data/lib/contrast/agent/reporting/report.rb +1 -0
  18. data/lib/contrast/agent/reporting/reporter.rb +34 -0
  19. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +3 -9
  20. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +1 -1
  21. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +12 -7
  22. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -1
  23. data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
  24. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +239 -93
  25. data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +10 -23
  26. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +10 -9
  27. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +12 -0
  28. data/lib/contrast/agent/reporting/reporting_events/server_reporting_event.rb +8 -0
  29. data/lib/contrast/agent/reporting/reporting_events/server_settings.rb +40 -0
  30. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +6 -0
  31. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +43 -1
  32. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -4
  33. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +58 -4
  34. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +4 -3
  35. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +76 -16
  36. data/lib/contrast/agent/reporting/server_settings_worker.rb +44 -0
  37. data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +14 -2
  38. data/lib/contrast/agent/reporting/settings/helpers.rb +7 -0
  39. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +39 -2
  40. data/lib/contrast/agent/reporting/settings/rule_definition.rb +3 -0
  41. data/lib/contrast/agent/reporting/settings/security_logger.rb +77 -0
  42. data/lib/contrast/agent/reporting/settings/server_features.rb +9 -0
  43. data/lib/contrast/agent/reporting/settings/syslog.rb +34 -5
  44. data/lib/contrast/agent/request.rb +1 -0
  45. data/lib/contrast/agent/request_handler.rb +5 -10
  46. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +1 -1
  47. data/lib/contrast/agent/thread_watcher.rb +35 -1
  48. data/lib/contrast/agent/version.rb +1 -1
  49. data/lib/contrast/agent.rb +6 -0
  50. data/lib/contrast/api/communication/connection_status.rb +15 -0
  51. data/lib/contrast/components/agent.rb +34 -0
  52. data/lib/contrast/components/api.rb +23 -0
  53. data/lib/contrast/components/app_context.rb +23 -3
  54. data/lib/contrast/components/assess.rb +34 -4
  55. data/lib/contrast/components/assess_rules.rb +18 -0
  56. data/lib/contrast/components/base.rb +40 -0
  57. data/lib/contrast/components/config/sources.rb +95 -0
  58. data/lib/contrast/components/config.rb +18 -1
  59. data/lib/contrast/components/heap_dump.rb +10 -0
  60. data/lib/contrast/components/inventory.rb +15 -2
  61. data/lib/contrast/components/logger.rb +18 -0
  62. data/lib/contrast/components/polling.rb +36 -0
  63. data/lib/contrast/components/protect.rb +48 -1
  64. data/lib/contrast/components/ruby_component.rb +15 -0
  65. data/lib/contrast/components/sampling.rb +70 -13
  66. data/lib/contrast/components/security_logger.rb +13 -0
  67. data/lib/contrast/components/settings.rb +74 -7
  68. data/lib/contrast/config/certification_configuration.rb +14 -0
  69. data/lib/contrast/config/config.rb +46 -0
  70. data/lib/contrast/config/diagnostics.rb +114 -0
  71. data/lib/contrast/config/diagnostics_tools.rb +98 -0
  72. data/lib/contrast/config/effective_config.rb +65 -0
  73. data/lib/contrast/config/effective_config_value.rb +32 -0
  74. data/lib/contrast/config/exception_configuration.rb +12 -0
  75. data/lib/contrast/config/protect_rule_configuration.rb +1 -1
  76. data/lib/contrast/config/protect_rules_configuration.rb +8 -7
  77. data/lib/contrast/config/request_audit_configuration.rb +13 -0
  78. data/lib/contrast/config/server_configuration.rb +41 -2
  79. data/lib/contrast/configuration.rb +28 -2
  80. data/lib/contrast/extension/assess/erb.rb +1 -1
  81. data/lib/contrast/utils/assess/event_limit_utils.rb +31 -9
  82. data/lib/contrast/utils/assess/trigger_method_utils.rb +5 -4
  83. data/lib/contrast/utils/hash_digest.rb +2 -2
  84. data/lib/contrast/utils/input_classification_base.rb +1 -2
  85. data/lib/contrast/utils/reporting/application_activity_batch_utils.rb +81 -0
  86. data/lib/contrast/utils/routes_sent.rb +60 -0
  87. data/lib/contrast/utils/telemetry_client.rb +1 -2
  88. data/lib/contrast/utils/timer.rb +16 -0
  89. data/lib/contrast.rb +3 -1
  90. data/ruby-agent.gemspec +5 -1
  91. metadata +29 -20
  92. data/lib/contrast/agent/assess/contrast_event.rb +0 -157
  93. data/lib/contrast/agent/assess/events/event_factory.rb +0 -34
  94. data/lib/contrast/agent/assess/events/source_event.rb +0 -46
  95. 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
- @protect_state.enabled = server_features.protect.enabled?
88
- @assess_state.enabled = server_features.assess.enabled?
89
- @assess_state.sampling_settings = server_features.assess.sampling
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
- Contrast::Agent::Protect::Rule::Sqli.new
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]
@@ -40,7 +40,7 @@ module Contrast
40
40
  def applicable_mode
41
41
  return unless mode
42
42
 
43
- case mode
43
+ case mode.downcase
44
44
  when 'permit'
45
45
  :PERMIT
46
46
  when 'block_at_perimeter'
@@ -11,13 +11,13 @@ module Contrast
11
11
 
12
12
  attr_accessor :disabled_rules
13
13
  attr_reader :bot_blocker,
14
- :cmd_injection, :cmdi_command_backdoors,
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, :sqli_dangerous_function,
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
- @cmdi_command_backdoors =
36
+ @cmd_injection_command_backdoors =
36
37
  Contrast::Config::ProtectRuleConfiguration.new(hsh[:'cmd-injection-command-backdoors'])
37
- @cmdi_chained_command =
38
+ @cmd_injection_semantic_chained_commands =
38
39
  Contrast::Config::ProtectRuleConfiguration.new(hsh[:'cmd-injection-semantic-chained-commands'])
39
- @cmdi_dangerous_path =
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
- @sqli_dangerous_function =
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[:xxe])
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