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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b285299c51379c206ff8c7a64426ef5207594391d59c1ce284bae3a899d6d56f
|
4
|
+
data.tar.gz: f7847b7523600ebad30ba380cd338d36f6ed2e8136e86d2fdb71f4c7953f530a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3edc5557919437f5c7f76b741ed4133263bd975baebb6c81d8ac416713571c6ef871e9ab4d3b1ddf34aa0ff548242e71081443bc5c3cd04c2f4bae35b030e866
|
7
|
+
data.tar.gz: 21c33b917d5f9c0f3b8070bde64b42c8d800dd73c952b0d45830b9d13eb6135ba086c9abf43b3003168f5c3902753cf1a480444bbfca506f00d7bf51b5039d03
|
@@ -31,11 +31,12 @@ module Contrast
|
|
31
31
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
32
32
|
return unless context
|
33
33
|
|
34
|
+
Contrast::Agent.reporter.send_event_immediately(context.observed_library_usage)
|
35
|
+
|
34
36
|
if Contrast::Agent::Reporter.enabled?
|
35
37
|
[
|
36
38
|
context.new_observed_route,
|
37
39
|
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
|
38
|
-
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity.library_usages),
|
39
40
|
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
|
40
41
|
].each do |event|
|
41
42
|
Contrast::Agent.reporter&.send_event_immediately(event)
|
@@ -1,6 +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/reporting/reporting_events/library_usage_observation'
|
4
5
|
require 'contrast/agent/inventory/dependencies'
|
5
6
|
require 'contrast/components/logger'
|
6
7
|
require 'contrast/utils/object_share'
|
@@ -73,10 +74,11 @@ module Contrast
|
|
73
74
|
#
|
74
75
|
# TODO: RUBY-1355
|
75
76
|
# TODO: RUBY-1438 -- change to just use EventMessage
|
76
|
-
# @param
|
77
|
-
|
77
|
+
# @param observed_library_usage [Contrast::Agent::Reporting::ObservedLibraryUsage] the message to
|
78
|
+
# which to append the usage data
|
79
|
+
def generate_library_usage observed_library_usage
|
78
80
|
return unless enabled?
|
79
|
-
return unless
|
81
|
+
return unless observed_library_usage
|
80
82
|
|
81
83
|
# Disconnect gemdigest_cache and replace it with an empty one; synch so new libs cannot be added between the
|
82
84
|
# assignment and the replace
|
@@ -86,8 +88,10 @@ module Contrast
|
|
86
88
|
hold
|
87
89
|
end
|
88
90
|
gem_spec_digest_to_files.each_pair do |digest, files|
|
89
|
-
usage = Contrast::
|
90
|
-
|
91
|
+
usage = Contrast::Agent::Reporting::LibraryUsageObservation.new(digest, files)
|
92
|
+
next if usage.names.empty?
|
93
|
+
|
94
|
+
observed_library_usage.observations << usage
|
91
95
|
end
|
92
96
|
rescue StandardError => e
|
93
97
|
logger.error('Unable to generate library usage.', e)
|
@@ -16,6 +16,10 @@ module Contrast
|
|
16
16
|
# @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
|
17
17
|
# @return [Hash] the details for this specific rule
|
18
18
|
def extract_details attack_sample
|
19
|
+
# TODO: RUBY-1702 - figure out why xss isn't populated when reported; probably something to do w/
|
20
|
+
# suspicious
|
21
|
+
return Contrast::Utils::ObjectShare::EMPTY_HASH unless attack_sample&.xss
|
22
|
+
|
19
23
|
{
|
20
24
|
input: attack_sample.xss.input,
|
21
25
|
matches: attack_sample.xss.matches.map do |match|
|
@@ -31,16 +31,6 @@ module Contrast
|
|
31
31
|
@_connection ||= client.initialize_connection
|
32
32
|
end
|
33
33
|
|
34
|
-
def attempt_to_start?
|
35
|
-
unless cs__class.enabled?
|
36
|
-
logger.warn('Reporter service is disabled!')
|
37
|
-
return false
|
38
|
-
end
|
39
|
-
|
40
|
-
logger.debug('Attempting to start Reporter thread') unless running?
|
41
|
-
true
|
42
|
-
end
|
43
|
-
|
44
34
|
def start_thread!
|
45
35
|
return if running?
|
46
36
|
|
@@ -54,6 +44,8 @@ module Contrast
|
|
54
44
|
next unless client && connection
|
55
45
|
|
56
46
|
process_event(queue.pop)
|
47
|
+
rescue StandardError => e
|
48
|
+
logger.debug('Reporter thread could not process because of:', error: e)
|
57
49
|
end
|
58
50
|
end
|
59
51
|
end
|
@@ -78,7 +70,6 @@ module Contrast
|
|
78
70
|
return
|
79
71
|
end
|
80
72
|
return unless event
|
81
|
-
return unless cs__class.enabled?
|
82
73
|
|
83
74
|
logger.debug('Enqueued event for sending', event_type: event.cs__class)
|
84
75
|
queue << event
|
@@ -17,19 +17,12 @@ module Contrast
|
|
17
17
|
# discovered during first request processing.
|
18
18
|
attr_reader :routes
|
19
19
|
|
20
|
-
class << self
|
21
|
-
def convert app_update_dtm
|
22
|
-
app_inventory = new
|
23
|
-
app_inventory.attach_data(app_update_dtm)
|
24
|
-
app_inventory
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
20
|
def initialize
|
29
|
-
@routes = []
|
30
21
|
@event_type = :application_inventory
|
31
22
|
@event_method = :POST
|
32
23
|
@event_endpoint = Contrast::Agent::Reporting::Endpoints.application_inventory
|
24
|
+
# The API spec limits us to 500 routes, so we'll enforce that here to not hold on to superfulous data.
|
25
|
+
@routes = Contrast::Agent.framework_manager.find_route_discovery_data.take(500)
|
33
26
|
super
|
34
27
|
end
|
35
28
|
|
@@ -39,18 +32,10 @@ module Contrast
|
|
39
32
|
|
40
33
|
def to_controlled_hash
|
41
34
|
{
|
42
|
-
session_id:
|
35
|
+
session_id: ::Contrast::ASSESS.session_id,
|
43
36
|
routes: routes.map(&:to_controlled_hash)
|
44
37
|
}
|
45
38
|
end
|
46
|
-
|
47
|
-
# @param inventory_dtm [Contrast::Api::Dtm::ApplicationUpdate]
|
48
|
-
# @return [Contrast::Agent::Reporting::ApplicationInventory]
|
49
|
-
def attach_data inventory_dtm
|
50
|
-
inventory_dtm.routes.each do |route|
|
51
|
-
@routes << Contrast::Agent::Reporting::DiscoveredRoute.convert(route)
|
52
|
-
end
|
53
|
-
end
|
54
39
|
end
|
55
40
|
end
|
56
41
|
end
|
@@ -17,15 +17,84 @@ module Contrast
|
|
17
17
|
# recorded.
|
18
18
|
class DiscoveredRoute < Contrast::Agent::Reporting::ObservedRoute
|
19
19
|
class << self
|
20
|
-
# @param
|
20
|
+
# @param obj [Regexp, Object]
|
21
|
+
# @return [String]
|
22
|
+
def source_or_string obj
|
23
|
+
if obj.cs__is_a?(Regexp)
|
24
|
+
obj.source
|
25
|
+
elsif obj.cs__respond_to?(:safe_string)
|
26
|
+
obj.safe_string
|
27
|
+
else
|
28
|
+
obj.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Convert ActionDispatch::Journey::Route to Contrast::Agent::Reporting::DiscoveredRoute
|
33
|
+
#
|
34
|
+
# @param journey_obj [ActionDispatch::Journey::Route] a rails route
|
35
|
+
# @param url [String, nil] use url from string instead of journey object.
|
36
|
+
# @return [Contrast::Agent::Reporting::DiscoveredRoute]
|
37
|
+
def from_action_dispatch_journey journey_obj, url = nil
|
38
|
+
msg = new
|
39
|
+
msg.signature = "#{ journey_obj.defaults[:controller] }##{ journey_obj.defaults[:action] }"
|
40
|
+
|
41
|
+
verb = source_or_string(journey_obj.verb)
|
42
|
+
msg.verb = Contrast::Utils::StringUtils.force_utf8(verb)
|
43
|
+
|
44
|
+
url ||= source_or_string(journey_obj.path.spec)
|
45
|
+
msg.url = Contrast::Utils::StringUtils.force_utf8(url)
|
46
|
+
msg
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convert Grape route data to discovered route.
|
50
|
+
#
|
51
|
+
# @param controller [::Grape::API] the route's final controller.
|
52
|
+
# @param method [String] GET, PUT, POST, etc...
|
53
|
+
# @param url [String, nil] use url from string instead matched pattern.
|
54
|
+
# @param pattern [String, Grape::Router::Route] the pattern that was matched in routing.
|
21
55
|
# @return [Contrast::Agent::Reporting::DiscoveredRoute]
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
56
|
+
def from_grape_controller controller, method, pattern, url = nil
|
57
|
+
if pattern.cs__is_a?(Grape::Router::Route)
|
58
|
+
safe_pattern = pattern.pattern&.path&.to_s
|
59
|
+
safe_url = source_or_string(url || safe_pattern)
|
60
|
+
else
|
61
|
+
safe_pattern = source_or_string(pattern)
|
62
|
+
safe_url = source_or_string(url || pattern)
|
63
|
+
end
|
64
|
+
|
65
|
+
msg = new
|
66
|
+
msg.signature = "#{ controller }##{ method } #{ safe_pattern }"
|
67
|
+
msg.verb = Contrast::Utils::StringUtils.force_utf8(method)
|
68
|
+
msg.url = Contrast::Utils::StringUtils.force_utf8(safe_url)
|
69
|
+
msg
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convert Sinatra route data to discovered route.
|
73
|
+
#
|
74
|
+
# @param controller [::Sinatra::Base] the route's final controller.
|
75
|
+
# @param method [String] GET, PUT, POST, etc...
|
76
|
+
# @param pattern [::Mustermann::Sinatra] the pattern that was matched in routing.
|
77
|
+
# @param url [String, nil] use url from string instead matched pattern.
|
78
|
+
# @return [Contrast::Agent::Reporting::DiscoveredRoute]
|
79
|
+
def from_sinatra_route controller, method, pattern, url = nil
|
80
|
+
safe_pattern = source_or_string(pattern)
|
81
|
+
safe_url = source_or_string(url || pattern)
|
82
|
+
|
83
|
+
msg = new
|
84
|
+
msg.signature = "#{ controller }##{ method } #{ safe_pattern }"
|
85
|
+
msg.verb = Contrast::Utils::StringUtils.force_utf8(method)
|
86
|
+
msg.url = Contrast::Utils::StringUtils.force_utf8(safe_url)
|
87
|
+
msg
|
26
88
|
end
|
27
89
|
end
|
28
90
|
|
91
|
+
# @return [String] the controller, method, and pattern of this route
|
92
|
+
attr_accessor :signature
|
93
|
+
# @return [String] the url (or url pattern) used to access this route
|
94
|
+
attr_accessor :url
|
95
|
+
# @return [String, nil] the HTTP verb used to access this route or nil for any verb
|
96
|
+
attr_accessor :verb
|
97
|
+
|
29
98
|
def initialize
|
30
99
|
super
|
31
100
|
@event_type = :discovered_route
|
@@ -34,18 +103,9 @@ module Contrast
|
|
34
103
|
@url = Contrast::Utils::ObjectShare::EMPTY_STRING
|
35
104
|
end
|
36
105
|
|
37
|
-
# @param dtm[Contrast::Api::Dtm::RouteCoverage]
|
38
|
-
def attach_data dtm
|
39
|
-
@signature = dtm.route
|
40
|
-
@verb = dtm.verb
|
41
|
-
@url = dtm.url
|
42
|
-
end
|
43
|
-
|
44
106
|
def to_controlled_hash
|
45
107
|
validate
|
46
|
-
|
47
|
-
dr_hash.delete(:verb) unless @verb
|
48
|
-
dr_hash
|
108
|
+
{ session_id: ::Contrast::ASSESS.session_id, signature: @signature, verb: @verb, url: @url }.compact
|
49
109
|
end
|
50
110
|
|
51
111
|
def validate
|
@@ -135,7 +135,7 @@ module Contrast
|
|
135
135
|
created: created,
|
136
136
|
hash: hash_code.to_s,
|
137
137
|
ruleId: rule_id,
|
138
|
-
session_id:
|
138
|
+
session_id: ::Contrast::ASSESS.session_id,
|
139
139
|
version: 4
|
140
140
|
}
|
141
141
|
hsh[:events] = events.map(&:to_controlled_hash) if event_based?
|
@@ -152,7 +152,7 @@ module Contrast
|
|
152
152
|
# @raise [ArgumentError]
|
153
153
|
def validate
|
154
154
|
raise(ArgumentError, "#{ self } did not have a proper rule. Unable to continue.") unless @rule_id
|
155
|
-
unless
|
155
|
+
unless ::Contrast::ASSESS.session_id
|
156
156
|
raise(ArgumentError, "#{ self } did not have a proper session id. Unable to continue.")
|
157
157
|
end
|
158
158
|
if event_based? && events.empty?
|
@@ -11,25 +11,11 @@ module Contrast
|
|
11
11
|
# @param [Array<String>] List of file paths that have been loaded out of or executed by the library
|
12
12
|
attr_reader :names
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def convert usage_dtm
|
20
|
-
observation = new
|
21
|
-
observation.attach_data(usage_dtm)
|
22
|
-
observation
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize
|
27
|
-
@names = []
|
28
|
-
end
|
29
|
-
|
30
|
-
def attach_data usage_dtm
|
31
|
-
@id = usage_dtm.hash_code
|
32
|
-
@names = usage_dtm.class_names.keys
|
14
|
+
# @param id [String] Sha256Sum of library as identified by the agent
|
15
|
+
# @param class_names [Array<String>] List of file paths that have been loaded out of or executed by the library
|
16
|
+
def initialize id, class_names
|
17
|
+
@id = id
|
18
|
+
@names = class_names
|
33
19
|
end
|
34
20
|
|
35
21
|
def to_controlled_hash
|
@@ -9,23 +9,11 @@ module Contrast
|
|
9
9
|
module Reporting
|
10
10
|
# List of libraries that have been observed to have something loaded or executed.
|
11
11
|
#
|
12
|
-
# @attr_reader observations - Array[Contrast::Agent::Reporting::LibraryUsageObservation]
|
13
|
-
# - Hash of LibraryUsageObservations
|
14
12
|
class ObservedLibraryUsage < Contrast::Agent::Reporting::ApplicationReportingEvent
|
13
|
+
# @attr_reader observations - Array[Contrast::Agent::Reporting::LibraryUsageObservation]
|
14
|
+
# - Hash of LibraryUsageObservations
|
15
15
|
attr_reader :observations
|
16
16
|
|
17
|
-
class << self
|
18
|
-
# Convert a Hash of LibraryUsageUpdate DTMs for SpeedRacer to an Event for TeamServer.
|
19
|
-
#
|
20
|
-
# @param usages_dtm_hash Hash[Contrast::Api::Dtm::LibraryUsageUpdate]
|
21
|
-
# @return [Contrast::Agent::Reporting::ObservedLibraryUsage]
|
22
|
-
def convert usages_dtm_hash
|
23
|
-
report = new
|
24
|
-
report.attach_data(usages_dtm_hash)
|
25
|
-
report
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
17
|
def initialize
|
30
18
|
@event_endpoint = Contrast::Agent::Reporting::Endpoints.library_usage
|
31
19
|
@observations = []
|
@@ -41,17 +29,13 @@ module Contrast
|
|
41
29
|
{ observations: @observations.map(&:to_controlled_hash) }
|
42
30
|
end
|
43
31
|
|
44
|
-
def attach_data usages_dtm_hash
|
45
|
-
usages_dtm_hash.each do |_key, value|
|
46
|
-
next unless value.class_names.any?
|
47
|
-
|
48
|
-
@observations << Contrast::Agent::Reporting::LibraryUsageObservation.convert(value)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
32
|
def validate
|
53
33
|
raise(ArgumentError, "#{ self } did not have observations. Unable to continue.") if observations.empty?
|
54
34
|
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
@observations = []
|
38
|
+
end
|
55
39
|
end
|
56
40
|
end
|
57
41
|
end
|
@@ -30,7 +30,6 @@ module Contrast
|
|
30
30
|
@app_name = ::Contrast::APP_CONTEXT.app_name
|
31
31
|
@app_version = ::Contrast::APP_CONTEXT.app_version
|
32
32
|
@routes = []
|
33
|
-
@agent_session_id_value = ::Contrast::ASSESS.session_id
|
34
33
|
end
|
35
34
|
|
36
35
|
# Convert the instance variables on the class, and other information, into the identifiers required for
|
@@ -48,7 +47,7 @@ module Contrast
|
|
48
47
|
data: '',
|
49
48
|
key: 0,
|
50
49
|
routes: @routes,
|
51
|
-
session_id:
|
50
|
+
session_id: ::Contrast::ASSESS.session_id
|
52
51
|
}
|
53
52
|
end
|
54
53
|
|
@@ -60,7 +59,7 @@ module Contrast
|
|
60
59
|
unless @app_language
|
61
60
|
raise(ArgumentError, "#{ cs__class } did not have a proper application language. Unable to continue.")
|
62
61
|
end
|
63
|
-
unless
|
62
|
+
unless ::Contrast::ASSESS.session_id
|
64
63
|
raise(ArgumentError, "#{ cs__class } did not have a proper session id. Unable to continue.")
|
65
64
|
end
|
66
65
|
|
@@ -20,9 +20,7 @@ module Contrast
|
|
20
20
|
attr_reader :event_method
|
21
21
|
|
22
22
|
def initialize
|
23
|
-
@
|
24
|
-
@event_endpoint ||= nil
|
25
|
-
@event_method ||= :POST
|
23
|
+
@event_method ||= :POST # rubocop:disable Lint/DisjunctiveAssignmentInConstructor
|
26
24
|
end
|
27
25
|
|
28
26
|
# Some reports require specific additional headers to be used. To that end, we'll attach them here, letting
|
@@ -32,6 +32,15 @@ module Contrast
|
|
32
32
|
@count = 0
|
33
33
|
end
|
34
34
|
|
35
|
+
# Parse the given controller and route from a Rack based application framework in order to create an instance
|
36
|
+
# of this class
|
37
|
+
#
|
38
|
+
# @param final_controller [Grape::API, Sinatra::Base] the controller responsible for the definition of the
|
39
|
+
# entrypoint of the route actively being executed
|
40
|
+
# @param method [String] the HTTP request method of the route actively being executed
|
41
|
+
# @param route_pattern [Grape::Router::Route, Mustermann::Sinatra] the pattern to which the url maps
|
42
|
+
# @param url [String] the literal url of the route actively being executed
|
43
|
+
# @return [Contrast::Agent::Reporting::RouteCoverage]
|
35
44
|
def attach_rack_based_data final_controller, method, route_pattern, url = nil
|
36
45
|
if route_pattern.cs__is_a?(Grape::Router::Route)
|
37
46
|
safe_pattern = route_pattern.pattern&.path&.to_s
|
@@ -16,7 +16,7 @@ module Contrast
|
|
16
16
|
attr_reader :path_for_requests, :path_for_responses
|
17
17
|
|
18
18
|
def initialize
|
19
|
-
generate_paths if enabled?
|
19
|
+
generate_paths if enabled?
|
20
20
|
end
|
21
21
|
|
22
22
|
# This method will be handling the auditing of the requests and responses we send to SpeedRacer. If the audit
|
@@ -45,7 +45,6 @@ module Contrast
|
|
45
45
|
# @param data[String] String representation if the logged data
|
46
46
|
def log_data type, file_name, data = nil
|
47
47
|
return unless enabled?
|
48
|
-
return unless Contrast::CONTRAST_SERVICE.use_agent_communication?
|
49
48
|
|
50
49
|
logger.debug('logging to file', file_name: file_name) # TODO: RUBY-99999 DO NOT COMMIT THIS
|
51
50
|
write_to_file(type, file_name, data)
|
@@ -21,15 +21,6 @@ module Contrast
|
|
21
21
|
dtm.cs__is_a?(Contrast::Api::Dtm::ServerActivity)
|
22
22
|
end
|
23
23
|
|
24
|
-
# Checks if the message is a Hash of Contrast::Api::Dtm::LibraryUsageUpdate class
|
25
|
-
#
|
26
|
-
# @param message [Protobuf::Field::FieldHash<String,::Contrast::Api::Dtm::LibraryUsageUpdate>]
|
27
|
-
# @return [Boolean]
|
28
|
-
def library_usage? message
|
29
|
-
message.cs__is_a?(Protobuf::Field::FieldHash) &&
|
30
|
-
message.values[0].cs__is_a?(Contrast::Api::Dtm::LibraryUsageUpdate)
|
31
|
-
end
|
32
|
-
|
33
24
|
# Checks if the message is of Contrast::Api::Dtm::ApplicationUpdate class
|
34
25
|
#
|
35
26
|
# @param dtm [Contrast::Api::Dtm::ApplicationUpdate,Object]
|
@@ -61,7 +52,6 @@ module Contrast
|
|
61
52
|
return Contrast::Agent::Reporting::ServerActivity.new if server_activity?(dtm)
|
62
53
|
|
63
54
|
# For the others, we convert them.
|
64
|
-
return Contrast::Agent::Reporting::ObservedLibraryUsage.convert(dtm) if library_usage?(dtm)
|
65
55
|
return Contrast::Agent::Reporting::ApplicationUpdate.convert(dtm) if application_update?(dtm)
|
66
56
|
return Contrast::Agent::Reporting::Finding.convert(dtm) if finding?(dtm)
|
67
57
|
return Contrast::Agent::Reporting::ApplicationActivity.convert(dtm) if activity?(dtm)
|
@@ -56,7 +56,6 @@ module Contrast
|
|
56
56
|
# @param send_immediately [Boolean] flag for the logger
|
57
57
|
# @return response [Net::HTTP::Response, nil] response from TS if no response
|
58
58
|
def send_event event, connection, send_immediately: false
|
59
|
-
return unless Contrast::Agent::Reporter.enabled?
|
60
59
|
return unless connection
|
61
60
|
|
62
61
|
log_send_event(event) if send_immediately
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'contrast/agent/reporting/settings/application_settings'
|
5
5
|
require 'contrast/agent/reporting/settings/server_features'
|
6
|
+
require 'contrast/agent/reporting/settings/reaction'
|
6
7
|
|
7
8
|
module Contrast
|
8
9
|
module Agent
|
@@ -21,19 +22,76 @@ module Contrast
|
|
21
22
|
# @return [Contrast::Agent::Reporting::Settings::FeatureSettings, nil]
|
22
23
|
attr_accessor :server_features
|
23
24
|
|
25
|
+
# Success boolean message value
|
26
|
+
#
|
27
|
+
# @return [Boolean]
|
28
|
+
attr_accessor :success
|
29
|
+
|
30
|
+
# Message with reasons for success or fail.
|
31
|
+
#
|
32
|
+
# @return [Array<String>] Messages received from TS.
|
33
|
+
attr_accessor :messages
|
34
|
+
|
24
35
|
class << self
|
25
|
-
|
36
|
+
# All of the settings from TeamServer that apply at the application level.
|
37
|
+
#
|
38
|
+
# @return response [Contrast::Agent::Reporting::Response]
|
39
|
+
def build_application_response
|
26
40
|
res = new
|
27
41
|
res.application_settings = Contrast::Agent::Reporting::Settings::ApplicationSettings.new
|
28
42
|
res
|
29
43
|
end
|
30
44
|
|
31
|
-
|
45
|
+
# All of the settings from TeamServer that apply at the server level.
|
46
|
+
#
|
47
|
+
# @return response [Contrast::Agent::Reporting::Response]
|
48
|
+
def build_server_response
|
32
49
|
res = new
|
33
50
|
res.server_features = Contrast::Agent::Reporting::Settings::ServerFeatures.new
|
34
51
|
res
|
35
52
|
end
|
36
53
|
end
|
54
|
+
|
55
|
+
# Reaction the agent should take based on a state in TS.
|
56
|
+
# This is moved one level up because the responses we
|
57
|
+
# receive for feature and settings from TS have different
|
58
|
+
# place to store these reactions:
|
59
|
+
#
|
60
|
+
# body.reactions vs body.settings.reactions
|
61
|
+
#
|
62
|
+
# @return [Array<Contrast::Agent::Reporting::Settings::Reaction>]
|
63
|
+
def reactions
|
64
|
+
@_reactions ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
# Set the reaction
|
68
|
+
#
|
69
|
+
# @param reaction_array [Array<Reaction>] {
|
70
|
+
# level [String] The level at which the agent should log this reaction.
|
71
|
+
# [ERROR, WARN, INFO, DEBUG, TRACE]
|
72
|
+
# message [String] A message to log when receiving this reaction.
|
73
|
+
# operation [String] What to do in response to this reaction.[NOOP, DISABLE] }
|
74
|
+
# @return [Array<Contrast::Agent::Reporting::Settings::Reaction>]
|
75
|
+
def reactions= reaction_array
|
76
|
+
return unless reaction_array.is_a?(Array)
|
77
|
+
|
78
|
+
reaction_array.each do |r|
|
79
|
+
reactions << Contrast::Agent::Reporting::Settings::Reaction.new(r[:level], r[:operation], r[:message])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# This method is used only for testing with golden files.
|
84
|
+
def to_controlled_hash
|
85
|
+
{
|
86
|
+
success: success,
|
87
|
+
messages: messages,
|
88
|
+
features: server_features.nil? ? nil : server_features.to_controlled_hash,
|
89
|
+
settings: application_settings.nil? ? nil : application_settings.to_controlled_hash,
|
90
|
+
logLevel: server_features&.log_level,
|
91
|
+
logFile: server_features&.log_file,
|
92
|
+
reactions: server_features.nil? ? nil : reactions.map(&:to_controlled_hash)
|
93
|
+
}.compact
|
94
|
+
end
|
37
95
|
end
|
38
96
|
end
|
39
97
|
end
|
@@ -24,7 +24,6 @@ module Contrast
|
|
24
24
|
protect = response_data[:settings][:defend]
|
25
25
|
return unless protect
|
26
26
|
|
27
|
-
# TODO: RUBY-1636 should this be `:rules` or `:protectionRules`
|
28
27
|
res.application_settings.protect.protection_rules = protect[:protectionRules]
|
29
28
|
res.application_settings.protect.virtual_patches = protect[:virtualPatches]
|
30
29
|
end
|
@@ -40,10 +39,14 @@ module Contrast
|
|
40
39
|
res.application_settings.exclusions.url_exclusions = exclusions[:urlExceptions]
|
41
40
|
end
|
42
41
|
|
42
|
+
# The responses we receive for feature and settings from TS have different
|
43
|
+
# place to store these reactions: body.reactions vs body.settings.reactions.
|
44
|
+
#
|
43
45
|
# @param response_data [Hash]
|
44
46
|
# @param res [Contrast::Agent::Reporting::Response]
|
45
47
|
def extract_reactions response_data, res
|
46
|
-
res.
|
48
|
+
res.reactions = response_data[:settings][:reactions] if response_data[:settings]
|
49
|
+
res.reactions = response_data[:reactions] if response_data[:features]
|
47
50
|
end
|
48
51
|
|
49
52
|
# @param response_data [Hash]
|
@@ -65,10 +68,17 @@ module Contrast
|
|
65
68
|
return unless protect
|
66
69
|
|
67
70
|
res.server_features.protect.enabled = protect[:enabled]
|
68
|
-
res.server_features.protect.bot_blocker = protect[:'bot-blocker']
|
69
|
-
|
70
|
-
|
71
|
-
|
71
|
+
res.server_features.protect.bot_blocker.enable = protect[:'bot-blocker']
|
72
|
+
res.server_features.protect.bot_blocker.bots = protect[:botBlockers]
|
73
|
+
extract_syslog(response_data, res)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param response_data [Hash]
|
77
|
+
# @param res [Contrast::Agent::Reporting::Response]
|
78
|
+
def extract_syslog response_data, res
|
79
|
+
return unless (syslog = response_data[:features][:defend][:syslog])
|
80
|
+
|
81
|
+
res.server_features.protect.syslog.assign_array(syslog)
|
72
82
|
end
|
73
83
|
|
74
84
|
# @param response_data [Hash]
|
@@ -78,22 +88,34 @@ module Contrast
|
|
78
88
|
return unless protect
|
79
89
|
|
80
90
|
res.server_features.protect.ip_allowlist = protect[:ipAllowlist]
|
81
|
-
res.server_features.protect.ip_denylist = protect[:
|
82
|
-
res.server_features.protect.
|
91
|
+
res.server_features.protect.ip_denylist = protect[:ipDenylist]
|
92
|
+
res.server_features.protect.log_enhancers = protect[:logEnhancers]
|
83
93
|
res.server_features.protect.rule_definition_list = protect[:ruleDefinitionList]
|
84
94
|
end
|
85
95
|
|
86
96
|
# Here we extract the rules and state for the sensitive data masking policy
|
87
|
-
#
|
97
|
+
# received from TS.
|
88
98
|
#
|
89
99
|
# @param response_data [Hash]
|
90
100
|
# @param res [Contrast::Agent::Reporting::Response]
|
91
101
|
def extract_sensitive_data_policy response_data, res
|
92
|
-
sensitive_data = response_data[:settings][:sensitive_data_masking_policy]
|
102
|
+
return unless (sensitive_data = response_data[:settings][:sensitive_data_masking_policy])
|
103
|
+
|
93
104
|
res.application_settings.sensitive_data_masking.mask_http_body = sensitive_data[:mask_http_body]
|
94
105
|
res.application_settings.sensitive_data_masking.mask_attack_vector = sensitive_data[:mask_attack_vector]
|
95
106
|
res.application_settings.sensitive_data_masking.build_rules_form_settings(sensitive_data[:rules])
|
96
107
|
end
|
108
|
+
|
109
|
+
# Here we extract the log settings received from TS.
|
110
|
+
#
|
111
|
+
# @param response_data [Hash]
|
112
|
+
# @param res [Contrast::Agent::Reporting::Response]
|
113
|
+
def extract_log_settings response_data, res
|
114
|
+
return unless (log_level = response_data[:logLevel])
|
115
|
+
|
116
|
+
res.server_features.log_level = log_level
|
117
|
+
res.server_features.log_file = response_data[:logFile] if response_data[:logFile]
|
118
|
+
end
|
97
119
|
end
|
98
120
|
end
|
99
121
|
end
|
@@ -90,7 +90,7 @@ module Contrast
|
|
90
90
|
when ERROR_CODES[:too_many_requests]
|
91
91
|
handle_response_errors(response, RETRY_AFTER_MSG, mode.resending)
|
92
92
|
else
|
93
|
-
logger.
|
93
|
+
logger.error('Response Error code could not be processed')
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|