contrast-agent 6.1.2 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
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