contrast-agent 6.8.0 → 6.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/lib/contrast/agent/assess/policy/trigger_method.rb +1 -1
  4. data/lib/contrast/agent/assess/property/evented.rb +11 -11
  5. data/lib/contrast/agent/assess/rule/response/body_rule.rb +1 -1
  6. data/lib/contrast/agent/assess.rb +0 -1
  7. data/lib/contrast/agent/excluder.rb +1 -1
  8. data/lib/contrast/agent/middleware.rb +12 -4
  9. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +76 -83
  10. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +121 -0
  11. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +2 -0
  12. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +6 -3
  13. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +3 -0
  14. data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +3 -0
  15. data/lib/contrast/agent/protect/policy/rule_applicator.rb +12 -0
  16. data/lib/contrast/agent/protect/rule/base.rb +21 -7
  17. data/lib/contrast/agent/protect/rule/base_service.rb +6 -0
  18. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker_input_classification.rb +1 -1
  19. data/lib/contrast/agent/protect/rule/bot_blocker.rb +9 -1
  20. data/lib/contrast/agent/protect/rule/cmd_injection.rb +8 -7
  21. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +8 -0
  22. data/lib/contrast/agent/protect/rule/cmdi/cmdi_chained_command.rb +0 -5
  23. data/lib/contrast/agent/protect/rule/cmdi/cmdi_dangerous_path.rb +0 -5
  24. data/lib/contrast/agent/protect/rule/deserialization.rb +2 -2
  25. data/lib/contrast/agent/protect/rule/no_sqli.rb +24 -2
  26. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_input_classification.rb +1 -1
  27. data/lib/contrast/agent/protect/rule/path_traversal.rb +12 -3
  28. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +0 -1
  29. data/lib/contrast/agent/protect/rule/sqli.rb +10 -13
  30. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +6 -2
  31. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +20 -0
  32. data/lib/contrast/agent/protect/rule/xss/reflected_xss_input_classification.rb +1 -1
  33. data/lib/contrast/agent/protect/rule/xss.rb +9 -0
  34. data/lib/contrast/agent/protect/rule/xxe.rb +2 -2
  35. data/lib/contrast/agent/protect/rule.rb +0 -3
  36. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +1 -1
  37. data/lib/contrast/agent/reporting/attack_result/user_input.rb +0 -1
  38. data/lib/contrast/agent/reporting/details/details.rb +0 -1
  39. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +12 -0
  40. data/lib/contrast/agent/reporting/report.rb +2 -0
  41. data/lib/contrast/agent/reporting/reporter.rb +42 -7
  42. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +5 -6
  43. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +24 -7
  44. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +20 -5
  45. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +0 -1
  46. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -0
  47. data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -1
  48. data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +2 -1
  49. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -1
  50. data/lib/contrast/agent/reporting/reporting_events/application_reporting_event.rb +10 -0
  51. data/lib/contrast/agent/reporting/reporting_events/application_settings.rb +40 -0
  52. data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
  53. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +239 -93
  54. data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +10 -23
  55. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +10 -9
  56. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +12 -0
  57. data/lib/contrast/agent/reporting/reporting_events/server_reporting_event.rb +8 -0
  58. data/lib/contrast/agent/reporting/reporting_events/server_settings.rb +40 -0
  59. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +6 -0
  60. data/lib/contrast/agent/reporting/reporting_utilities/ng_response_extractor.rb +137 -0
  61. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +52 -2
  62. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -4
  63. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +105 -58
  64. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +9 -7
  65. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +143 -49
  66. data/lib/contrast/agent/reporting/reporting_workers/application_server_worker.rb +46 -0
  67. data/lib/contrast/agent/reporting/reporting_workers/reporter_heartbeat.rb +51 -0
  68. data/lib/contrast/agent/reporting/reporting_workers/reporting_workers.rb +14 -0
  69. data/lib/contrast/agent/reporting/reporting_workers/server_settings_worker.rb +46 -0
  70. data/lib/contrast/agent/reporting/settings/assess.rb +14 -1
  71. data/lib/contrast/agent/reporting/settings/assess_rule.rb +18 -0
  72. data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +14 -2
  73. data/lib/contrast/agent/reporting/settings/helpers.rb +9 -0
  74. data/lib/contrast/agent/reporting/settings/protect.rb +17 -12
  75. data/lib/contrast/agent/reporting/settings/protect_rule.rb +18 -0
  76. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +40 -3
  77. data/lib/contrast/agent/reporting/settings/rule_definition.rb +3 -0
  78. data/lib/contrast/agent/reporting/settings/security_logger.rb +77 -0
  79. data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +1 -1
  80. data/lib/contrast/agent/reporting/settings/server_features.rb +9 -0
  81. data/lib/contrast/agent/reporting/settings/syslog.rb +34 -5
  82. data/lib/contrast/agent/reporting/settings/virtual_patch.rb +56 -0
  83. data/lib/contrast/agent/reporting/settings/virtual_patch_condition.rb +47 -0
  84. data/lib/contrast/agent/request.rb +1 -0
  85. data/lib/contrast/agent/request_context_extend.rb +20 -0
  86. data/lib/contrast/agent/request_handler.rb +5 -10
  87. data/lib/contrast/agent/telemetry/base.rb +11 -10
  88. data/lib/contrast/agent/telemetry/events/exceptions/obfuscate.rb +108 -103
  89. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +1 -1
  90. data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +1 -1
  91. data/lib/contrast/agent/thread_watcher.rb +46 -6
  92. data/lib/contrast/agent/version.rb +1 -1
  93. data/lib/contrast/agent.rb +18 -0
  94. data/lib/contrast/agent_lib/api/init.rb +1 -7
  95. data/lib/contrast/agent_lib/api/input_tracing.rb +2 -4
  96. data/lib/contrast/agent_lib/interface.rb +1 -16
  97. data/lib/contrast/agent_lib/interface_base.rb +52 -39
  98. data/lib/contrast/agent_lib/return_types/eval_result.rb +2 -2
  99. data/lib/contrast/api/communication/connection_status.rb +15 -0
  100. data/lib/contrast/components/agent.rb +34 -0
  101. data/lib/contrast/components/api.rb +23 -0
  102. data/lib/contrast/components/app_context.rb +23 -3
  103. data/lib/contrast/components/assess.rb +60 -8
  104. data/lib/contrast/components/assess_rules.rb +18 -0
  105. data/lib/contrast/components/base.rb +40 -0
  106. data/lib/contrast/components/config/sources.rb +95 -0
  107. data/lib/contrast/components/config.rb +18 -1
  108. data/lib/contrast/components/heap_dump.rb +10 -0
  109. data/lib/contrast/components/inventory.rb +15 -2
  110. data/lib/contrast/components/logger.rb +18 -0
  111. data/lib/contrast/components/polling.rb +39 -0
  112. data/lib/contrast/components/protect.rb +48 -1
  113. data/lib/contrast/components/ruby_component.rb +15 -0
  114. data/lib/contrast/components/sampling.rb +70 -13
  115. data/lib/contrast/components/security_logger.rb +13 -0
  116. data/lib/contrast/components/settings.rb +120 -10
  117. data/lib/contrast/config/certification_configuration.rb +14 -0
  118. data/lib/contrast/config/config.rb +46 -0
  119. data/lib/contrast/config/diagnostics.rb +114 -0
  120. data/lib/contrast/config/diagnostics_tools.rb +98 -0
  121. data/lib/contrast/config/effective_config.rb +65 -0
  122. data/lib/contrast/config/effective_config_value.rb +32 -0
  123. data/lib/contrast/config/exception_configuration.rb +12 -0
  124. data/lib/contrast/config/protect_rule_configuration.rb +2 -2
  125. data/lib/contrast/config/protect_rules_configuration.rb +7 -6
  126. data/lib/contrast/config/request_audit_configuration.rb +13 -0
  127. data/lib/contrast/config/server_configuration.rb +41 -2
  128. data/lib/contrast/configuration.rb +28 -2
  129. data/lib/contrast/extension/assess/array.rb +3 -3
  130. data/lib/contrast/extension/assess/erb.rb +1 -1
  131. data/lib/contrast/extension/assess/regexp.rb +2 -2
  132. data/lib/contrast/logger/aliased_logging.rb +48 -15
  133. data/lib/contrast/utils/assess/event_limit_utils.rb +31 -9
  134. data/lib/contrast/utils/assess/trigger_method_utils.rb +5 -4
  135. data/lib/contrast/utils/hash_digest.rb +2 -2
  136. data/lib/contrast/utils/input_classification_base.rb +21 -5
  137. data/lib/contrast/utils/reporting/application_activity_batch_utils.rb +81 -0
  138. data/lib/contrast/utils/routes_sent.rb +60 -0
  139. data/lib/contrast/utils/telemetry.rb +1 -1
  140. data/lib/contrast/utils/telemetry_client.rb +2 -3
  141. data/lib/contrast/utils/timer.rb +16 -0
  142. data/lib/contrast.rb +3 -1
  143. data/resources/protect/policy.json +8 -0
  144. data/ruby-agent.gemspec +6 -2
  145. metadata +43 -24
  146. data/lib/contrast/agent/assess/contrast_event.rb +0 -157
  147. data/lib/contrast/agent/assess/events/event_factory.rb +0 -34
  148. data/lib/contrast/agent/assess/events/source_event.rb +0 -46
  149. data/lib/contrast/agent/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +0 -96
  150. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +0 -83
  151. data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +0 -27
  152. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +0 -53
  153. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +0 -36
  154. data/lib/contrast/agent_lib/api/method_tempering.rb +0 -29
@@ -0,0 +1,39 @@
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/components/base'
5
+
6
+ module Contrast
7
+ module Components
8
+ module Polling
9
+ # A wrapper build around the agent.polling config
10
+ class Interface
11
+ include Contrast::Components::ComponentBase
12
+
13
+ # @return [Integer, nil]
14
+ attr_reader :server_settings_ms
15
+ # @return [Integer, nil]
16
+ attr_reader :app_settings_ms
17
+ # @return [String]
18
+ attr_reader :canon_name
19
+ # @return [Array]
20
+ attr_reader :config_values
21
+ # @return [Integer, nil]
22
+ attr_reader :batch_reporting_interval_ms
23
+
24
+ CANON_NAME = 'agent.polling'
25
+ CONFIG_VALUES = %w[server_settings_ms app_settings_ms batch_reporting_interval_ms].cs__freeze
26
+
27
+ def initialize hsh = {}
28
+ @config_values = CONFIG_VALUES
29
+ @canon_name = CANON_NAME
30
+ return unless hsh
31
+
32
+ @server_settings_ms = hsh[:server_settings_ms]
33
+ @batch_reporting_interval_ms = hsh[:batch_reporting_interval_ms]
34
+ @app_settings_ms = hsh[:app_settings_ms]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -14,12 +14,23 @@ module Contrast
14
14
  include Contrast::Components::ComponentBase
15
15
  include Contrast::Config::BaseConfiguration
16
16
 
17
+ CANON_NAME = 'protect'
18
+ CONFIG_VALUES = %w[enabled?].cs__freeze
19
+ RULES = 'rules'
20
+ MODE = 'mode'
21
+
17
22
  # @return [Boolean, nil]
18
23
  attr_accessor :enable
24
+ # @return [String]
25
+ attr_reader :canon_name
26
+ # @return [Array]
27
+ attr_reader :config_values
19
28
  # @return [Boolean, nil]
20
29
  attr_accessor :agent_lib
21
30
 
22
31
  def initialize hsh = {}
32
+ @config_values = CONFIG_VALUES
33
+ @canon_name = CANON_NAME
23
34
  return unless hsh
24
35
 
25
36
  @_exceptions = Contrast::Config::ExceptionConfiguration.new(hsh[:exceptions])
@@ -57,6 +68,9 @@ module Contrast
57
68
  ::Contrast::SETTINGS.protect_state.enabled == true
58
69
  end
59
70
 
71
+ # Current Configuration for the protect rules
72
+ #
73
+ # @return [Contrast::Config::ProtectRulesConfiguration]
60
74
  def rule_config
61
75
  ::Contrast::CONFIG.protect.rules
62
76
  end
@@ -65,11 +79,17 @@ module Contrast
65
79
  # protect rules.
66
80
  #
67
81
  # @return defend_rules[Hash<Contrast::SETTINGS.protect_state.rules>]
68
- #
69
82
  def defend_rules
70
83
  ::Contrast::SETTINGS.protect_state.rules
71
84
  end
72
85
 
86
+ # The Contrast::CONFIG.protect.rules is object so we need to check it's
87
+ # corresponding method call for each rule of interest. If there is no
88
+ # status available we search for any Settings available received form
89
+ # TS response.
90
+ #
91
+ # @param rule_id [String]
92
+ # @return mode [Symbol]
73
93
  def rule_mode rule_id
74
94
  str = rule_id.tr('-', '_')
75
95
  ::Contrast::CONFIG.protect.rules[str]&.applicable_mode ||
@@ -77,6 +97,9 @@ module Contrast
77
97
  :NO_ACTION
78
98
  end
79
99
 
100
+ # Name of the protect rule
101
+ #
102
+ # @return [String]
80
103
  def rule name
81
104
  ::Contrast::SETTINGS.protect_state.rules[name]
82
105
  end
@@ -104,8 +127,32 @@ module Contrast
104
127
  @_forcibly_disabled = false?(::Contrast::CONFIG.protect.enable)
105
128
  end
106
129
 
130
+ # Converts current configuration to effective config values class and appends them to
131
+ # EffectiveConfig class.
132
+ #
133
+ # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
134
+ def to_effective_config effective_config
135
+ super
136
+ protect_rules_to_effective_config(effective_config)
137
+ end
138
+
107
139
  private
108
140
 
141
+ # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
142
+ def protect_rules_to_effective_config effective_config
143
+ return unless defend_rules
144
+
145
+ defend_rules.each do |key, value|
146
+ next unless key && value
147
+
148
+ config_prefix = "#{ CANON_NAME }.#{ RULES }.#{ key }"
149
+ name_prefix = "#{ CONTRAST }.#{ CANON_NAME }.#{ RULES }.#{ key }"
150
+ add_single_effective_value(effective_config, ENABLE, value.enabled?, config_prefix, name_prefix)
151
+ # Get the mode by checking Current Configs or Settings received:
152
+ add_single_effective_value(effective_config, MODE, rule_mode(key), config_prefix, name_prefix)
153
+ end
154
+ end
155
+
109
156
  def forcibly_enabled?
110
157
  return @_forcibly_enabled unless @_forcibly_enabled.nil?
111
158
 
@@ -1,6 +1,8 @@
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 Components
6
8
  module Ruby
@@ -23,11 +25,24 @@ module Contrast
23
25
  ].cs__freeze
24
26
 
25
27
  DEFAULT_UNINSTRUMENTED_NAMESPACES = %w[FactoryGirl FactoryBot].cs__freeze
28
+ CANON_NAME = 'agent.ruby'
29
+ CONFIG_VALUES = %w[
30
+ disabled_agent_rake_tasks
31
+ propagate_yield require_scan
32
+ non_request_tracking
33
+ uninstrument_namespace
34
+ ].cs__freeze
26
35
 
27
36
  attr_writer :disabled_agent_rake_tasks, :exceptions, :propagate_yield, :require_scan,
28
37
  :non_request_tracking, :uninstrument_namespace
38
+ # @return [String]
39
+ attr_reader :canon_name
40
+ # @return [Array]
41
+ attr_reader :config_values
29
42
 
30
43
  def initialize hsh = {}
44
+ @config_values = CONFIG_VALUES
45
+ @canon_name = CANON_NAME
31
46
  return unless hsh
32
47
 
33
48
  @disabled_agent_rake_tasks = hsh[:disabled_agent_rake_tasks]
@@ -28,10 +28,10 @@ module Contrast
28
28
  settings = ::Contrast::SETTINGS&.assess_state&.sampling_settings
29
29
  {
30
30
  enabled: enabled?(config_settings, settings),
31
- baseline: baseline(config_settings, settings),
32
- request_frequency: request_frequency(config_settings, settings),
33
- response_frequency: response_frequency(config_settings, settings),
34
- window: window(config_settings, settings)
31
+ baseline: check_baseline(config_settings, settings),
32
+ request_frequency: check_request_frequency(config_settings, settings),
33
+ response_frequency: check_response_frequency(config_settings, settings),
34
+ window: check_window(config_settings, settings)
35
35
  }
36
36
  end
37
37
  end
@@ -45,7 +45,8 @@ module Contrast
45
45
 
46
46
  # @param config_settings [Contrast::Config::SamplingConfiguration] the Sampling configuration as provided by
47
47
  # local user input
48
- # @param settings [Contrast::Api::Settings::Sampling] the Sampling settings as provided by TeamServer
48
+ # @param settings [Contrast::Agent::Reporting::Settings::Sampling, nil] the Sampling settings as provided by
49
+ # TeamServer
49
50
  # @return [Boolean] the resolution of the config_settings, settings, and default value
50
51
  def enabled? config_settings, settings
51
52
  true?([config_settings&.enable, settings&.enabled, DEFAULT_SAMPLING_ENABLED].compact[0])
@@ -53,35 +54,39 @@ module Contrast
53
54
 
54
55
  # @param config_settings [Contrast::Config::SamplingConfiguration] the Sampling configuration as provided by
55
56
  # local user input
56
- # @param settings [Contrast::Api::Settings::Sampling] the Sampling settings as provided by TeamServer
57
+ # @param settings [Contrast::Agent::Reporting::Settings::Sampling] the Sampling settings as provided by
58
+ # TeamServer
57
59
  # @return [Integer] the resolution of the config_settings, settings, and default value
58
- def baseline config_settings, settings
60
+ def check_baseline config_settings, settings
59
61
  [config_settings&.baseline, settings&.baseline].map(&:to_i).find(&:positive?) || DEFAULT_SAMPLING_BASELINE
60
62
  end
61
63
 
62
64
  # @param config_settings [Contrast::Config::SamplingConfiguration] the Sampling configuration as provided by
63
65
  # local user input
64
- # @param settings [Contrast::Api::Settings::Sampling] the Sampling settings as provided by TeamServer
66
+ # @param settings [Contrast::Agent::Reporting::Settings::Sampling] the Sampling settings as provided by
67
+ # TeamServer
65
68
  # @return [Integer] the resolution of the config_settings, settings, and default value
66
- def request_frequency config_settings, settings
69
+ def check_request_frequency config_settings, settings
67
70
  [config_settings&.request_frequency, settings&.request_frequency].map(&:to_i).find(&:positive?) ||
68
71
  DEFAULT_SAMPLING_REQUEST_FREQUENCY
69
72
  end
70
73
 
71
74
  # @param config_settings [Contrast::Config::SamplingConfiguration] the Sampling configuration as provided by
72
75
  # local user input
73
- # @param settings [Contrast::Api::Settings::Sampling] the Sampling settings as provided by TeamServer
76
+ # @param settings [Contrast::Agent::Reporting::Settings::Sampling] the Sampling settings as provided by
77
+ # TeamServer
74
78
  # @return [Integer] the resolution of the config_settings, settings, and default value
75
- def response_frequency config_settings, settings
79
+ def check_response_frequency config_settings, settings
76
80
  [config_settings&.response_frequency, settings&.response_frequency].map(&:to_i).find(&:positive?) ||
77
81
  DEFAULT_SAMPLING_RESPONSE_FREQUENCY
78
82
  end
79
83
 
80
84
  # @param config_settings [Contrast::Config::SamplingConfiguration] the Sampling configuration as provided by
81
85
  # local user input
82
- # @param settings [Contrast::Api::Settings::Sampling] the Sampling settings as provided by TeamServer
86
+ # @param settings [Contrast::Agent::Reporting::Settings::Sampling] the Sampling settings as provided by
87
+ # TeamServer
83
88
  # @return [Integer] the resolution of the config_settings, settings, and default value
84
- def window config_settings, settings
89
+ def check_window config_settings, settings
85
90
  [config_settings&.window_ms, settings&.window_ms].map(&:to_i).find(&:positive?) || DEFAULT_SAMPLING_WINDOW_MS
86
91
  end
87
92
  end
@@ -94,8 +99,13 @@ module Contrast
94
99
 
95
100
  class Interface # :nodoc:
96
101
  include InstanceMethods
102
+ extend ClassMethods
97
103
  include Contrast::Config::BaseConfiguration
98
104
 
105
+ CANON_NAME = 'assess.sampling'
106
+ NAME_PREFIX = "#{ CONTRAST }.#{ CANON_NAME }"
107
+ CONFIG_VALUES = %w[enable baseline request_frequency response_frequency window_ms].cs__freeze
108
+
99
109
  # @return [Integer, nil]
100
110
  attr_reader :baseline
101
111
  # @return [Integer, nil]
@@ -104,8 +114,11 @@ module Contrast
104
114
  attr_reader :response_frequency
105
115
  # @return [Integer, nil]
106
116
  attr_reader :window_ms
117
+ # @return [String]
118
+ attr_reader :canon_name
107
119
 
108
120
  def initialize hsh = {}
121
+ @canon_name = CANON_NAME
109
122
  return unless hsh
110
123
 
111
124
  @enable = hsh[:enable]
@@ -119,6 +132,50 @@ module Contrast
119
132
  def enable
120
133
  !!@enable
121
134
  end
135
+
136
+ # Converts current configuration to effective config values class and appends them to
137
+ # EffectiveConfig class.
138
+ #
139
+ # @param effective_config [Contrast::Agent::DiagnosticsConfig::EffectiveConfig]
140
+ def to_effective_config effective_config
141
+ confirm_sources
142
+
143
+ add_single_effective_value(effective_config, 'enable', sampling_control[:enabled], canon_name, NAME_PREFIX)
144
+ add_single_effective_value(effective_config, 'baseline', sampling_control[:baseline], canon_name, NAME_PREFIX)
145
+ add_single_effective_value(effective_config, 'window_ms', sampling_control[:window], canon_name, NAME_PREFIX)
146
+ add_single_effective_value(effective_config,
147
+ 'request_frequency',
148
+ sampling_control[:request_frequency],
149
+ canon_name,
150
+ NAME_PREFIX)
151
+ add_single_effective_value(effective_config,
152
+ 'response_frequency',
153
+ sampling_control[:response_frequency],
154
+ canon_name,
155
+ NAME_PREFIX)
156
+ end
157
+
158
+ private
159
+
160
+ # Confirm the sources for the sample entries to ensure that we use 'Default' if we've fallen
161
+ # back to the default values.
162
+ def confirm_sources
163
+ if sampling_control[:enabled] == DEFAULT_SAMPLING_ENABLED
164
+ Contrast::CONFIG.sources.set('assess.sampling.enable', Contrast::Components::Config::Sources::DEFAULT)
165
+ end
166
+ if sampling_control[:window] == DEFAULT_SAMPLING_WINDOW_MS
167
+ Contrast::CONFIG.sources.set('assess.sampling.window_ms', Contrast::Components::Config::Sources::DEFAULT)
168
+ end
169
+ {
170
+ 'baseline' => :baseline,
171
+ 'request_frequency' => :request_frequency,
172
+ 'response_frequency' => :response_frequency
173
+ }.each do |k, v|
174
+ 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
+ end
177
+ end
178
+ end
122
179
  end
123
180
  end
124
181
  end
@@ -1,17 +1,30 @@
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 Components
6
8
  module SecurityLogger
7
9
  # Here we will read and store the setting for the CEF Logging functionality
8
10
  class Interface
11
+ include Contrast::Components::ComponentBase
12
+
13
+ CANON_NAME = 'agent.security_logger'
14
+ CONFIG_VALUES = %w[path level].cs__freeze
15
+
9
16
  # @return [String, nil]
10
17
  attr_accessor :path
11
18
  # @return [String, nil]
12
19
  attr_accessor :level
20
+ # @return [String]
21
+ attr_reader :canon_name
22
+ # @return [Array]
23
+ attr_reader :config_values
13
24
 
14
25
  def initialize hsh = {}
26
+ @config_values = CONFIG_VALUES
27
+ @canon_name = CANON_NAME
15
28
  return unless hsh
16
29
 
17
30
  @path = hsh[:path]
@@ -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,16 @@ 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
78
+ # @return [String] The last update but in string format used to build request header.
79
+ # This value should be sent be TS in the Last-Modified header to sync and save resources if the
80
+ # two dates are the same.
81
+ # format: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
82
+ attr_reader :app_settings_last_httpdate
71
83
 
72
84
  def initialize
73
85
  reset_state
@@ -78,16 +90,69 @@ module Contrast
78
90
  end
79
91
 
80
92
  # @param features_response [Contrast::Agent::Reporting::Response]
81
- def update_from_server_features features_response
93
+ def update_from_server_features features_response # rubocop:disable Metrics/AbcSize
82
94
  return unless (server_features = features_response&.server_features)
83
95
 
84
96
  log_file = server_features.log_file
85
97
  log_level = server_features.log_level
98
+ # Update logger:
86
99
  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
100
+ # Update AgentLib Logger
101
+ update_agent_lib_log(log_level.to_s)
102
+ # Update CEFlogger:
103
+ unless server_features.security_logger.settings_blank?
104
+ cef_logger.build_logger(server_features.security_logger.log_level, server_features.security_logger.log_file)
105
+ end
106
+ # TODO: RUBY-99999 Update Bot-Blocker from server settings - check enable value.
107
+ # For now all protection rules are rebuild on Application update. Bot blocker uses the default
108
+ # enable from the base rule, and update it's mode on app settings update.
109
+ # Here we receive also bots for that rule.
110
+ unless settings_empty?(server_features.protect.enabled?)
111
+ @protect_state.enabled = server_features.protect.enabled?
112
+ store_in_config(%i[protect enable], server_features.protect.enabled?)
113
+ end
114
+ update_assess_server_features(server_features.assess)
90
115
  @last_server_update_ms = Contrast::Utils::Timer.now_ms
116
+ # update via response header. We receive header from TS with last update info, setting the
117
+ # next request's header with the same time will save needless update of settings if there
118
+ # are no new server features updates after the said time.
119
+ @server_settings_last_httpdate = header_last_update
120
+ rescue StandardError => e
121
+ logger.warn('The following error occurred from server update: ', e: e)
122
+ end
123
+
124
+ # Update AgentLib log level
125
+ def update_agent_lib_log new_log_level
126
+ agent_lib_log_level = Contrast::AgentLib::InterfaceBase::LOG_LEVEL[0] if new_log_level.empty?
127
+ agent_lib_log_level ||= Contrast::AgentLib::InterfaceBase::LOG_LEVEL.key(new_log_level.upcase)
128
+
129
+ # detect if the provided level is invalid and log if it is
130
+ # by default if we pass invalid log level - it will leave the last active
131
+ unless Contrast::AgentLib::InterfaceBase::LOG_LEVEL.value?(new_log_level.upcase)
132
+ cur_active = Contrast::AGENT_LIB.log_level
133
+ logger.debug('The provided level was invalid, so the logger stays to the last active: ',
134
+ active: cur_active,
135
+ provided_level: new_log_level)
136
+ end
137
+
138
+ Contrast::AGENT_LIB.change_log_options(true, agent_lib_log_level)
139
+ end
140
+
141
+ # Update Assess server features
142
+ #
143
+ # @param assess [Contrast::Agent::Reporting::Settings::AssessServerFeature]
144
+ def update_assess_server_features assess
145
+ return if settings_empty?(assess.enabled?)
146
+
147
+ @assess_state.enabled = assess.enabled?
148
+ store_in_config(%i[assess enable], assess.enabled?)
149
+ @assess_state.sampling_settings = assess.sampling
150
+
151
+ Contrast::Components::Sampling::Interface::CONFIG_VALUES.each do |field|
152
+ lookup_field = field == 'enable' ? :enabled : field.to_sym
153
+ store_in_config(Contrast::Components::Sampling::Interface::CANON_NAME.split('.') + [field.to_sym],
154
+ assess.sampling.send(lookup_field))
155
+ end
91
156
  end
92
157
 
93
158
  # @param settings_response [Contrast::Agent::Reporting::Response]
@@ -96,34 +161,54 @@ module Contrast
96
161
 
97
162
  @application_state.modes_by_id = app_settings.protect.protection_rules_to_settings_hash
98
163
  update_exclusion_matchers(app_settings.exclusions)
164
+ app_settings.protect.virtual_patches = app_settings.protect.virtual_patches unless
165
+ settings_empty?(app_settings.protect.virtual_patches)
99
166
  update_sensitive_data_policy(app_settings.sensitive_data_masking)
100
167
  @assess_state.disabled_assess_rules = app_settings.assess.disabled_rules
101
168
  new_session_id = app_settings.assess.session_id
102
169
  @assess_state.session_id = new_session_id if new_session_id && !new_session_id.blank?
103
170
  @last_app_update_ms = Contrast::Utils::Timer.now_ms
171
+ @app_settings_last_httpdate = header_last_update
104
172
  end
105
173
 
106
174
  # Wipe state to zero.
107
175
  def reset_state
108
176
  @protect_state = PROTECT_STATE_BASE.dup
109
- @assess_state = ASSESS_STATE_BASE.dup
177
+ update_assess_state
110
178
  @application_state = APPLICATION_STATE_BASE.dup
111
179
  @tainted_columns = {}
112
180
  @sensitive_data_masking = SENSITIVE_DATA_MASKING_BASE.dup
113
181
  @excluder = Contrast::Agent::Excluder.new
114
182
  end
115
183
 
184
+ # We save the session_id, reset and set it again if available.
185
+ # This done so that reporting between updates won't trigger argument error
186
+ # for missing session_id given one is already set and used with the first application
187
+ # create response received from TS.
188
+ #
189
+ # @return [Struct]
190
+ def update_assess_state
191
+ current_session_id = @assess_state&.session_id
192
+ @assess_state = ASSESS_STATE_BASE.dup
193
+ # There is application settings update for the session id if new is received.
194
+ # Here we make sure not to delete the already set one.
195
+ @assess_state&.session_id = current_session_id unless current_session_id&.empty?
196
+ @assess_state
197
+ end
198
+
116
199
  def build_protect_rules
117
200
  @protect_state.rules = {}
118
201
 
119
202
  # Rules. They add themselves on initialize.
120
203
  Contrast::Agent::Protect::Rule::BotBlocker.new
121
- Contrast::Agent::Protect::Rule::CmdInjection.new
204
+ cmdi = Contrast::Agent::Protect::Rule::CmdInjection.new
205
+ cmdi.sub_rules
122
206
  Contrast::Agent::Protect::Rule::Deserialization.new
123
- Contrast::Agent::Protect::Rule::HttpMethodTampering.new
124
207
  Contrast::Agent::Protect::Rule::NoSqli.new
125
- Contrast::Agent::Protect::Rule::PathTraversal.new
126
- Contrast::Agent::Protect::Rule::Sqli.new
208
+ path = Contrast::Agent::Protect::Rule::PathTraversal.new
209
+ path.sub_rules
210
+ sqli = Contrast::Agent::Protect::Rule::Sqli.new
211
+ sqli.sub_rules
127
212
  Contrast::Agent::Protect::Rule::UnsafeFileUpload.new
128
213
  Contrast::Agent::Protect::Rule::Xss.new
129
214
  Contrast::Agent::Protect::Rule::Xxe.new
@@ -174,6 +259,31 @@ module Contrast
174
259
 
175
260
  false
176
261
  end
262
+
263
+ # update via response header.
264
+ # Used to build the next request header.
265
+ #
266
+ # @return [String]
267
+ def header_last_update
268
+ Contrast::Agent.reporter.client.response_handler.last_server_modified
269
+ end
270
+
271
+ # Update the stored config values to ensure that we know about the correct values,
272
+ # and that the sources are correct for entries updated from the UI.
273
+ #
274
+ # @param parts [Array] the path to the setting in config
275
+ # @param value [String, Integer, Array, nil] the value for the configuration setting
276
+ def store_in_config parts, value
277
+ level = Contrast::CONFIG.config.loaded_config
278
+ parts[0...-1].each do |segment|
279
+ level[segment] ||= {}
280
+ level = level[segment]
281
+ end
282
+ return unless level.cs__is_a?(Hash)
283
+
284
+ level[parts[-1]] = value
285
+ Contrast::CONFIG.sources.set(parts.join('.'), Contrast::Components::Config::Sources::CONTRASTUI)
286
+ end
177
287
  end
178
288
  end
179
289
  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 [MESSAGE_FAIL, CONN_STATUS_MSG_FAILURE].include?(@config_status)
29
+
30
+ response_code = response.code.to_s
31
+ @config_status = response_code.start_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