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.
- checksums.yaml +4 -4
- data/lib/contrast/agent/at_exit_hook.rb +2 -1
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +9 -5
- data/lib/contrast/agent/protect/rule/xss.rb +4 -0
- data/lib/contrast/agent/reporting/reporter.rb +2 -11
- data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +3 -18
- data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +75 -15
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +5 -19
- data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +6 -22
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -3
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +1 -3
- data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +9 -0
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -10
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +0 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response.rb +60 -2
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +32 -10
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +58 -26
- data/lib/contrast/agent/reporting/settings/application_settings.rb +8 -23
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +27 -33
- data/lib/contrast/agent/reporting/settings/bot_blocker.rb +68 -0
- data/lib/contrast/agent/reporting/settings/code_exclusion.rb +27 -0
- data/lib/contrast/agent/reporting/settings/exclusion_base.rb +33 -0
- data/lib/contrast/agent/reporting/settings/exclusions.rb +39 -57
- data/lib/contrast/agent/reporting/settings/helpers.rb +56 -0
- data/lib/contrast/agent/reporting/settings/input_exclusion.rb +37 -0
- data/lib/contrast/agent/reporting/settings/ip_filter.rb +35 -0
- data/lib/contrast/agent/reporting/settings/keyword.rb +74 -0
- data/lib/contrast/agent/reporting/settings/log_enhancer.rb +65 -0
- data/lib/contrast/agent/reporting/settings/protect.rb +4 -2
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +62 -115
- data/lib/contrast/agent/reporting/settings/reaction.rb +11 -2
- data/lib/contrast/agent/reporting/settings/rule_definition.rb +63 -0
- data/lib/contrast/agent/reporting/settings/sampling.rb +10 -0
- data/lib/contrast/agent/reporting/settings/sanitizer.rb +38 -0
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +9 -1
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +7 -0
- data/lib/contrast/agent/reporting/settings/server_features.rb +8 -0
- data/lib/contrast/agent/reporting/settings/syslog.rb +176 -0
- data/lib/contrast/agent/reporting/settings/url_exclusion.rb +42 -0
- data/lib/contrast/agent/reporting/settings/validator.rb +17 -0
- data/lib/contrast/agent/request_context.rb +4 -0
- data/lib/contrast/agent/request_handler.rb +8 -4
- data/lib/contrast/agent/static_analysis.rb +4 -8
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +1 -1
- data/lib/contrast/agent/thread_watcher.rb +4 -5
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +1 -3
- data/lib/contrast/api/decorators/application_update.rb +0 -8
- data/lib/contrast/api/decorators.rb +0 -1
- data/lib/contrast/framework/base_support.rb +5 -4
- data/lib/contrast/framework/grape/support.rb +6 -6
- data/lib/contrast/framework/manager.rb +2 -4
- data/lib/contrast/framework/manager_extend.rb +1 -0
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +2 -1
- data/lib/contrast/framework/rails/support.rb +9 -2
- data/lib/contrast/framework/sinatra/support.rb +3 -2
- data/lib/contrast/logger/aliased_logging.rb +31 -26
- data/lib/contrast/utils/response_utils.rb +14 -1
- data/lib/contrast/utils/telemetry.rb +9 -0
- data/lib/contrast/utils/telemetry_hash.rb +36 -12
- data/lib/contrast/utils/telemetry_identifier.rb +8 -0
- data/lib/contrast/utils/thread_tracker.rb +26 -9
- data/lib/contrast/utils/timer.rb +6 -1
- data/lib/contrast.rb +1 -3
- metadata +26 -14
- 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.
|
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.
|
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-
|
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-
|
35
|
-
if Contrast::Agent.
|
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.
|
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
|
-
|
36
|
-
|
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
|
data/lib/contrast/agent.rb
CHANGED
@@ -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
|
26
|
-
#
|
27
|
-
#
|
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::
|
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
|
-
|
64
|
-
|
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]
|
144
|
-
#
|
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) # ::
|
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)
|
@@ -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::
|
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::
|
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::
|
59
|
+
routes << Contrast::Agent::Reporting::DiscoveredRoute.from_sinatra_route(controller, method,
|
60
|
+
route_pattern)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
end
|