contrast-agent 6.1.2 → 6.2.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/at_exit_hook.rb +2 -1
  3. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +9 -5
  4. data/lib/contrast/agent/protect/rule/xss.rb +4 -0
  5. data/lib/contrast/agent/reporting/reporter.rb +2 -11
  6. data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +3 -18
  7. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +75 -15
  8. data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
  9. data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +5 -19
  10. data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +6 -22
  11. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +1 -1
  12. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -3
  13. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +1 -3
  14. data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +9 -0
  15. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -2
  16. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -10
  17. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +0 -1
  18. data/lib/contrast/agent/reporting/reporting_utilities/response.rb +60 -2
  19. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +32 -10
  20. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
  21. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +58 -26
  22. data/lib/contrast/agent/reporting/settings/application_settings.rb +8 -23
  23. data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +27 -33
  24. data/lib/contrast/agent/reporting/settings/bot_blocker.rb +68 -0
  25. data/lib/contrast/agent/reporting/settings/code_exclusion.rb +27 -0
  26. data/lib/contrast/agent/reporting/settings/exclusion_base.rb +33 -0
  27. data/lib/contrast/agent/reporting/settings/exclusions.rb +39 -57
  28. data/lib/contrast/agent/reporting/settings/helpers.rb +56 -0
  29. data/lib/contrast/agent/reporting/settings/input_exclusion.rb +37 -0
  30. data/lib/contrast/agent/reporting/settings/ip_filter.rb +35 -0
  31. data/lib/contrast/agent/reporting/settings/keyword.rb +74 -0
  32. data/lib/contrast/agent/reporting/settings/log_enhancer.rb +65 -0
  33. data/lib/contrast/agent/reporting/settings/protect.rb +4 -2
  34. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +62 -115
  35. data/lib/contrast/agent/reporting/settings/reaction.rb +11 -2
  36. data/lib/contrast/agent/reporting/settings/rule_definition.rb +63 -0
  37. data/lib/contrast/agent/reporting/settings/sampling.rb +10 -0
  38. data/lib/contrast/agent/reporting/settings/sanitizer.rb +38 -0
  39. data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +9 -1
  40. data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +7 -0
  41. data/lib/contrast/agent/reporting/settings/server_features.rb +8 -0
  42. data/lib/contrast/agent/reporting/settings/syslog.rb +176 -0
  43. data/lib/contrast/agent/reporting/settings/url_exclusion.rb +42 -0
  44. data/lib/contrast/agent/reporting/settings/validator.rb +17 -0
  45. data/lib/contrast/agent/request_context.rb +4 -0
  46. data/lib/contrast/agent/request_handler.rb +8 -4
  47. data/lib/contrast/agent/static_analysis.rb +4 -8
  48. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +1 -1
  49. data/lib/contrast/agent/thread_watcher.rb +4 -5
  50. data/lib/contrast/agent/version.rb +1 -1
  51. data/lib/contrast/agent.rb +1 -3
  52. data/lib/contrast/api/decorators/application_update.rb +0 -8
  53. data/lib/contrast/api/decorators.rb +0 -1
  54. data/lib/contrast/framework/base_support.rb +5 -4
  55. data/lib/contrast/framework/grape/support.rb +6 -6
  56. data/lib/contrast/framework/manager.rb +2 -4
  57. data/lib/contrast/framework/manager_extend.rb +1 -0
  58. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +2 -1
  59. data/lib/contrast/framework/rails/support.rb +9 -2
  60. data/lib/contrast/framework/sinatra/support.rb +3 -2
  61. data/lib/contrast/logger/aliased_logging.rb +31 -26
  62. data/lib/contrast/utils/response_utils.rb +14 -1
  63. data/lib/contrast/utils/telemetry.rb +9 -0
  64. data/lib/contrast/utils/telemetry_hash.rb +36 -12
  65. data/lib/contrast/utils/telemetry_identifier.rb +8 -0
  66. data/lib/contrast/utils/thread_tracker.rb +26 -9
  67. data/lib/contrast/utils/timer.rb +6 -1
  68. data/lib/contrast.rb +1 -3
  69. metadata +26 -14
  70. data/lib/contrast/api/decorators/library_usage_update.rb +0 -31
@@ -0,0 +1,176 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/object_share'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Reporting
9
+ module Settings
10
+ # Controls for the syslogging feature in the agent
11
+ class Syslog
12
+ CONNECTION_TYPE = %w[UNENCRYPTED ENCRYPTED].cs__freeze
13
+ # Used for:
14
+ # severity_blocked, severity_blocked_perimeter, severity_exploited, severity_probed,
15
+ # severity_probed_perimeter
16
+ SEVERITIES = %w[ALERT CRITICAL ERROR WARNING NOTICE INFO DEBUG].cs__freeze
17
+ SYSLOG_METHODS = %i[
18
+ enable= ip= port= facility= protocol= connection_type= severity_exploited= severity_blocked=
19
+ severity_probed= severity_probed_suspicious= severity_blocked_perimeter= severity_probed_perimeter=
20
+ ].cs__freeze
21
+ SYSLOG_RESPONSE_KEYS = %i[
22
+ syslogEnabled syslogIpAddress syslogPortNumber syslogFacilityCode syslogProtocol
23
+ syslogConnectionType syslogSeverityExploited syslogSeverityBlocked syslogSeverityProbed
24
+ syslogSeveritySuspicious syslogSeverityBlockedPerimeter syslogSeverityProbedPerimeter
25
+ ].cs__freeze
26
+
27
+ # @return enable [Boolean]
28
+ attr_accessor :enable
29
+ # @return ip [Integer]
30
+ attr_accessor :ip
31
+ # @return port [Integer]
32
+ attr_accessor :port
33
+ # @return facility [Integer]
34
+ attr_accessor :facility
35
+ # @return protocol [String]
36
+ attr_accessor :protocol
37
+
38
+ def initialize
39
+ @enable = false
40
+ @ip = Contrast::Utils::ObjectShare::EMPTY_STRING
41
+ @port = 0
42
+ @facility = 0
43
+ end
44
+
45
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
46
+ def connection_type
47
+ @_connection_type ||= Contrast::Utils::ObjectShare::EMPTY_STRING
48
+ end
49
+
50
+ # Set the connection type
51
+ #
52
+ # @param type [String, Symbol] one of UNENCRYPTED, ENCRYPTED
53
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
54
+ def connection_type= type
55
+ @_connection_type = type if valid_entry?(type, CONNECTION_TYPE)
56
+ end
57
+
58
+ # @return severity_blocked [String]
59
+ def severity_blocked
60
+ @_severity_blocked ||= Contrast::Utils::ObjectShare::EMPTY_STRING
61
+ end
62
+
63
+ # Set the severity type
64
+ #
65
+ # @param severity [String, Symbol] one of UNENCRYPTED, ENCRYPTED
66
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
67
+ def severity_blocked= severity
68
+ @_severity_blocked = severity if valid_entry?(severity, SEVERITIES)
69
+ end
70
+
71
+ # @return severity_blocked [String]
72
+ def severity_blocked_perimeter
73
+ @_severity_blocked_perimeter ||= Contrast::Utils::ObjectShare::EMPTY_STRING
74
+ end
75
+
76
+ # Set the severity type
77
+ #
78
+ # @param severity [String, Symbol] one of UNENCRYPTED, ENCRYPTED
79
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
80
+ def severity_blocked_perimeter= severity
81
+ @_severity_blocked_perimeter = severity if valid_entry?(severity, SEVERITIES)
82
+ end
83
+
84
+ # @return severity_blocked [String]
85
+ def severity_exploited
86
+ @_severity_exploited ||= Contrast::Utils::ObjectShare::EMPTY_STRING
87
+ end
88
+
89
+ # Set the severity type
90
+ #
91
+ # @param severity [String, Symbol] one of UNENCRYPTED, ENCRYPTED
92
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
93
+ def severity_exploited= severity
94
+ @_severity_exploited = severity if valid_entry?(severity, SEVERITIES)
95
+ end
96
+
97
+ # @return severity_blocked [String]
98
+ def severity_probed
99
+ @_severity_probed ||= Contrast::Utils::ObjectShare::EMPTY_STRING
100
+ end
101
+
102
+ # Set the severity type
103
+ #
104
+ # @param severity [String, Symbol] one of UNENCRYPTED, ENCRYPTED
105
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
106
+ def severity_probed= severity
107
+ @_severity_probed = severity if valid_entry?(severity, SEVERITIES)
108
+ end
109
+
110
+ # @return severity_blocked [String]
111
+ def severity_probed_perimeter
112
+ @_severity_probed_perimeter ||= Contrast::Utils::ObjectShare::EMPTY_STRING
113
+ end
114
+
115
+ # Set the severity type
116
+ #
117
+ # @param severity [String, Symbol] one of UNENCRYPTED, ENCRYPTED
118
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
119
+ def severity_probed_perimeter= severity
120
+ @_severity_probed_perimeter = severity if valid_entry?(severity, SEVERITIES)
121
+ end
122
+
123
+ # @return severity_blocked [String]
124
+ def severity_probed_suspicious
125
+ @_severity_probed_suspicious ||= Contrast::Utils::ObjectShare::EMPTY_STRING
126
+ end
127
+
128
+ # Set the severity type
129
+ #
130
+ # @param severity [String, Symbol] one of UNENCRYPTED, ENCRYPTED
131
+ # @return connection_type [String] one of UNENCRYPTED, ENCRYPTED
132
+ def severity_probed_suspicious= severity
133
+ @_severity_probed_suspicious = severity if valid_entry?(severity, SEVERITIES)
134
+ end
135
+
136
+ # @param settings_array [Array] Settings retrieved from response
137
+ def assign_array settings_array
138
+ Contrast::Agent::Reporting::Settings::Syslog::SYSLOG_METHODS.each_with_index do |method, index|
139
+ send(method, settings_array[SYSLOG_RESPONSE_KEYS[index]])
140
+ end
141
+ end
142
+
143
+ def to_controlled_hash
144
+ {
145
+ syslogEnabled: enable,
146
+ syslogIpAddress: ip,
147
+ syslogPortNumber: port,
148
+ syslogFacilityCode: facility,
149
+ syslogConnectionType: connection_type,
150
+ syslogProtocol: protocol,
151
+ syslogSeverityExploited: severity_exploited,
152
+ syslogSeverityBlocked: severity_blocked,
153
+ syslogSeverityProbed: severity_probed,
154
+ syslogSeveritySuspicious: severity_probed_suspicious,
155
+ syslogSeverityBlockedPerimeter: severity_blocked_perimeter,
156
+ syslogSeverityProbedPerimeter: severity_probed_perimeter
157
+ }
158
+ end
159
+
160
+ private
161
+
162
+ # Gets String or Symbol value and assigns it to iv after
163
+ # validation with allowed types.
164
+ #
165
+ # @param value [String, Symbol] value to write
166
+ # @param validation_hash [Hash] to validate against
167
+ def valid_entry? value, validation_hash
168
+ return false unless value && validation_hash
169
+
170
+ validation_hash.include?(value)
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,42 @@
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/reporting/settings/exclusion_base'
5
+ require 'contrast/utils/object_share'
6
+
7
+ module Contrast
8
+ module Agent
9
+ module Reporting
10
+ module Settings
11
+ # UrlExclusions class
12
+ class UrlExclusion < ExclusionBase
13
+ ATTRIBUTES = BASE_ATTRIBUTES.dup << :urls
14
+ ATTRIBUTES << :match_strategy
15
+ ATTRIBUTES.cs__freeze
16
+ STRATEGIES = %w[ALL ONLY].cs__freeze
17
+
18
+ # @return urls [Array<String>]
19
+ attr_accessor :urls
20
+
21
+ # @return match_strategy [String] The type of the input
22
+ def match_strategy
23
+ @_match_strategy ||= Contrast::Utils::ObjectShare::EMPTY_STRING
24
+ end
25
+
26
+ # @param new_strategy [String] Set new input type.
27
+ # @return type [String] The type of the input.
28
+ def match_strategy= new_strategy
29
+ @_match_strategy = new_strategy if STRATEGIES.include?(new_strategy)
30
+ end
31
+
32
+ def to_controlled_hash
33
+ hash = super
34
+ hash[:urls] = urls
35
+ hash[:matchStrategy] = match_strategy
36
+ hash
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
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/reporting/settings/sanitizer'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Reporting
9
+ module Settings
10
+ # The validators defined by the user for use by the agent on this server for this organization.
11
+ class Validator < Sanitizer
12
+ # This uses the same fields as Sanitizer.
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -46,6 +46,9 @@ module Contrast
46
46
  attr_reader :speedracer_input_analysis
47
47
  # @return [Contrast::Utils::Timer] when the context was created
48
48
  attr_reader :timer
49
+ # @return [Contrast::Agent::Reporting::ObservedLibraryUsage] List of the libraries that have been observed to be
50
+ # executed or to have something loaded. This here replaces part of the Activity Dtm
51
+ attr_reader :observed_library_usage
49
52
 
50
53
  def initialize rack_request, app_loaded: true
51
54
  with_contrast_scope do
@@ -60,6 +63,7 @@ module Contrast
60
63
  @activity.http_request = request.dtm
61
64
 
62
65
  @server_activity = Contrast::Api::Dtm::ServerActivity.new
66
+ @observed_library_usage = Contrast::Agent::Reporting::ObservedLibraryUsage.new
63
67
 
64
68
  # build analyzer
65
69
  @do_not_track = false
@@ -34,7 +34,6 @@ module Contrast
34
34
  def send_activity_messages
35
35
  events = [context.activity]
36
36
  unless Contrast::Agent::Reporter.enabled?
37
- Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage(context.activity)
38
37
  events << context.server_activity
39
38
  events << context.observed_route
40
39
  end
@@ -48,6 +47,10 @@ module Contrast
48
47
  # more endpoints over, this method will take the messages originally sent by #send_actiivty_messages. At the end,
49
48
  # that method should be removed.
50
49
  def report_activity # rubocop:disable Metrics/AbcSize
50
+ Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.
51
+ generate_library_usage(context.observed_library_usage)
52
+
53
+ Contrast::Agent.reporter.send_event(context.observed_library_usage)
51
54
  return unless Contrast::Agent::Reporter.enabled?
52
55
 
53
56
  reporter = Contrast::Agent.reporter
@@ -56,18 +59,19 @@ module Contrast
56
59
  # Mask Sensitive Data
57
60
  Contrast::Agent::Reporting::Masker.mask(context.activity)
58
61
 
59
- Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage(context.activity)
62
+ Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.
63
+ generate_library_usage(context.observed_library_usage)
60
64
  [
61
65
  context.new_observed_route,
66
+ context.observed_library_usage,
62
67
  Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
63
- Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity.library_usages),
64
68
  Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
65
69
  ].each do |event|
66
70
  reporter.send_event(event)
67
71
  rescue StandardError => e
68
72
  logger.warn('Unable to send Event Activity', e)
69
73
  end
70
- context.activity.library_usages.clear # TODO: RUBY-1355 remove when no longer using activity
74
+ context.observed_library_usage.clear # TODO: RUBY-1355 remove when no longer using activity
71
75
  end
72
76
 
73
77
  # If the response is streaming, we should only perform filtering on our stream safe rules
@@ -24,24 +24,20 @@ module Contrast
24
24
  end
25
25
  end
26
26
 
27
- # TODO: RUBY-1356
27
+ # TODO: RUBY-1703
28
28
  def send_inventory_message
29
29
  return unless ::Contrast::INVENTORY.enabled?
30
30
 
31
31
  app_update_msg = Contrast::Api::Dtm::ApplicationUpdate.build
32
-
33
32
  Contrast::Agent::Inventory::DatabaseConfig.append_db_config(app_update_msg)
34
- # TODO: RUBY-1438 -- remove and build ReportingEvent directly
35
- if Contrast::Agent.reporter
33
+ # TODO: RUBY-1703 -- remove and build ReportingEvent directly
34
+ if Contrast::Agent::Reporter.enabled?
36
35
  report = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(app_update_msg)
37
36
  Contrast::Agent.reporter.send_event(report)
38
-
39
- # This is being reported separately because we extract the data from the same message
40
- inventory_report = Contrast::Agent::Reporting::ApplicationInventory.convert(app_update_msg)
41
- Contrast::Agent.reporter.send_event(inventory_report)
42
37
  else
43
38
  Contrast::Agent.messaging_queue.send_event_eventually(app_update_msg, force: true)
44
39
  end
40
+ Contrast::Agent.reporter.send_event(Contrast::Agent::Reporting::ApplicationInventory.new)
45
41
  end
46
42
 
47
43
  private
@@ -14,7 +14,7 @@ module Contrast
14
14
  def push_exceptions
15
15
  return unless Contrast::TELEMETRY_EXCEPTIONS&.any?
16
16
 
17
- Contrast::TELEMETRY_EXCEPTIONS.values.each_slice(256) { |tuple| error_messages.push(tuple) }
17
+ Contrast::TELEMETRY_EXCEPTIONS.each_value { |value| error_messages.push(value) }
18
18
  # Clear the hash. All exceptions now live in @_error_messages instance variable. and we will
19
19
  # add them to the queue. Clearing would make the hash available to be populated again while the
20
20
  # sending is proceeding.
@@ -32,10 +32,8 @@ module Contrast
32
32
  @heartbeat = Contrast::Agent::ServiceHeartbeat.new
33
33
  @messaging_queue = Contrast::Api::Communication::MessagingQueue.new
34
34
  end
35
- if Contrast::Agent::Reporter.enabled?
36
- @reporter = Contrast::Agent::Reporter.new
37
- @reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new
38
- end
35
+ @reporter = Contrast::Agent::Reporter.new
36
+ @reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new if Contrast::Agent::Reporter.enabled?
39
37
  @telemetry = Contrast::Agent::Telemetry::Base.new if Contrast::Agent::Telemetry::Base.enabled?
40
38
  end
41
39
 
@@ -51,9 +49,10 @@ module Contrast
51
49
  telemetry_status = init_thread(telemetry_queue)
52
50
  @pids[Process.pid] = @pids[Process.pid] && telemetry_status
53
51
  end
52
+ reporter_status = init_thread(reporter)
53
+
54
54
  return @pids unless Contrast::Agent::Reporter.enabled?
55
55
 
56
- reporter_status = init_thread(reporter)
57
56
  reporter_heartbeat_status = init_thread(reporter_heartbeat)
58
57
  @pids[Process.pid] = @pids[Process.pid] && reporter_status && reporter_heartbeat_status
59
58
  @pids
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '6.1.2'
6
+ VERSION = '6.2.0'
7
7
  end
8
8
  end
@@ -76,10 +76,8 @@ module Contrast
76
76
  thread_watcher.telemetry_queue
77
77
  end
78
78
 
79
- # @return [nil, Contrast::Agent::Reporter]
79
+ # @return [Contrast::Agent::Reporter]
80
80
  def self.reporter
81
- return unless thread_watcher.reporter
82
-
83
81
  thread_watcher.reporter
84
82
  end
85
83
 
@@ -20,13 +20,6 @@ module Contrast
20
20
  end
21
21
  end
22
22
 
23
- # TS only allows you to report 500 routes per application
24
- def append_route_coverage_data route_coverage_dtms
25
- route_coverage_dtms.take(500).each do |route_coverage_dtm|
26
- routes << route_coverage_dtm
27
- end
28
- end
29
-
30
23
  def append_platform_version platform_version
31
24
  self.platform = Contrast::Api::Dtm::Platform.new if platform.nil?
32
25
  platform.major = platform_version.major
@@ -38,7 +31,6 @@ module Contrast
38
31
  module ClassMethods
39
32
  def build
40
33
  msg = new
41
- msg.append_route_coverage_data(Contrast::Agent.framework_manager.find_route_discovery_data)
42
34
  msg.append_platform_version(Contrast::Agent.framework_manager.platform_version)
43
35
  msg.append_library_update(Contrast::Agent::Inventory::DependencyAnalysis.instance.library_pb_list)
44
36
  msg
@@ -18,7 +18,6 @@ require 'contrast/api/decorators/input_analysis'
18
18
  require 'contrast/api/decorators/application_settings'
19
19
  require 'contrast/api/decorators/server_features'
20
20
  require 'contrast/api/decorators/library'
21
- require 'contrast/api/decorators/library_usage_update'
22
21
  require 'contrast/api/decorators/route_coverage'
23
22
  require 'contrast/api/decorators/trace_event_object'
24
23
  require 'contrast/api/decorators/trace_event_signature'
@@ -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/agent/reporting/reporting_events/discovered_route'
5
+
4
6
  module Contrast
5
7
  module Framework
6
8
  # The API for all subclasses to implement to correctly support a given framework
@@ -22,10 +24,9 @@ module Contrast
22
24
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
23
25
  end
24
26
 
25
- # Find all the predefined routes for this application and append them to the
26
- # provided inventory message
27
- # msg should be a Contrast::Api::Dtm::ApplicationUpdate or some other msg
28
- # that has a routes array consisting of Contrast::Api::Dtm::RouteCoverage
27
+ # Find all the predefined routes for this application
28
+ #
29
+ # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
29
30
  def collect_routes
30
31
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
31
32
  end
@@ -49,7 +49,7 @@ module Contrast
49
49
 
50
50
  # Find all classes that subclass ::Grape::API, Gather their routes
51
51
  #
52
- # @return [Array<Contrast::Api::Dtm::RouteCoverage>, Array]- founded routes as Dtms
52
+ # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
53
53
  def collect_routes
54
54
  return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless grape_defined?
55
55
 
@@ -60,8 +60,8 @@ module Contrast
60
60
  c&.endpoints&.each do |endpoint|
61
61
  endpoint&.routes&.map do |r|
62
62
  pattern = r.pattern.pattern
63
- temp = Contrast::Api::Dtm::RouteCoverage.from_grape_controller(c, r.request_method, pattern, r.path)
64
- routes << temp
63
+ routes << Contrast::Agent::Reporting::DiscoveredRoute.from_grape_controller(c, r.request_method,
64
+ pattern, r.path)
65
65
  end
66
66
  end
67
67
  end
@@ -140,8 +140,8 @@ module Contrast
140
140
  # @param method [::Rack::REQUEST_METHOD] GET, POST, PUT, etc...
141
141
  # @param route [String] the relative route passed from Rack.
142
142
  # @param controllers [Array<::Grape::API>] All Grape controllers found
143
- # @return [Array[::Grape::API]], nil] Either the controller that
144
- # will handle the route along with the route pattern or nil if no match.
143
+ # @return [Array[::Grape::API. Grape::Router::Route], nil] Either the controller that will handle the route
144
+ # along with the route pattern or nil if no match.
145
145
  def _route_recurse method, route, controllers = grape_controllers
146
146
  # return if there aren't any controllers
147
147
  return unless controllers&.any?
@@ -155,7 +155,7 @@ module Contrast
155
155
 
156
156
  contr_routes = controller.endpoints&.map(&:routes)&.flatten || []
157
157
  route_pattern = contr_routes&.find do |r|
158
- r.pattern.to_regexp.match(route) # ::Mustermann::Grape match
158
+ r.pattern.to_regexp.match(route) # Grape::Router::Route match
159
159
  end
160
160
  return controller, route_pattern unless route_pattern.nil?
161
161
 
@@ -59,6 +59,7 @@ module Contrast
59
59
  patches
60
60
  end
61
61
 
62
+ # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
62
63
  def find_route_discovery_data
63
64
  routes_for_all_frameworks
64
65
  end
@@ -162,13 +163,10 @@ module Contrast
162
163
  if Contrast::Agent.reporter
163
164
  report = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(app_update_msg)
164
165
  Contrast::Agent.reporter.send_event(report)
165
-
166
- # This is being reported separately because we extract the data from the same message
167
- inventory_report = Contrast::Agent::Reporting::ApplicationInventory.convert(app_update_msg)
168
- Contrast::Agent.reporter.send_event(inventory_report)
169
166
  else
170
167
  Contrast::Agent.messaging_queue.send_event_eventually(app_update_msg)
171
168
  end
169
+ Contrast::Agent.reporter.send_event(Contrast::Agent::Reporting::ApplicationInventory.new)
172
170
  logger.info('Framework detected after initialization. Enabling support.',
173
171
  framework: framework.detection_class,
174
172
  frameworks: @_frameworks)
@@ -20,6 +20,7 @@ module Contrast
20
20
  Contrast::Utils::ClassUtil.truly_defined?(klass)
21
21
  end
22
22
 
23
+ # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
23
24
  def routes_for_all_frameworks
24
25
  data_for_all_frameworks(:collect_routes)
25
26
  end
@@ -19,13 +19,14 @@ module Contrast
19
19
  if Contrast::Agent::Reporter.enabled?
20
20
  [
21
21
  context.new_observed_route,
22
+ context.observed_library_usage,
22
23
  Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
23
- Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity.library_usages),
24
24
  Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
25
25
  ].each do |event|
26
26
  Contrast::Agent.reporter&.send_event_immediately(event)
27
27
  end
28
28
  else
29
+ Contrast::Agent.reporter.send_event_immediately(context.observed_library_usage)
29
30
  [context.server_activity, context.activity, context.observed_route].each do |msg|
30
31
  Contrast::Agent.messaging_queue&.send_event_immediately(msg)
31
32
  end
@@ -44,6 +44,7 @@ module Contrast
44
44
  'rails'
45
45
  end
46
46
 
47
+ # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
47
48
  def collect_routes
48
49
  find_all_routes(::Rails.application, [])
49
50
  end
@@ -138,7 +139,8 @@ module Contrast
138
139
  route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) && route.app.app < ::Rails::Engine
139
140
  end
140
141
 
141
- # Recursively get final route traversing engines as required.
142
+ # Recursively get final route traversing engines as required. Because this can only be called once, we store
143
+ # this match for the duration of our request context.
142
144
  #
143
145
  # @param request [::Rack::Request] the rack request as will be handed to rails controller.
144
146
  # @param top_router [::ActionDispatch::Journey::Router] the current router relative to the previous.
@@ -162,12 +164,17 @@ module Contrast
162
164
  end
163
165
 
164
166
  # Rails engine routes need to be detected by inspecting Engine class route set
167
+ #
168
+ # @param app [Rails::Application]
169
+ # @param route_list [Array<Contrast::Agent::Reporting::DiscoveredRoute>] the list of discovered routes to
170
+ # which to append and return
171
+ # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
165
172
  def find_all_routes app, route_list
166
173
  return route_list unless app.cs__respond_to?(:routes) && app.routes.cs__respond_to?(:routes)
167
174
 
168
175
  app.routes.routes.each do |route|
169
176
  if route.cs__respond_to?(:app) && route.app.cs__class == ActionDispatch::Routing::RouteSet::Dispatcher
170
- route_list << Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(route)
177
+ route_list << Contrast::Agent::Reporting::DiscoveredRoute.from_action_dispatch_journey(route)
171
178
  elsif route.app.app.cs__respond_to?(:routes)
172
179
  route_list += find_all_routes(route.app.app, [])
173
180
  end
@@ -47,7 +47,7 @@ module Contrast
47
47
 
48
48
  # Find all classes that subclass ::Sinatra::Base. Gather their routes.
49
49
  #
50
- # @return [Array<Contrast::Api::Dtm::RouteCoverage>] the routes found as Dtms.
50
+ # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
51
51
  def collect_routes
52
52
  return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless defined?(::Sinatra) && defined?(::Sinatra::Base)
53
53
 
@@ -56,7 +56,8 @@ module Contrast
56
56
  controller.routes.each_pair do |method, route_triplets|
57
57
  # Sinatra stores its routes as a triplet: [Mustermann::Sinatra, [], Proc]
58
58
  route_triplets.map(&:first).each do |route_pattern|
59
- routes << Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(controller, method, route_pattern)
59
+ routes << Contrast::Agent::Reporting::DiscoveredRoute.from_sinatra_route(controller, method,
60
+ route_pattern)
60
61
  end
61
62
  end
62
63
  end