contrast-agent 6.8.0 → 6.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c32847e196cdcf1540bd39373f9327a46e50cb1c4be6516f2ad5664696c0221
4
- data.tar.gz: 03b9bc37cf6b8fe3fd0662f120f6f24cc9faf868cf91c7fb698ccbcb52f15aac
3
+ metadata.gz: b850f63bce180f09f998f5363f58b01e9c69db61a2f98a31db97fb59b82564d7
4
+ data.tar.gz: 811072666998fb4daf0f49d2514d875b45c18d79d29398639862f1c2144aa930
5
5
  SHA512:
6
- metadata.gz: 204a13ebf2cc20d9302d55a1d2201cc0f8f2ba35b5bf1b3392f75c0636f8ab5224de54106af8349c16da111d86f2381e894d8925eccf2df1e46e4d04b99eb7be
7
- data.tar.gz: ac50424a98754b82bab6576b7205cdb3f6243f5dc6aa9c55f817f15cfba61e1f0858c27a66efd18457442f0b4f2e8ea42cdfcbecdd67c6941edcd86c77b11164
6
+ metadata.gz: ac0f4dcea0a62d6aa000659943f2b994a02940148fffdff2901ff4ff61d27fd257170ec4f48d0b1925d569dbc20de89a8866f76a17dd1c22aa15d9c80e4e9eb1
7
+ data.tar.gz: bd2490f015d1a5c8be5f0e7a0fc60e5acdc436b03e32420cbf19542a53b760c843cd84a17a0e7a8a3794ec69e3aa909e63fabdb32f4c32d5daa48ece261176aa
@@ -1,7 +1,6 @@
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/agent/assess/events/event_factory'
5
4
  require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
6
5
  require 'contrast/agent/excluder'
7
6
  require 'contrast/components/logger'
@@ -15,6 +14,7 @@ require 'contrast/agent/reporting/reporting_events/preflight_message'
15
14
  require 'contrast/agent/reporting/reporting_events/route_discovery'
16
15
  require 'contrast/agent/reporting/reporting_utilities/reporting_storage'
17
16
  require 'contrast/agent/reporting/reporting_utilities/build_preflight'
17
+ require 'contrast/utils/assess/event_limit_utils'
18
18
 
19
19
  module Contrast
20
20
  module Agent
@@ -1,8 +1,7 @@
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/agent/assess/events/event_factory'
5
- require 'contrast/agent/assess/events/source_event'
4
+ require 'contrast/agent/reporting/reporting_events/finding_event'
6
5
 
7
6
  module Contrast
8
7
  module Agent
@@ -25,7 +24,7 @@ module Contrast
25
24
  # the key used to accessed if from a map or nil if a type like
26
25
  # BODY
27
26
  def build_event event_data, source_type = nil, source_name = nil
28
- @event = Contrast::Agent::Assess::Events::EventFactory.build(event_data, source_type, source_name)
27
+ @event = Contrast::Agent::Reporting::FindingEvent.new(event_data, source_type, source_name)
29
28
  report_sources(event_data.tagged, @event)
30
29
  end
31
30
 
@@ -35,21 +34,22 @@ module Contrast
35
34
  # context's observed route
36
35
  #
37
36
  # @param tagged [Object] The Target of the Event
38
- # @param event [Contrast::Agent::Assess::Events::ContrastEvent]
37
+ # @param event [Contrast::Agent::Reporting::FindingEvent]
39
38
  def report_sources tagged, event
40
39
  return unless tagged && !tagged.to_s.empty?
41
- return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
42
40
  return unless event.source_type
43
41
  return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
44
42
 
45
- if current_request.observed_route.sources.any? do |source|
46
- source.type == event.source_type && source.name == event.source_name # rubocop:disable Security/Module/Name
47
- end
43
+ event.event_sources&.each do |event_source|
44
+ if current_request.observed_route.sources.any? do |source|
45
+ source.source_type == event_source.source_type && source.source_name == event_source.source_name
46
+ end
48
47
 
49
- return
50
- end
48
+ next
49
+ end
51
50
 
52
- current_request.observed_route.sources << event.event_source if event.event_source
51
+ current_request.observed_route.sources << event_source
52
+ end
53
53
  end
54
54
  end
55
55
  end
@@ -18,7 +18,6 @@ module Contrast
18
18
  # reporting / tracking
19
19
  require 'contrast/agent/assess/properties'
20
20
  require 'contrast/agent/assess/tag'
21
- require 'contrast/agent/assess/events/event_factory'
22
21
  end
23
22
  end
24
23
  end
@@ -82,7 +82,7 @@ module Contrast
82
82
  event_sources = finding.events.flat_map(&:event_sources)
83
83
  event_sources.each do |event_source|
84
84
  return false unless rule_input_exclusions.any? do |exclusion|
85
- input_match?(exclusion, event_source.type, event_source.name) # rubocop:disable Security/Module/Name
85
+ input_match?(exclusion, event_source.source_type, event_source.source_name)
86
86
  end
87
87
  end
88
88
 
@@ -15,7 +15,7 @@ require 'contrast/agent/request_handler'
15
15
  require 'contrast/agent/static_analysis'
16
16
  require 'contrast/agent/telemetry/events/startup_metrics_event'
17
17
  require 'contrast/utils/middleware_utils'
18
-
18
+ require 'contrast/utils/reporting/application_activity_batch_utils'
19
19
  require 'contrast/utils/timer'
20
20
 
21
21
  module Contrast
@@ -27,6 +27,7 @@ module Contrast
27
27
  include Contrast::Components::Logger::InstanceMethods
28
28
  include Contrast::Components::Scope::InstanceMethods
29
29
  include Contrast::Utils::MiddlewareUtils
30
+ include Contrast::Utils::Reporting::ApplicationActivityBatchUtils
30
31
 
31
32
  attr_reader :app
32
33
 
@@ -62,6 +63,7 @@ module Contrast
62
63
  # the Rack framework.
63
64
  def call env
64
65
  logger.trace_with_time('Elapsed time for Contrast::Agent::Middleware#call') do
66
+ ::Contrast::Agent::ThreadWatcher.check_before_start
65
67
  return app.call(env) unless ::Contrast::AGENT.enabled?
66
68
 
67
69
  Contrast::Agent.heapdump_util.start_thread!
@@ -173,13 +175,17 @@ module Contrast
173
175
  Contrast::Agent::FINDINGS.report_collected_findings unless Contrast::Agent::FINDINGS.collection.empty?
174
176
  # All protect rules, which are trigger but require response to be reported
175
177
  Contrast::Agent::EXPLOITS.report_recorded_exploits(context) unless Contrast::Agent::EXPLOITS.collection.empty?
178
+ # Process Worth Watching Inputs for v2 rules
179
+ Contrast::Agent.worth_watching_analyzer&.add_to_queue(context.agent_input_analysis)
176
180
 
177
181
  if Contrast::Agent.framework_manager.streaming?(env)
178
182
  context.reset_activity
179
183
  request_handler.stream_safe_postfilter
180
184
  else
181
185
  request_handler.ruleset.postfilter
182
- request_handler.report_activity
186
+ request_handler.report_observed_route
187
+ add_activity_to_batch(context.activity)
188
+ report_batch
183
189
  end
184
190
  end
185
191
  # unsuccessful attack
@@ -0,0 +1,116 @@
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/agent/worker_thread'
5
+ require 'contrast/agent/reporting/input_analysis/score_level'
6
+ require 'contrast/agent/reporting/reporting_events/application_activity'
7
+ require 'contrast/utils/input_classification_base'
8
+
9
+ module Contrast
10
+ module Agent
11
+ module Protect
12
+ # WorthWatchingInputAnalyzer Perform analysis of input tracing v2 worthwatching results in a
13
+ # separate thread, should only be run at the end of the request.
14
+ # Currently only includes: cmd_injection & sqli_injection rules
15
+ class WorthWatchingInputAnalyzer < WorkerThread
16
+ include Timeout
17
+ include Contrast::Agent::Protect::Rule::InputClassificationBase
18
+
19
+ QUEUE_SIZE = 1000.cs__freeze
20
+ AGENTLIB_TIMEOUT = 5.cs__freeze
21
+ # max size of inputs to evaluate
22
+ INPUT_BYTESIZE_THRESHOLD = 100_000.cs__freeze
23
+ REPORT_INTERVAL_SECOND = 30.cs__freeze
24
+
25
+ # Thread that will process all the InputAnalysisResults that have a score level of WORTHWATCHING and
26
+ # sends results to TeamServer
27
+ def start_thread!
28
+ return if running?
29
+
30
+ @_thread = Contrast::Agent::Thread.new do
31
+ logger.info('Starting Worth Watching Analyzer thread.')
32
+ loop do
33
+ sleep(REPORT_INTERVAL_SECOND)
34
+ next if queue.empty?
35
+
36
+ report = false
37
+ num_to_report = queue.length
38
+ activity = Contrast::Agent::Reporting::ApplicationActivity.new
39
+ num_to_report.times do
40
+ next unless (attack_result = eval_input(queue.pop))
41
+
42
+ activity.attach_defend(attack_result)
43
+ report = true
44
+ end
45
+ Contrast::Agent.reporter.send_event_immediately(activity) if report
46
+ rescue StandardError => e
47
+ logger.debug('Worth Watching Analyzer thread could not process result because of:', e)
48
+ end
49
+ end
50
+ end
51
+
52
+ # param Contrast::Agent::Reporting::InputAnalysis
53
+ def add_to_queue input_analysis
54
+ return if input_analysis&.results&.empty?
55
+
56
+ if queue.size >= QUEUE_SIZE
57
+ logger.debug('WorthWatching queue at max size, skip input_result')
58
+ return
59
+ end
60
+ input_analysis.results.select { |val| val.score_level == WORTHWATCHING }.
61
+ each do |ia_result|
62
+ queue << ia_result
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # @param ia_result Contrast::Agent::Reporting::InputAnalysisResult the WorthWatching InputAnalysisResult
69
+ # @return [Contrast::Agent::Reporting::InputAnalysisResult, nil] InputAnalysisResult updated Result or nil
70
+ def eval_input ia_result
71
+ return if ia_result.nil?
72
+
73
+ if ia_result.value.to_s.bytesize >= INPUT_BYTESIZE_THRESHOLD
74
+ logger.debug("Skip anaylsis: Input size is larger than #{ INPUT_BYTESIZE_THRESHOLD / 1024 }KB")
75
+ return
76
+ end
77
+
78
+ begin
79
+ input_eval = Timeout.timeout(AGENTLIB_TIMEOUT) do
80
+ Contrast::AGENT_LIB.eval_input(ia_result.value,
81
+ convert_input_type(ia_result.input_type),
82
+ Contrast::AGENT_LIB.rule_set[ia_result.rule_id],
83
+ Contrast::AGENT_LIB.eval_option[:NONE])
84
+ end
85
+ score = input_eval&.score || 0
86
+ return if score <= THRESHOLD
87
+
88
+ ia_result.score_level = DEFINITEATTACK
89
+ build_attack_result(ia_result)
90
+ rescue Timeout::Error => e
91
+ logger.warn('AgentLib timed out when processing WORTHWATCHING InputAnalysisResult', e, ia_result)
92
+ nil
93
+ end
94
+ end
95
+
96
+ # @param ia_result Contrast::Agent::Reporting::InputAnalysisResult the updated InputAnalysisResult
97
+ # with a score of :DEFINITEATTACK
98
+ # @return [Contrast::Agent::Reporting::AttackResult] the attack result from
99
+ # this input
100
+ def build_attack_result ia_result
101
+ Contrast::PROTECT.rule(ia_result.rule_id).build_attack_without_match(nil, ia_result, nil)
102
+ end
103
+
104
+ def queue
105
+ @_queue ||= Queue.new
106
+ end
107
+
108
+ def delete_queue!
109
+ @_queue&.clear
110
+ @_queue&.close
111
+ @_queue = nil
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -148,14 +148,14 @@ module Contrast
148
148
  # protect rule but did not exploit the application. As such, we need
149
149
  # to build a result to report this violation to TeamServer.
150
150
  #
151
- # @param context [Contrast::Agent::RequestContext] the context of the
151
+ # @param context [Contrast::Agent::RequestContext, nil] the context of the
152
152
  # request in which this input is evaluated.
153
153
  # @param ia_result [Contrast::Agent::Reporting::InputAnalysis] the
154
154
  # analysis of the input that was determined to be an attack
155
155
  # @param result [Contrast::Agent::Reporting::AttackResult, nil] previous
156
156
  # attack result for this rule, if one exists, in the case of
157
157
  # multiple inputs being found to violate the protection criteria
158
- # @param kwargs [Hash] key - value pairs of context individual rules
158
+ # @param kwargs [Hash, nil] key - value pairs of context individual rules
159
159
  # need to build out details to send to TeamServer to tell the
160
160
  # story of the attack
161
161
  # @return [Contrast::Agent::Reporting::AttackResult] the attack result from
@@ -66,7 +66,7 @@ module Contrast
66
66
  # @param ia_result [Contrast::Agent::Reporting::InputAnalysisResult]
67
67
  # @param _candidate_string
68
68
  # @param **_kwargs
69
- # @return [Contrast::Api::Dtm::RaspRuleSample]
69
+ # @return [Contrast::Agent::Reporting::RaspRuleSample]
70
70
  def build_sample context, ia_result, _candidate_string, **_kwargs
71
71
  sample = build_base_sample(context, ia_result)
72
72
  sample.details = Contrast::Agent::Reporting::BotBlockerDetails.new
@@ -21,24 +21,25 @@ module Contrast
21
21
  include Contrast::Components::Logger::InstanceMethods
22
22
  include Contrast::Agent::Reporting::InputType
23
23
  NAME = 'cmd-injection'
24
-
25
24
  APPLICABLE_USER_INPUTS = [
26
25
  BODY, COOKIE_VALUE, HEADER, PARAMETER_NAME,
27
26
  PARAMETER_VALUE, JSON_VALUE, MULTIPART_VALUE,
28
27
  MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE
29
28
  ].cs__freeze
30
- SUB_RULES = [
31
- Contrast::Agent::Protect::Rule::CmdiBackdoors.new,
32
- Contrast::Agent::Protect::Rule::CmdiChainedCommand.new,
33
- Contrast::Agent::Protect::Rule::CmdiDangerousPath.new
34
- ].cs__freeze
35
29
 
36
30
  def rule_name
37
31
  NAME
38
32
  end
39
33
 
34
+ # Array of sub_rules:
35
+ #
36
+ # @return [Array]
40
37
  def sub_rules
41
- SUB_RULES
38
+ @_sub_rules ||= [
39
+ Contrast::Agent::Protect::Rule::CmdiBackdoors.new,
40
+ Contrast::Agent::Protect::Rule::CmdiChainedCommand.new,
41
+ Contrast::Agent::Protect::Rule::CmdiDangerousPath.new
42
+ ].cs__freeze
42
43
  end
43
44
 
44
45
  def applicable_user_inputs
@@ -15,11 +15,6 @@ module Contrast
15
15
  class CmdiChainedCommand < Contrast::Agent::Protect::Rule::CmdiBaseRule
16
16
  NAME = 'cmd-injection-semantic-chained-commands'
17
17
 
18
- def initialize
19
- super
20
- @mode = :MONITOR
21
- end
22
-
23
18
  def rule_name
24
19
  NAME
25
20
  end
@@ -15,11 +15,6 @@ module Contrast
15
15
  class CmdiDangerousPath < Contrast::Agent::Protect::Rule::CmdiBaseRule
16
16
  NAME = 'cmd-injection-semantic-dangerous-paths'
17
17
 
18
- def initialize
19
- super
20
- @mode = :MONITOR
21
- end
22
-
23
18
  def rule_name
24
19
  NAME
25
20
  end
@@ -37,14 +37,15 @@ module Contrast
37
37
  /windows/repair/
38
38
  ].cs__freeze
39
39
 
40
- SUB_RULES = [Contrast::Agent::Protect::Rule::PathTraversalSemanticBypass.new].cs__freeze
41
-
42
40
  def rule_name
43
41
  NAME
44
42
  end
45
43
 
44
+ # Array of sub_rules
45
+ #
46
+ # @return [Array]
46
47
  def sub_rules
47
- SUB_RULES
48
+ @_sub_rules ||= [Contrast::Agent::Protect::Rule::PathTraversalSemanticBypass.new].cs__freeze
48
49
  end
49
50
 
50
51
  def applicable_user_inputs
@@ -26,8 +26,6 @@ module Contrast
26
26
 
27
27
  NAME = 'sql-injection'
28
28
 
29
- SUB_RULES = [Contrast::Agent::Protect::Rule::SqliDangerousFunctions.new].cs__freeze
30
-
31
29
  def rule_name
32
30
  NAME
33
31
  end
@@ -36,8 +34,11 @@ module Contrast
36
34
  BLOCK_MESSAGE
37
35
  end
38
36
 
37
+ # Array of sub_rules
38
+ #
39
+ # @return [Array]
39
40
  def sub_rules
40
- SUB_RULES
41
+ @_sub_rules ||= [Contrast::Agent::Protect::Rule::SqliDangerousFunctions.new].cs__freeze
41
42
  end
42
43
 
43
44
  def applicable_user_inputs
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/protect/rule/base_service'
5
+ require 'contrast/agent/reporting/input_analysis/input_type'
5
6
 
6
7
  module Contrast
7
8
  module Agent
@@ -28,7 +28,7 @@ module Contrast
28
28
  # @return [Contrast::Agent::Reporting::RaspRuleSample]
29
29
  def build context, ia_result
30
30
  sample = new
31
- sample.time_stamp = context&.timer&.start_ms
31
+ sample.time_stamp = context&.timer&.start_ms || Contrast::Utils::Timer.now_ms
32
32
  sample.user_input = build_user_input_from_ia(ia_result)
33
33
  sample.user_input.document_type = if context&.request
34
34
  Contrast::Utils::StringUtils.force_utf8(context.request.document_type)
@@ -26,5 +26,6 @@ require 'contrast/agent/reporting/reporting_events/observed_route'
26
26
  require 'contrast/agent/reporting/reporting_events/route_coverage'
27
27
  require 'contrast/agent/reporting/reporting_events/observed_library_usage'
28
28
  require 'contrast/agent/reporting/reporting_events/poll'
29
+ require 'contrast/agent/reporting/reporting_events/server_settings'
29
30
  # Sensitive data masking
30
31
  require 'contrast/agent/reporting/masker/masker'
@@ -5,6 +5,8 @@ require 'contrast/agent/worker_thread'
5
5
  require 'contrast/agent/reporting/report'
6
6
  require 'contrast/components/logger'
7
7
  require 'contrast/agent/reporting/reporting_events/agent_startup'
8
+ require 'contrast/agent/telemetry/events/exceptions/telemetry_exceptions'
9
+ require 'contrast/agent/telemetry/events/exceptions/obfuscate'
8
10
 
9
11
  module Contrast
10
12
  module Agent
@@ -13,6 +15,8 @@ module Contrast
13
15
  include Contrast::Components::Logger::InstanceMethods
14
16
  include Contrast::Utils::ObjectShare
15
17
 
18
+ MAX_QUEUE_SIZE = 1000
19
+
16
20
  class << self
17
21
  # check if we can report to TS
18
22
  #
@@ -68,6 +72,14 @@ module Contrast
68
72
  end
69
73
  return unless event
70
74
 
75
+ if queue.size >= MAX_QUEUE_SIZE
76
+ if Contrast::Agent::Telemetry::Base.enabled?
77
+ Contrast::Agent.thread_watcher.telemetry_queue.send_event(queue_limit_telemetry_event)
78
+ end
79
+
80
+ return
81
+ end
82
+
71
83
  queue << event
72
84
  end
73
85
 
@@ -126,6 +138,28 @@ module Contrast
126
138
  rescue StandardError => e
127
139
  logger.error('Could not send message to TeamServer from Reporter queue.', e)
128
140
  end
141
+
142
+ # @return [Contrast::Agent::Telemetry::TelemetryException::Event]
143
+ def queue_limit_telemetry_event
144
+ message_exception = Contrast::Agent::Telemetry::TelemetryException::MessageException.build(
145
+ 'String',
146
+ "Maximum queue size (#{ MAX_QUEUE_SIZE }) reached for reporting events", nil, stack_frame)
147
+ message = Contrast::Agent::Telemetry::TelemetryException::Message.build({}, [message_exception])
148
+ Contrast::Agent::Telemetry::TelemetryException::Event.new(message)
149
+ end
150
+
151
+ def stack_frame
152
+ stack_trace = caller_locations(20, 20)
153
+ stack_frame_type = if stack_trace.nil? || stack_trace[1].nil?
154
+ 'none'
155
+ else
156
+ Contrast::Agent::Telemetry::TelemetryException::Obfuscate.obfuscate_type(
157
+ stack_trace[1].path.delete_prefix(Dir.pwd))
158
+ end
159
+
160
+ stack_frame_function = stack_trace.nil? || stack_trace[1].nil? ? 'none' : stack_trace[1].label
161
+ Contrast::Agent::Telemetry::TelemetryException::StackFrame.build(stack_frame_function, stack_frame_type, nil)
162
+ end
129
163
  end
130
164
  end
131
165
  end
@@ -1,18 +1,16 @@
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/agent/worker_thread'
5
- require 'contrast/agent/reporting/report'
4
+ require 'contrast/agent/reporting/reporter'
6
5
  require 'contrast/agent/inventory/dependency_usage_analysis'
7
6
  require 'contrast/agent/reporting/reporting_events/poll'
8
- require 'contrast/agent/reporting/reporting_events/server_activity'
9
7
 
10
8
  module Contrast
11
9
  module Agent
12
10
  # The ReporterHeartbeat will make sure that the process remains marked alive by TeamServer and that we periodically
13
11
  # reach out to get the latest settings for this application. It also sends out those messages which do not need to
14
12
  # be associated directly with a request, such as Server Activity and Library Observation.
15
- class ReporterHeartbeat < WorkerThread
13
+ class ReporterHeartbeat < Reporter
16
14
  # TeamServer will mark an application offline after 5 minutes. Sending this every one should be more than enough
17
15
  # to satisfy our goals.
18
16
  REFRESH_INTERVAL_SEC = 60
@@ -42,11 +40,7 @@ module Contrast
42
40
  #
43
41
  # @return [Array<Contrast::Agent::Reporting::ReportingEvent>]
44
42
  def polling_events
45
- [
46
- Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage,
47
- Contrast::Agent::Reporting::ServerActivity.new,
48
- poll_message
49
- ].compact
43
+ [Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage, poll_message].compact
50
44
  end
51
45
  end
52
46
  end
@@ -110,7 +110,7 @@ module Contrast
110
110
 
111
111
  # This is primary used for attaching new inventory reporting
112
112
  #
113
- # @param architecture [Contrast::Agent::Reporting::AttackResult]
113
+ # @param architecture [Contrast::Agent::Reporting::ArchitectureComponent]
114
114
  def attach_inventory architecture
115
115
  inventory.attach_data(architecture)
116
116
  end
@@ -28,18 +28,23 @@ module Contrast
28
28
  def attach_data attack_result
29
29
  attacker_activity = Contrast::Agent::Reporting::ApplicationDefendAttackerActivity.new
30
30
  attacker_activity.attach_data(attack_result)
31
- existing_attacker_activity = attackers.find do |existing|
32
- existing.source_forwarded_for == attacker_activity.source_forwarded_for &&
33
- existing.source_ip == attacker_activity.source_ip
34
- end
35
- rule = attack_result.rule_id
36
- if existing_attacker_activity
37
- attach_existing(existing_attacker_activity, attacker_activity, rule)
31
+
32
+ if (existing_attacker_activity = find_existing_attacker_activity(attacker_activity))
33
+ attach_existing(existing_attacker_activity, attacker_activity, attack_result.rule_id)
38
34
  else
39
35
  attackers << attacker_activity
40
36
  end
41
37
  end
42
38
 
39
+ # Find an existing attacker if it matches on source details
40
+ # @param attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
41
+ def find_existing_attacker_activity new_attacker_activity
42
+ attackers.find do |existing|
43
+ existing.source_forwarded_for == new_attacker_activity.source_forwarded_for &&
44
+ existing.source_ip == new_attacker_activity.source_ip
45
+ end
46
+ end
47
+
43
48
  # @param existing_attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
44
49
  # @param attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
45
50
  # @param rule [String]
@@ -13,7 +13,7 @@ module Contrast
13
13
  # about the inventory of the application which was discovered during exercise of the application
14
14
  # during this activity period.
15
15
  class ApplicationInventoryActivity < Contrast::Agent::Reporting::ApplicationReportingEvent
16
- # return [Contrast::Agent::Reporting::ArchitectureComponent]
16
+ # return [Array<Contrast::Agent::Reporting::ArchitectureComponent>]
17
17
  attr_reader :components
18
18
  # @ return [Array<String>, nil] - User-Agent Header value
19
19
  attr_reader :browsers
@@ -46,6 +46,11 @@ module Contrast
46
46
  def duration
47
47
  Contrast::Utils::Timer.now_ms - (Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0)
48
48
  end
49
+
50
+ # Helper method to determine if InventoryActivity has no data
51
+ def empty?
52
+ @browsers.empty? && @components.empty?
53
+ end
49
54
  end
50
55
  end
51
56
  end
@@ -88,8 +88,8 @@ module Contrast
88
88
  event_messages = Contrast::Agent::Reporting::FindingEvent.from_source(source)
89
89
  events.concat(event_messages) if event_messages&.any?
90
90
  event_data = Contrast::Agent::Assess::Events::EventData.new(trigger_node, source, object, ret, args)
91
- contrast_event = Contrast::Agent::Assess::ContrastEvent.new(event_data)
92
- events << Contrast::Agent::Reporting::FindingEvent.convert(contrast_event)
91
+ contrast_event = Contrast::Agent::Reporting::FindingEvent.new(event_data)
92
+ events << contrast_event
93
93
  return unless request
94
94
 
95
95
  @request = Contrast::Agent::Reporting::FindingRequest.convert(request)