contrast-agent 7.0.0 → 7.1.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/assess/policy/policy.rb +1 -1
  3. data/lib/contrast/agent/deadzone/policy/policy.rb +1 -1
  4. data/lib/contrast/agent/patching/policy/policy.rb +2 -2
  5. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +3 -0
  6. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli.rb +1 -1
  7. data/lib/contrast/agent/reporting/reporter.rb +19 -4
  8. data/lib/contrast/agent/reporting/reporting_events/agent_effective_config.rb +32 -0
  9. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +7 -0
  10. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +3 -1
  11. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +11 -7
  12. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +15 -7
  13. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +2 -1
  14. data/lib/contrast/agent/reporting/reporting_workers/application_server_worker.rb +3 -0
  15. data/lib/contrast/agent/reporting/reporting_workers/reporter_heartbeat.rb +3 -0
  16. data/lib/contrast/agent/reporting/reporting_workers/server_settings_worker.rb +3 -0
  17. data/lib/contrast/agent/telemetry/base.rb +37 -12
  18. data/lib/contrast/agent/telemetry/client.rb +1 -3
  19. data/lib/contrast/agent/telemetry/telemetry.rb +0 -7
  20. data/lib/contrast/agent/thread/thread_watcher.rb +2 -2
  21. data/lib/contrast/agent/version.rb +1 -1
  22. data/lib/contrast/components/agent.rb +1 -1
  23. data/lib/contrast/components/api.rb +2 -2
  24. data/lib/contrast/components/app_context.rb +1 -1
  25. data/lib/contrast/components/assess.rb +1 -1
  26. data/lib/contrast/components/assess_rules.rb +1 -1
  27. data/lib/contrast/components/base.rb +3 -3
  28. data/lib/contrast/components/config/sources.rb +12 -9
  29. data/lib/contrast/components/config.rb +2 -2
  30. data/lib/contrast/components/protect.rb +2 -2
  31. data/lib/contrast/components/sampling.rb +6 -4
  32. data/lib/contrast/components/settings.rb +1 -1
  33. data/lib/contrast/config/certification_configuration.rb +1 -1
  34. data/lib/contrast/config/configuration_files.rb +47 -0
  35. data/lib/contrast/config/diagnostics/command_line.rb +24 -0
  36. data/lib/contrast/config/{config.rb → diagnostics/config.rb} +21 -6
  37. data/lib/contrast/config/diagnostics/contrast_ui.rb +24 -0
  38. data/lib/contrast/config/diagnostics/effective_config.rb +28 -0
  39. data/lib/contrast/config/diagnostics/effective_config_value.rb +14 -0
  40. data/lib/contrast/config/diagnostics/environment_variables.rb +51 -0
  41. data/lib/contrast/config/{diagnostics.rb → diagnostics/monitor.rb} +10 -10
  42. data/lib/contrast/config/diagnostics/source_config_value.rb +51 -0
  43. data/lib/contrast/config/diagnostics/tools.rb +188 -0
  44. data/lib/contrast/config/diagnostics/user_configuration_file.rb +44 -0
  45. data/lib/contrast/config/request_audit_configuration.rb +1 -1
  46. data/lib/contrast/config/server_configuration.rb +1 -1
  47. data/lib/contrast/configuration.rb +90 -57
  48. data/lib/contrast/utils/hash_utils.rb +43 -0
  49. data/lib/contrast/utils/json.rb +46 -0
  50. data/lib/contrast/utils/net_http_base.rb +75 -26
  51. metadata +16 -7
  52. data/lib/contrast/config/diagnostics_tools.rb +0 -99
  53. data/lib/contrast/config/effective_config.rb +0 -131
  54. data/lib/contrast/config/effective_config_value.rb +0 -32
@@ -4,7 +4,7 @@
4
4
  require 'contrast/utils/env_configuration_item'
5
5
  require 'ougai'
6
6
  require 'contrast/configuration'
7
- require 'contrast/config/diagnostics'
7
+ require 'contrast/config/diagnostics/monitor'
8
8
 
9
9
  module Contrast
10
10
  module Components
@@ -249,7 +249,7 @@ module Contrast
249
249
  return unless current_level.nil? == false && current_level.cs__respond_to?(dot_path_array[-1])
250
250
 
251
251
  current_level.send("#{ dot_path_array[-1] }=", value)
252
- sources.set(dot_path_array.join('.'), Contrast::Components::Config::Sources::ENVIRONMENT)
252
+ sources.set(dot_path_array.join('.'), Contrast::Components::Config::Sources::ENVIRONMENT_VARIABLE)
253
253
  end
254
254
  end
255
255
  end
@@ -130,7 +130,7 @@ module Contrast
130
130
  # Converts current configuration to effective config values class and appends them to
131
131
  # EffectiveConfig class.
132
132
  #
133
- # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
133
+ # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
134
134
  def to_effective_config effective_config
135
135
  super
136
136
  protect_rules_to_effective_config(effective_config)
@@ -138,7 +138,7 @@ module Contrast
138
138
 
139
139
  private
140
140
 
141
- # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
141
+ # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
142
142
  def protect_rules_to_effective_config effective_config
143
143
  return unless defend_rules
144
144
 
@@ -136,7 +136,7 @@ module Contrast
136
136
  # Converts current configuration to effective config values class and appends them to
137
137
  # EffectiveConfig class.
138
138
  #
139
- # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
139
+ # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
140
140
  def to_effective_config effective_config
141
141
  confirm_sources
142
142
 
@@ -161,10 +161,11 @@ module Contrast
161
161
  # back to the default values.
162
162
  def confirm_sources
163
163
  if sampling_control[:enabled] == DEFAULT_SAMPLING_ENABLED
164
- Contrast::CONFIG.sources.set('assess.sampling.enable', Contrast::Components::Config::Sources::DEFAULT)
164
+ Contrast::CONFIG.sources.set('assess.sampling.enable', Contrast::Components::Config::Sources::DEFAULT_VALUE)
165
165
  end
166
166
  if sampling_control[:window] == DEFAULT_SAMPLING_WINDOW_MS
167
- Contrast::CONFIG.sources.set('assess.sampling.window_ms', Contrast::Components::Config::Sources::DEFAULT)
167
+ Contrast::CONFIG.sources.set('assess.sampling.window_ms',
168
+ Contrast::Components::Config::Sources::DEFAULT_VALUE)
168
169
  end
169
170
  {
170
171
  'baseline' => :baseline,
@@ -172,7 +173,8 @@ module Contrast
172
173
  'response_frequency' => :response_frequency
173
174
  }.each do |k, v|
174
175
  if sampling_control[v] == cs__class.cs__const_get("DEFAULT_SAMPLING_#{ v.upcase }")
175
- Contrast::CONFIG.sources.set("assess.sampling.#{ k }", Contrast::Components::Config::Sources::DEFAULT)
176
+ Contrast::CONFIG.sources.set("assess.sampling.#{ k }",
177
+ Contrast::Components::Config::Sources::DEFAULT_VALUE)
176
178
  end
177
179
  end
178
180
  end
@@ -283,7 +283,7 @@ module Contrast
283
283
  return unless level.cs__is_a?(Hash)
284
284
 
285
285
  level[parts[-1]] = value
286
- Contrast::CONFIG.sources.set(parts.join('.'), Contrast::Components::Config::Sources::CONTRASTUI)
286
+ Contrast::CONFIG.sources.set(parts.join('.'), Contrast::Components::Config::Sources::CONTRAST_UI)
287
287
  end
288
288
  end
289
289
  end
@@ -38,7 +38,7 @@ module Contrast
38
38
  # Converts current configuration to effective config values class and appends them to
39
39
  # EffectiveConfig class.
40
40
  #
41
- # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
41
+ # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
42
42
  def to_effective_config effective_config
43
43
  add_effective_config_values(effective_config, CONFIG_VALUES, CANON_NAME, CONTRAST)
44
44
  end
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/duck_utils'
5
+ require 'contrast/utils/object_share'
6
+ require 'contrast/components/config/sources'
7
+ require 'contrast/config/diagnostics/tools'
8
+
9
+ module Contrast
10
+ module Config
11
+ # This class will hold all the references for the configuration files. It will safe read values used to
12
+ # identify the source of configuration in Configuration Diagnostics reported to TS.
13
+ class ConfigurationFiles
14
+ # @return [String] path of the main configuration file.
15
+ attr_accessor :main_file
16
+
17
+ # @return [Array<Contrast::Config::LocalSourceValue>]
18
+ def source_files
19
+ @_source_files ||= []
20
+ end
21
+
22
+ # @param path [String]
23
+ # @param values [Hash]
24
+ def add_source_file path, values
25
+ source_files << Contrast::Config::LocalSourceValue.new(path, values)
26
+ @main_file = path if source_files.length == 1
27
+ end
28
+ end
29
+
30
+ # This class will hold all the info about the read file.
31
+ class LocalSourceValue
32
+ YML_EXT = '.yml'
33
+ YAML_EXT = '.yaml'
34
+ # @return [String]
35
+ attr_reader :path
36
+ # @return [Hash]
37
+ attr_reader :values
38
+
39
+ # @param path [String]
40
+ # @param values [Hash]
41
+ def initialize path = '', values = {}
42
+ @path = path unless Contrast::Utils::DuckUtils.empty_duck?(path)
43
+ @values = values
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/config/diagnostics/effective_config_value'
5
+ require 'contrast/config/diagnostics/tools'
6
+ require 'contrast/utils/object_share'
7
+
8
+ module Contrast
9
+ module Config
10
+ module Diagnostics
11
+ # Reads All ENV variables.
12
+ module CommandLine
13
+ class << self
14
+ def command_line_settings
15
+ cli = Contrast::Config::Diagnostics::Tools.flatten_settings(Contrast::CONFIG.sources.
16
+ for(Contrast::Components::Config::Sources::COMMAND_LINE))
17
+
18
+ Contrast::Config::Diagnostics::Tools.to_config_values(cli, source: true)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,24 +1,32 @@
1
1
  # Copyright (c) 2023 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/config/effective_config'
4
+ require 'contrast/config/diagnostics/effective_config'
5
+ require 'contrast/config/diagnostics/user_configuration_file'
6
+ require 'contrast/config/diagnostics/environment_variables'
7
+ require 'contrast/config/diagnostics/command_line'
8
+ require 'contrast/config/diagnostics/contrast_ui'
5
9
 
6
10
  module Contrast
7
- module Agent
8
- module DiagnosticsConfig
11
+ module Config
12
+ module Diagnostics
9
13
  # This class is responsible for logging to file the effective Agent configurations after startup.
10
14
  class Config
15
+ # @return [Contrast::Config::Diagnostics::EffectiveConfig]
11
16
  attr_reader :effective_config
12
17
  # @return [String] Status message of the connection with TS.
13
18
  attr_accessor :config_status
19
+ # @return [Contrast::Config::Diagnostics::UserConfigurationFile]
20
+ attr_reader :user_configuration_file
14
21
 
15
22
  MESSAGE_FAIL = 'Unable to connect to Contrast, configuration details from the Contrast UI will not be included.'
16
23
  MESSAGE_SUCCESSFUL = 'Success'
17
24
  CONN_STATUS_MSG_FAILURE = 'Unable to connect to Contrast, insufficient connection properties provided.'
18
25
 
19
26
  def initialize
20
- @effective_config = Contrast::Agent::DiagnosticsConfig::EffectiveConfig.new
27
+ @effective_config = Contrast::Config::Diagnostics::EffectiveConfig.new
21
28
  @config_status = Contrast::Utils::ObjectShare::EMPTY_STRING
29
+ @user_configuration_file = Contrast::Config::Diagnostics::UserConfigurationFile.new
22
30
  end
23
31
 
24
32
  # This method will set the status from the request/response cycles
@@ -31,7 +39,6 @@ module Contrast
31
39
  else
32
40
  MESSAGE_FAIL
33
41
  end
34
- true
35
42
  end
36
43
 
37
44
  # This method will set the status message from the config validation
@@ -40,7 +47,15 @@ module Contrast
40
47
  end
41
48
 
42
49
  def to_controlled_hash
43
- @effective_config.to_controlled_hash.merge({ status: @config_status })
50
+ {
51
+ status: @config_status,
52
+ effective_config: effective_config.to_controlled_hash,
53
+ user_configuration_file: user_configuration_file.to_controlled_hash,
54
+ environment_variable: Contrast::Config::Diagnostics::EnvironmentVariables.environment_settings(ENV).
55
+ map(&:to_controlled_hash),
56
+ command_line: Contrast::Config::Diagnostics::CommandLine.command_line_settings.map(&:to_controlled_hash),
57
+ contrast_ui: Contrast::Config::Diagnostics::ContrastUI.contrast_ui_settings.map(&:to_controlled_hash)
58
+ }.compact
44
59
  end
45
60
  end
46
61
  end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/config/diagnostics/effective_config_value'
5
+ require 'contrast/config/diagnostics/tools'
6
+ require 'contrast/utils/object_share'
7
+
8
+ module Contrast
9
+ module Config
10
+ module Diagnostics
11
+ # Reads All ENV variables.
12
+ module ContrastUI
13
+ class << self
14
+ def contrast_ui_settings
15
+ ui = Contrast::Config::Diagnostics::Tools.flatten_settings(Contrast::CONFIG.sources.
16
+ for(Contrast::Components::Config::Sources::CONTRAST_UI))
17
+
18
+ Contrast::Config::Diagnostics::Tools.to_config_values(ui, source: true)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/config/diagnostics/effective_config_value'
5
+ require 'contrast/config/diagnostics/tools'
6
+ require 'contrast/utils/object_share'
7
+
8
+ module Contrast
9
+ module Config
10
+ module Diagnostics
11
+ # The current effective config received from all authorized configuration channels.
12
+ class EffectiveConfig
13
+ # Value of effective agent configurations
14
+ #
15
+ # @return [Array]
16
+ attr_reader :values
17
+
18
+ def initialize
19
+ @values = []
20
+ end
21
+
22
+ def to_controlled_hash
23
+ { values: @values&.map(&:to_controlled_hash) }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/config/diagnostics/source_config_value'
5
+
6
+ module Contrast
7
+ module Config
8
+ module Diagnostics
9
+ # All In effect config values stored in a easy to write representation.
10
+ class EffectiveConfigValue < SourceConfigValue
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/config/diagnostics/effective_config_value'
5
+ require 'contrast/config/diagnostics/tools'
6
+ require 'contrast/utils/object_share'
7
+
8
+ module Contrast
9
+ module Config
10
+ module Diagnostics
11
+ # Reads All ENV variables.
12
+ module EnvironmentVariables
13
+ class << self
14
+ NON_COMMON_ENV = %w[CONTRAST_CONFIG_PATH CONTRAST_AGENT_TELEMETRY_OPTOUT].cs__freeze
15
+
16
+ # This method will fill the canonical name for each env var and will check for any uncommon ones.
17
+ #
18
+ # @param env [Hash]
19
+ # @return [Array] array of all the values needed to be written.
20
+ def environment_settings env
21
+ env_hash = env.select do |e|
22
+ e.to_s.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER) || NON_COMMON_ENV.include?(e.to_s)
23
+ end
24
+ environment_settings = []
25
+ env_hash.each do |key, value|
26
+ efc_value = Contrast::Config::Diagnostics::EffectiveConfigValue.new.tap do |effective_value|
27
+ next unless value
28
+
29
+ effective_value.canonical_name = if NON_COMMON_ENV.include?(key)
30
+ key.gsub(Contrast::Utils::ObjectShare::UNDERSCORE,
31
+ Contrast::Utils::ObjectShare::PERIOD).downcase
32
+ else
33
+ key.gsub(Contrast::Utils::ObjectShare::DOUBLE_UNDERSCORE,
34
+ Contrast::Utils::ObjectShare::PERIOD).downcase
35
+ end
36
+ if effective_value.canonical_name
37
+ effective_value.key =
38
+ effective_value.canonical_name.gsub(Contrast::Utils::ObjectShare::CONTRAST_DOT,
39
+ Contrast::Utils::ObjectShare::EMPTY_STRING)
40
+ end
41
+ effective_value.value = Contrast::Config::Diagnostics::Tools.value_to_s(value)
42
+ end
43
+ environment_settings << efc_value if efc_value
44
+ end
45
+ environment_settings
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -7,16 +7,16 @@ require 'contrast/utils/timer'
7
7
  require 'contrast/utils/log_utils'
8
8
  require 'contrast/components/logger'
9
9
  require 'contrast/utils/object_share'
10
- require 'contrast/config/config'
11
- require 'contrast/config/effective_config'
12
- require 'contrast/config/effective_config_value'
10
+ require 'contrast/config/diagnostics/config'
11
+ require 'contrast/config/diagnostics/effective_config'
12
+ require 'contrast/config/diagnostics/effective_config_value'
13
13
  require 'contrast/utils/duck_utils'
14
14
 
15
15
  module Contrast
16
- module Agent
17
- module DiagnosticsConfig
16
+ module Config
17
+ module Diagnostics
18
18
  # This class is responsible for logging to file the effective Agent configurations after startup.
19
- class Diagnostics
19
+ class Monitor
20
20
  include Contrast::Components::Logger::InstanceMethods
21
21
  include Contrast::Utils::LogUtils
22
22
 
@@ -30,7 +30,7 @@ module Contrast
30
30
 
31
31
  # @param path [String] path to write to file.
32
32
  def initialize path
33
- @path = path if path && !path.empty?
33
+ @path = path unless Contrast::Utils::DuckUtils.empty_duck?(path)
34
34
  end
35
35
 
36
36
  # Write current settings to file.
@@ -71,9 +71,9 @@ module Contrast
71
71
 
72
72
  # Returns effective configurations of the agent.
73
73
  #
74
- # @return [Contrast::Agent::DiagnosticsConfig::Config]
74
+ # @return [Contrast::Config::Diagnostics::Config]
75
75
  def config
76
- @_config ||= Contrast::Agent::DiagnosticsConfig::Config.new
76
+ @_config ||= Contrast::Config::Diagnostics::Config.new
77
77
  end
78
78
 
79
79
  # Reset the state of the current effective config.
@@ -84,7 +84,7 @@ module Contrast
84
84
  else
85
85
  @_config.config_status
86
86
  end
87
- @_config = Contrast::Agent::DiagnosticsConfig::Config.new
87
+ @_config = Contrast::Config::Diagnostics::Config.new
88
88
  @_config.config_status = status if status
89
89
  @_config
90
90
  end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Config
6
+ module Diagnostics
7
+ # All config values from all sources, stored in a easy to write representation.
8
+ class SourceConfigValue
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 :key
13
+ # @return [String, Boolean, array<String>] Value set for the config. Current Effective Value
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: key,
24
+ value: value.cs__is_a?(Array) ? value.map(&:to_s) : value.to_s,
25
+ source: source,
26
+ filename: filename
27
+ }.compact
28
+ end
29
+
30
+ def to_source_hash
31
+ {
32
+ canonical_name: canonical_name,
33
+ name: key,
34
+ value: value.cs__is_a?(Array) ? value.map(&:to_s) : value.to_s
35
+ }.compact
36
+ end
37
+
38
+ # Assigns file name of the config iv viable, Currently supported formats for config file are *.yaml
39
+ # and *.yml
40
+ #
41
+ # @param source [String] name of the source file yaml | yml
42
+ # @return [Array<String>, nil]
43
+ def assign_filename source
44
+ Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE.each do |type|
45
+ instance_variable_set(:@filename, source) if source.include?(".#{ type.downcase }")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,188 @@
1
+ # Copyright (c) 2023 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
+ require 'contrast/config/diagnostics/effective_config_value'
6
+ require 'contrast/utils/duck_utils'
7
+
8
+ module Contrast
9
+ module Config
10
+ module Diagnostics
11
+ # Diagnostics tools to be included in config components.
12
+ module Tools
13
+ CHECK = 'd'
14
+ class << self
15
+ # Creates new config instances for each read config entry from the flat generated configs.
16
+ #
17
+ # @param flats [Array] of flatten configs produced by #flatten_settings
18
+ # @param source [Boolean] flag to set the desired value class, it may be a effective or source value.
19
+ # @return [Array<Contrast::Config::Diagnostics::SourceConfigValue>]
20
+ def to_config_values flats, source: false
21
+ config_value_klass = if source
22
+ Contrast::Config::Diagnostics::SourceConfigValue
23
+ else
24
+ Contrast::Config::Diagnostics::EffectiveConfigValue
25
+ end
26
+ settings = []
27
+ flats.each do |entry|
28
+ entry.each do |key, value|
29
+ efc_value = config_value_klass.new.tap do |config_value|
30
+ config_value.canonical_name = Contrast::Utils::ObjectShare::CONTRAST_DOT + key
31
+ config_value.key = key
32
+ config_value.value = value_to_s(value)
33
+ end
34
+ settings << efc_value if efc_value
35
+ end
36
+ end
37
+ settings
38
+ end
39
+
40
+ # Flattens out the read settings from file, env or contrast ui.
41
+ # example: {"agent.polling.server_settings_ms"=>"50000"}
42
+ #
43
+ # @param data [Hash, nil]
44
+ # @param path [String] where to look for settings.
45
+ # @param config [Hash] symbolized config to fetch keys from.
46
+ def flatten_settings data, path = [], config: Contrast::CONFIG.config.loaded_config
47
+ return [] unless data
48
+
49
+ data.each_with_object([]) do |(k, v), entries|
50
+ if v.cs__is_a?(Hash)
51
+ entries.concat(flatten_settings(v, path.dup.append(k.to_sym)))
52
+ else
53
+ entries << { "#{ path.join('.') }.#{ k }" => config.dig(*path, k).to_s }
54
+ end
55
+ end.flatten # rubocop:disable Style/MethodCalledOnDoEndBlock
56
+ end
57
+
58
+ # Recursively converts each value to string.
59
+ #
60
+ # @param value [Hash, nil]
61
+ def value_to_s value
62
+ return if value.nil?
63
+ return value if value.cs__is_a?(String)
64
+
65
+ value.each_with_object({}) do |(k, v), m| # rubocop:disable Style/HashTransformValues
66
+ m[k] = if v.cs__is_a?(Hash)
67
+ value_to_s(v)
68
+ elsif v.cs__is_a?(Array)
69
+ v.map(&:to_s)
70
+ else
71
+ v.to_s
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ # Converts current configuration from array of values to effective config values class and appends them to
78
+ # EffectiveConfig class. Must be used inside Config Components only.
79
+ #
80
+ # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
81
+ # @param config_values [] array of the names of values.
82
+ # @param canonical_prefix [String] starting of the path to config => api.proxy...
83
+ # @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
84
+ def add_effective_config_values effective_config, config_values, canonical_prefix, name_prefix
85
+ return if config_values.to_s.empty?
86
+
87
+ config_values.each do |config_value_name|
88
+ Contrast::Config::Diagnostics::EffectiveConfigValue.new.tap do |new_effective_value|
89
+ next if Contrast::Utils::DuckUtils.empty_duck?((config_value = send(config_value_name.to_sym)))
90
+
91
+ fill_effective_value(new_effective_value, config_value, config_value_name, canonical_prefix, name_prefix)
92
+ effective_config.values << new_effective_value
93
+ rescue StandardError => e
94
+ log_error(e)
95
+ next
96
+ end
97
+ end
98
+ end
99
+
100
+ # Converts current configuration from single value to effective config values class and appends them to
101
+ # EffectiveConfig class. Must be used inside Config Components only.
102
+ #
103
+ # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
104
+ # @param config_name [String] name of the config.
105
+ # @param config_value [String, Boolean] value of the config.
106
+ # @param canonical_prefix [String] starting of the path to config => api.proxy...
107
+ # @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
108
+ def add_single_effective_value effective_config, config_name, config_value, canonical_prefix, name_prefix
109
+ Contrast::Config::Diagnostics::EffectiveConfigValue.new.tap do |new_effective_value|
110
+ break if Contrast::Utils::DuckUtils.empty_duck?(config_value)
111
+
112
+ fill_effective_value(new_effective_value, config_value, config_name, canonical_prefix, name_prefix)
113
+ effective_config.values << new_effective_value
114
+ rescue StandardError => e
115
+ log_error(e)
116
+ next
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ # Fills instance of effective configuration value from read configuration.
123
+ #
124
+ # @param new_effective_value [Contrast::Config::Diagnostics::EffectiveConfigValue]
125
+ # @param config_value [String, Boolean] value of the config
126
+ # @param config_value_name [string] name of the value e.g. enabled?
127
+ # @param canonical_prefix [String] starting of the path to config => api.proxy...
128
+ # @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
129
+ # @return filled_new_effective_config [Contrast::Config::Diagnostics::EffectiveConfigValue]
130
+ def fill_effective_value new_effective_value, config_value, config_value_name, canonical_prefix, name_prefix
131
+ find_source(new_effective_value, canonical_prefix, assign_name(config_value_name), name_prefix)
132
+ new_effective_value.value = config_value
133
+ new_effective_value
134
+ end
135
+
136
+ # Assigns a proper name for the config removing '?' out of method names.
137
+ #
138
+ # @param config [String] name of the configuration
139
+ # @return [String]
140
+ def assign_name config
141
+ return Contrast::Utils::ObjectShare::EMPTY_STRING unless config
142
+
143
+ name = config.dup
144
+ if name.end_with?(Contrast::Utils::ObjectShare::QUESTION_MARK)
145
+ # check and remove '?' : start_bundled_service? => start_bundled_service
146
+ name.delete!(Contrast::Utils::ObjectShare::QUESTION_MARK)
147
+ # converts name e.g. enabled => enable
148
+ name.chop! if name.end_with?(CHECK)
149
+ name
150
+ end
151
+ name
152
+ end
153
+
154
+ # Retrieves the config value from sources by canonical name and sets the key value.
155
+ #
156
+ # @param new_effective_value [Contrast::Config::Diagnostics::EffectiveConfigValue]
157
+ # @param canonical_prefix [String] starting of the path to config => api.proxy...
158
+ # @param config_name [String] name of the config.
159
+ # @param name_prefix [String] the name of the config prefix => contrast.api_key, contrast.url
160
+ # @return [Contrast::Config::Diagnostics::EffectiveConfigValue]
161
+ def find_source new_effective_value, canonical_prefix, config_name, name_prefix
162
+ new_effective_value.key = "#{ name_prefix }.#{ config_name }"
163
+ new_effective_value.canonical_name = "#{ canonical_prefix }.#{ config_name }"
164
+ # For files we keep the whole path as source.
165
+ source = Contrast::CONFIG.sources.get(new_effective_value.canonical_name)
166
+ new_effective_value.assign_filename(source)
167
+ new_source = if source.include?(Contrast::Config::LocalSourceValue::YAML_EXT)
168
+ Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE[0]
169
+ elsif source.include?(Contrast::Config::LocalSourceValue::YML_EXT)
170
+ Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE[1]
171
+ else
172
+ Contrast::Components::Config::Sources::DEFAULT_VALUE
173
+ end
174
+ new_effective_value.source = new_source
175
+ new_effective_value
176
+ end
177
+
178
+ # Logs any caught error.
179
+ #
180
+ # @param error [StandardError]
181
+ def log_error error
182
+ Contrast::CONFIG.proto_logger.warn(Contrast::Config::Diagnostics::Monitor::ERROR_MESSAGE,
183
+ error: error, backtrace: error.backtrace)
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end