contrast-agent 6.6.2 → 6.6.5
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/assess/policy/trigger_method.rb +21 -6
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +2 -0
- data/lib/contrast/agent/at_exit_hook.rb +1 -7
- data/lib/contrast/agent/inventory/database_config.rb +16 -12
- data/lib/contrast/agent/inventory/policy/datastores.rb +1 -2
- data/lib/contrast/agent/middleware.rb +0 -1
- data/lib/contrast/agent/protect/rule/base.rb +16 -20
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +5 -4
- data/lib/contrast/agent/protect/rule/deserialization.rb +5 -4
- data/lib/contrast/agent/protect/rule/path_traversal.rb +9 -7
- data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +16 -14
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/protect/rule/xxe.rb +9 -6
- data/lib/contrast/agent/reporting/attack_result/attack_result.rb +8 -0
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +85 -36
- data/lib/contrast/agent/reporting/attack_result/user_input.rb +11 -0
- data/lib/contrast/agent/reporting/details/bot_blocker_details.rb +29 -0
- data/lib/contrast/agent/reporting/details/cmd_injection_details.rb +30 -0
- data/lib/contrast/agent/reporting/details/details.rb +18 -0
- data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/ip_denylist_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/no_sqli_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/path_traversal_details.rb +24 -0
- data/lib/contrast/agent/reporting/details/path_traversal_semantic_analysis_details.rb +32 -0
- data/lib/contrast/agent/reporting/details/protect_rule_details.rb +17 -0
- data/lib/contrast/agent/reporting/details/sqli_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/untrusted_deserialization_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/virtual_patch_details.rb +24 -0
- data/lib/contrast/agent/reporting/details/xss_details.rb +33 -0
- data/lib/contrast/agent/reporting/details/xss_match.rb +30 -0
- data/lib/contrast/agent/reporting/details/xxe_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/xxe_match.rb +25 -0
- data/lib/contrast/agent/reporting/details/xxe_wrapper.rb +25 -0
- data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +1 -1
- data/lib/contrast/agent/reporting/masker/masker.rb +78 -65
- data/lib/contrast/agent/reporting/masker/masker_utils.rb +1 -30
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +84 -15
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +13 -25
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +17 -22
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +46 -125
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -16
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -18
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -14
- data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +29 -20
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +45 -10
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -7
- data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +2 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
- data/lib/contrast/agent/request.rb +2 -0
- data/lib/contrast/agent/request_context.rb +13 -4
- data/lib/contrast/agent/request_context_extend.rb +59 -40
- data/lib/contrast/agent/request_handler.rb +7 -9
- data/lib/contrast/agent/service_heartbeat.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/decorators/message.rb +1 -1
- data/lib/contrast/components/app_context.rb +62 -8
- data/lib/contrast/components/app_context_extend.rb +8 -8
- data/lib/contrast/config/assess_configuration.rb +1 -1
- data/lib/contrast/config/root_configuration.rb +6 -4
- data/lib/contrast/config.rb +0 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -6
- data/lib/contrast/utils/assess/event_limit_utils.rb +26 -7
- data/lib/contrast/utils/log_utils.rb +16 -10
- data/lib/contrast/utils/net_http_base.rb +5 -6
- data/lib/contrast/utils/string_utils.rb +2 -6
- data/lib/contrast.rb +1 -1
- metadata +30 -14
- data/lib/contrast/config/application_configuration.rb +0 -57
@@ -6,6 +6,8 @@ require 'contrast/agent/protect/rule/deserialization'
|
|
6
6
|
require 'contrast/agent/protect/rule/http_method_tampering'
|
7
7
|
require 'contrast/agent/protect/rule/no_sqli'
|
8
8
|
require 'contrast/api/dtm.pb'
|
9
|
+
require 'contrast/agent/reporting/attack_result/user_input'
|
10
|
+
require 'contrast/agent/reporting/attack_result/response_type'
|
9
11
|
require 'contrast/components/logger'
|
10
12
|
|
11
13
|
module Contrast
|
@@ -16,12 +18,20 @@ module Contrast
|
|
16
18
|
class ApplicationDefendAttackSample
|
17
19
|
include Contrast::Agent::Reporting::InputType
|
18
20
|
|
19
|
-
# @return [
|
20
|
-
|
21
|
+
# @return [Contrast::Agent::Reporting::UserInput]
|
22
|
+
attr_accessor :user_input
|
23
|
+
# @return [Array<Contrast::Agent::Reporting::ApplicationDefendAttackSampleStack>]
|
24
|
+
attr_reader :stack
|
25
|
+
# Details are per rule specific and should be set when the sample is build
|
26
|
+
#
|
27
|
+
# @return [Contrast::Agent::Reporting::Details::ProtectRuleDetails, {}]
|
28
|
+
attr_accessor :details
|
29
|
+
# @return [Integer] time in ms
|
30
|
+
attr_accessor :time_stamp
|
21
31
|
|
22
32
|
class << self
|
23
|
-
# @param attack_result [Contrast::
|
24
|
-
# @param attack_sample [Contrast::
|
33
|
+
# @param attack_result [Contrast::Agent::Reporting::AttackResult]
|
34
|
+
# @param attack_sample [Contrast::Agent::Reporting::ApplicationDefendAttackSample]
|
25
35
|
def convert attack_result, attack_sample
|
26
36
|
activity = new
|
27
37
|
activity.attach_data(attack_result, attack_sample)
|
@@ -30,151 +40,62 @@ module Contrast
|
|
30
40
|
end
|
31
41
|
|
32
42
|
def initialize
|
43
|
+
@time_stamp = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0 # in ms
|
33
44
|
@blocked = false
|
34
45
|
@event_type = :application_defend_attack_sample
|
46
|
+
@user_input = Contrast::Agent::Reporting::UserInput.new
|
47
|
+
@request = Contrast::Agent::REQUEST_TRACKER.current&.activity&.request
|
48
|
+
@stack = Contrast::Utils::StackTraceUtils.build_protect_report_stack_array
|
49
|
+
@details = {}
|
35
50
|
end
|
36
51
|
|
37
52
|
def to_controlled_hash
|
38
53
|
{
|
39
54
|
blocked: @blocked,
|
40
|
-
input: @
|
41
|
-
details: @details,
|
55
|
+
input: build_input(@user_input),
|
56
|
+
details: @details&.to_controlled_hash,
|
42
57
|
request: @request&.to_controlled_hash,
|
43
58
|
stack: @stack&.map(&:to_controlled_hash),
|
44
|
-
timestamp:
|
59
|
+
timestamp: build_time_stamp
|
45
60
|
}
|
46
61
|
end
|
47
62
|
|
48
|
-
# @param
|
49
|
-
# @
|
50
|
-
def
|
51
|
-
@blocked =
|
52
|
-
@input = build_input(attack_sample)
|
53
|
-
@details = build_details(attack_result.rule_id, attack_sample)
|
54
|
-
@time_stamp = build_time_stamp(attack_sample.timestamp_ms)
|
55
|
-
@request = FindingRequest.convert(Contrast::Agent::REQUEST_TRACKER.current&.request)
|
56
|
-
@stack = Contrast::Utils::StackTraceUtils.build_protect_report_stack_array
|
57
|
-
end
|
58
|
-
|
59
|
-
# @param start [Integer]
|
60
|
-
# @return [Hash]
|
61
|
-
def build_time_stamp start
|
62
|
-
{
|
63
|
-
start: start,
|
64
|
-
elapsed: Contrast::Utils::Timer.now_ms - start
|
65
|
-
}
|
63
|
+
# @param response_type [Contrast::Api::Dtm::AttackResult::ResponseType]
|
64
|
+
# @return [Boolean] check if response type is blocked
|
65
|
+
def blocked? response_type
|
66
|
+
@blocked = response_type == Contrast::Agent::Reporting::ResponseType::BLOCKED
|
66
67
|
end
|
67
68
|
|
68
|
-
# @param
|
69
|
-
def build_input
|
70
|
-
user_input = attack_sample.user_input
|
69
|
+
# @param user_input [Contrast::Agent::Reporting::UserInput]
|
70
|
+
def build_input user_input
|
71
71
|
{
|
72
72
|
documentPath: user_input.path,
|
73
|
-
documentType:
|
73
|
+
documentType: user_input.document_type.to_s,
|
74
74
|
filters: user_input.matcher_ids,
|
75
75
|
name: user_input.key,
|
76
|
-
time:
|
77
|
-
type:
|
76
|
+
time: time_stamp,
|
77
|
+
type: user_input.input_type.to_s,
|
78
78
|
value: user_input.value
|
79
79
|
}
|
80
80
|
end
|
81
81
|
|
82
|
-
#
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
'XML'
|
92
|
-
else
|
93
|
-
'NORMAL'
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# Convert the input type api enum to a String constant TeamServer can understand.
|
98
|
-
#
|
99
|
-
# @param type_enum [when ::Contrast::Api::Dtm::UserInput::InputType]
|
100
|
-
# @return [String]
|
101
|
-
def build_input_type type_enum
|
102
|
-
case type_enum
|
103
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::UNDEFINED_TYPE
|
104
|
-
'UNDEFINED_TYPE'
|
105
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::BODY
|
106
|
-
'BODY'
|
107
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::COOKIE_NAME
|
108
|
-
'COOKIE_NAME'
|
109
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::COOKIE_VALUE
|
110
|
-
'COOKIE_VALUE'
|
111
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::HEADER
|
112
|
-
'HEADER'
|
113
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::PARAMETER_NAME
|
114
|
-
'PARAMETER_NAME'
|
115
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::PARAMETER_VALUE
|
116
|
-
'PARAMETER_VALUE'
|
117
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::QUERYSTRING
|
118
|
-
'QUERYSTRING'
|
119
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::URI
|
120
|
-
'URI'
|
121
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::SOCKET
|
122
|
-
'SOCKET'
|
123
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::JSON_VALUE
|
124
|
-
'JSON_VALUE'
|
125
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::JSON_ARRAYED_VALUE
|
126
|
-
'JSON_ARRAYED_VALUE'
|
127
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_CONTENT_TYPE
|
128
|
-
'MULTIPART_CONTENT_TYPE'
|
129
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_VALUE
|
130
|
-
'MULTIPART_VALUE'
|
131
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_FIELD_NAME
|
132
|
-
'MULTIPART_FIELD_NAME'
|
133
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_NAME
|
134
|
-
'MULTIPART_NAME'
|
135
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::XML_VALUE
|
136
|
-
'XML_VALUE'
|
137
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::DWR_VALUE
|
138
|
-
'DWR_VALUE'
|
139
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::METHOD
|
140
|
-
'METHOD'
|
141
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::REQUEST
|
142
|
-
'REQUEST'
|
143
|
-
when ::Contrast::Api::Dtm::UserInput::InputType::URL_PARAMETER
|
144
|
-
'URL_PARAMETER'
|
145
|
-
else # ::Contrast::Api::Dtm::UserInput::InputType::UNKNOWN
|
146
|
-
'UNKNOWN'
|
147
|
-
end
|
82
|
+
# @param attack_result [Contrast::Agent::Reporting::AttackResult]
|
83
|
+
# @param attack_sample [Contrast::Agent::Reporting::RaspRuleSample]
|
84
|
+
def attach_data attack_result, attack_sample
|
85
|
+
@blocked = attack_result.response == ::Contrast::Agent::Reporting::ResponseType::BLOCKED
|
86
|
+
@user_input = attack_sample.user_input
|
87
|
+
@details = attack_sample.details
|
88
|
+
@time_stamp = attack_sample.time_stamp
|
89
|
+
@request = FindingRequest.convert(Contrast::Agent::REQUEST_TRACKER.current&.request)
|
90
|
+
@stack = Contrast::Utils::StackTraceUtils.build_protect_report_stack_array
|
148
91
|
end
|
149
92
|
|
150
|
-
#
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# @return [Hash] the details for this specific rule
|
157
|
-
def build_details rule_id, attack_sample
|
158
|
-
case rule_id
|
159
|
-
when Contrast::Agent::Protect::Rule::CmdInjection::NAME
|
160
|
-
Contrast::Agent::Protect::Rule::CmdInjection.extract_details(attack_sample)
|
161
|
-
when Contrast::Agent::Protect::Rule::Deserialization::NAME
|
162
|
-
Contrast::Agent::Protect::Rule::Deserialization.extract_details(attack_sample)
|
163
|
-
when Contrast::Agent::Protect::Rule::HttpMethodTampering::NAME
|
164
|
-
Contrast::Agent::Protect::Rule::HttpMethodTampering.extract_details(attack_sample)
|
165
|
-
when Contrast::Agent::Protect::Rule::NoSqli::NAME
|
166
|
-
Contrast::Agent::Protect::Rule::NoSqli.extract_details(attack_sample)
|
167
|
-
when Contrast::Agent::Protect::Rule::PathTraversal::NAME
|
168
|
-
Contrast::Agent::Protect::Rule::PathTraversal.extract_details(attack_sample)
|
169
|
-
when Contrast::Agent::Protect::Rule::Sqli::NAME
|
170
|
-
Contrast::Agent::Protect::Rule::Sqli.extract_details(attack_sample)
|
171
|
-
when Contrast::Agent::Protect::Rule::Xss::NAME
|
172
|
-
Contrast::Agent::Protect::Rule::Xss.extract_details(attack_sample)
|
173
|
-
when Contrast::Agent::Protect::Rule::Xxe::NAME
|
174
|
-
Contrast::Agent::Protect::Rule::Xxe.extract_details(attack_sample)
|
175
|
-
else # something unknown or Contrast::Agent::Protect::Rule::UnsafeFileUpload::NAME
|
176
|
-
Contrast::Utils::ObjectShare::EMPTY_HASH
|
177
|
-
end
|
93
|
+
# @return [Hash]
|
94
|
+
def build_time_stamp
|
95
|
+
{
|
96
|
+
start: time_stamp,
|
97
|
+
elapsed: Contrast::Utils::Timer.now_ms - time_stamp
|
98
|
+
}
|
178
99
|
end
|
179
100
|
end
|
180
101
|
end
|
data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb
CHANGED
@@ -16,21 +16,11 @@ module Contrast
|
|
16
16
|
# @return [Hash<Integer,Integer>] map of time from start in seconds to number of attacks in that second
|
17
17
|
attr_reader :time_map
|
18
18
|
|
19
|
-
class << self
|
20
|
-
# @param attack_result [Contrast::Api::Dtm::AttackResult]
|
21
|
-
def convert attack_result
|
22
|
-
activity = new
|
23
|
-
activity.attach_data(attack_result)
|
24
|
-
activity
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
19
|
def initialize
|
29
20
|
@samples = []
|
30
21
|
@start_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0 # in ms
|
31
22
|
@event_type = :application_defend_attack_sample_activity
|
32
23
|
@time_map = Hash.new { |h, k| h[k] = 0 }
|
33
|
-
super
|
34
24
|
end
|
35
25
|
|
36
26
|
def to_controlled_hash
|
@@ -42,17 +32,16 @@ module Contrast
|
|
42
32
|
}
|
43
33
|
end
|
44
34
|
|
45
|
-
# @param attack_result [Contrast::
|
35
|
+
# @param attack_result [Contrast::Agent::Reporting::AttackResult]
|
46
36
|
def attach_data attack_result
|
47
37
|
attack_result.samples.each do |attack_sample|
|
48
38
|
base_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@start_time = if dmt_time.zero?
|
39
|
+
sample_time = attack_sample.time_stamp.to_i
|
40
|
+
samples << Contrast::Agent::Reporting::ApplicationDefendAttackSample.convert(attack_result, attack_sample)
|
41
|
+
@start_time = if sample_time.zero?
|
53
42
|
@start_time
|
54
43
|
else
|
55
|
-
|
44
|
+
sample_time
|
56
45
|
end
|
57
46
|
attack_second = (@start_time - base_time) / 1000 # in seconds
|
58
47
|
time_map[attack_second] += 1
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/components/logger'
|
5
|
+
require 'contrast/utils/object_share'
|
5
6
|
require 'contrast/agent/reporting/reporting_events/application_defend_attack_activity'
|
6
7
|
|
7
8
|
module Contrast
|
@@ -12,7 +13,7 @@ module Contrast
|
|
12
13
|
class ApplicationDefendAttackerActivity
|
13
14
|
# @return [Hash<String,Contrast::Agent::Reporting::ApplicationDefendAttackActivity>] map of rule-id to violated
|
14
15
|
# samples for that rule
|
15
|
-
|
16
|
+
attr_accessor :protection_rules
|
16
17
|
# @return [String, nil] the IP address of the request from which the attack originated; used to identify unique
|
17
18
|
# attackers
|
18
19
|
attr_reader :source_ip
|
@@ -20,25 +21,14 @@ module Contrast
|
|
20
21
|
# identify unique attackers
|
21
22
|
attr_reader :source_forwarded_for
|
22
23
|
|
23
|
-
class << self
|
24
|
-
# @param attack_result_dtm [Contrast::Api::Dtm::AttackResult]
|
25
|
-
# @return [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
|
26
|
-
def convert attack_result_dtm
|
27
|
-
activity = new
|
28
|
-
activity.attach_data(attack_result_dtm)
|
29
|
-
activity
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
24
|
def initialize
|
34
25
|
@protection_rules = {}
|
35
|
-
req = Contrast::Agent::REQUEST_TRACKER.current
|
26
|
+
req = Contrast::Agent::REQUEST_TRACKER.current&.request
|
36
27
|
if req
|
37
|
-
@source_ip = req.
|
38
|
-
@source_forwarded_for = req.
|
28
|
+
@source_ip = req.ip || Contrast::Utils::ObjectShare::EMPTY_STRING
|
29
|
+
@source_forwarded_for = req.headers['X-Forwarded-For']
|
39
30
|
end
|
40
31
|
@event_type = :application_defend_attacker_activity
|
41
|
-
super
|
42
32
|
end
|
43
33
|
|
44
34
|
def to_controlled_hash
|
@@ -51,10 +41,12 @@ module Contrast
|
|
51
41
|
}
|
52
42
|
end
|
53
43
|
|
54
|
-
# @param attack_result [Contrast::
|
44
|
+
# @param attack_result [Contrast::Agent::Reporting::AttackResult]
|
55
45
|
def attach_data attack_result
|
56
|
-
@protection_rules[attack_result.rule_id] =
|
57
|
-
|
46
|
+
@protection_rules[attack_result.rule_id] = Contrast::Agent::Reporting::ApplicationDefendAttackActivity.new.
|
47
|
+
tap do |activity|
|
48
|
+
activity.attach_data(attack_result)
|
49
|
+
end
|
58
50
|
end
|
59
51
|
|
60
52
|
def process_protection_rules
|
@@ -18,16 +18,6 @@ module Contrast
|
|
18
18
|
# @ return [Array<String>, nil] - User-Agent Header value
|
19
19
|
attr_reader :browsers
|
20
20
|
|
21
|
-
class << self
|
22
|
-
# @param activity_dtm [Contrast::Api::Dtm::ApplicationActivity]
|
23
|
-
# @return [Contrast::Agent::Reporting::ApplicationInventoryActivity]
|
24
|
-
def convert activity_dtm
|
25
|
-
inventory = new
|
26
|
-
inventory.attach_data(activity_dtm)
|
27
|
-
inventory
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
21
|
def initialize
|
32
22
|
@event_type = :application_inventory_activity
|
33
23
|
@browsers = []
|
@@ -43,11 +33,13 @@ module Contrast
|
|
43
33
|
}
|
44
34
|
end
|
45
35
|
|
46
|
-
|
47
|
-
|
48
|
-
|
36
|
+
# @param architectures [Array<Contrast::Agent::Reporting::ArchitectureComponent>,
|
37
|
+
# Contrast::Agent::Reporting::ArchitectureComponent]
|
38
|
+
def attach_data architectures
|
39
|
+
Array(architectures).each do |architecture|
|
40
|
+
@components << architecture
|
49
41
|
end
|
50
|
-
request_headers =
|
42
|
+
request_headers = Contrast::Agent::REQUEST_TRACKER.current&.request&.headers
|
51
43
|
@browsers << request_headers['USER_AGENT'] if request_headers
|
52
44
|
end
|
53
45
|
|
@@ -20,36 +20,45 @@ module Contrast
|
|
20
20
|
class ArchitectureComponent
|
21
21
|
include Contrast::Components::Logger::InstanceMethods
|
22
22
|
# required attributes
|
23
|
-
|
23
|
+
attr_accessor :type, :url
|
24
24
|
# optional attributes
|
25
|
-
|
25
|
+
attr_accessor :remote_host, :remote_port, :vendor
|
26
26
|
|
27
27
|
# TeamServer only treats these specific values as valid for Architecture Components. It does not know how to
|
28
28
|
# process a message with a different type.
|
29
|
+
AC_TYPE_DB = 'db'
|
29
30
|
VALID_TYPES = %w[db ldap ws].cs__freeze
|
30
31
|
|
32
|
+
# class << self
|
33
|
+
# # Convert a DTM for SpeedRacer to an Event for TeamServer.
|
34
|
+
# #
|
35
|
+
# # @param component_dtm [Contrast::Api::Dtm::ArchitectureComponent]
|
36
|
+
# # @return [Contrast::Agent::Reporting::ArchitectureComponent]
|
37
|
+
# def convert component_dtm
|
38
|
+
# report = new
|
39
|
+
# report.attach_data(component_dtm)
|
40
|
+
# report
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
|
31
44
|
class << self
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def convert component_dtm
|
37
|
-
report = new
|
38
|
-
report.attach_data(component_dtm)
|
39
|
-
report
|
45
|
+
def build_database
|
46
|
+
msg = new
|
47
|
+
msg.type = AC_TYPE_DB
|
48
|
+
msg
|
40
49
|
end
|
41
50
|
end
|
42
51
|
|
43
|
-
# Attach the data from the protobuf models to this reporter so that it can be sent to TeamServer directly.
|
44
|
-
#
|
45
|
-
# @param component_dtm [Contrast::Api::Dtm::ArchitectureComponent]
|
46
|
-
def attach_data component_dtm
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
52
|
+
# # Attach the data from the protobuf models to this reporter so that it can be sent to TeamServer directly.
|
53
|
+
# #
|
54
|
+
# # @param component_dtm [Contrast::Api::Dtm::ArchitectureComponent]
|
55
|
+
# def attach_data component_dtm
|
56
|
+
# @remote_host = component_dtm.remote_host
|
57
|
+
# @remote_port = component_dtm.remote_port
|
58
|
+
# @type = component_dtm.type
|
59
|
+
# @url = component_dtm.url
|
60
|
+
# @vendor = component_dtm.vendor
|
61
|
+
# end
|
53
62
|
|
54
63
|
# Convert the instance variables on the class, and other information, into the identifiers required for
|
55
64
|
# TeamServer to process the JSON form of this message.
|
@@ -13,10 +13,12 @@ module Contrast
|
|
13
13
|
class FindingRequest
|
14
14
|
include Contrast::Components::Logger::InstanceMethods
|
15
15
|
|
16
|
+
OMITTED_BODY = '{{body-omitted-by-contrast}}'
|
17
|
+
|
16
18
|
# @return [String] the body of this request
|
17
|
-
|
19
|
+
attr_accessor :body
|
18
20
|
# @return [Hash<String,Array<String>>] the headers of this request
|
19
|
-
|
21
|
+
attr_accessor :headers
|
20
22
|
# @return [String] the HTTP verb of this request
|
21
23
|
attr_reader :method
|
22
24
|
# @return [Hash<String,Array<String>>] the parameters of this request
|
@@ -26,16 +28,27 @@ module Contrast
|
|
26
28
|
# @return [String] the HTTP(S) protocol of this request
|
27
29
|
attr_reader :protocol
|
28
30
|
# @return [String] the query string of this request
|
29
|
-
|
31
|
+
attr_accessor :query_string
|
30
32
|
# @return [String] the url, including path and script, of this request
|
31
33
|
attr_reader :uri
|
32
34
|
# @return [String] the HTTP version of this request
|
33
35
|
attr_reader :version
|
36
|
+
# @return [Integer]
|
37
|
+
attr_reader :ip
|
38
|
+
# @return [String] Byte representation of the body
|
39
|
+
attr_accessor :body_binary
|
40
|
+
# @return [Hash]
|
41
|
+
attr_reader :cookies
|
42
|
+
# REMOVE_DTM_REQUEST
|
43
|
+
# @return [Contrast::Api::Dtm::RawRequest]
|
44
|
+
attr_reader :dtm
|
34
45
|
|
35
46
|
class << self
|
36
47
|
# @param request [Contrast::Agent::Request]
|
37
48
|
# @return [Contrast::Agent::Reporting::FindingRequest]
|
38
49
|
def convert request
|
50
|
+
return unless request
|
51
|
+
|
39
52
|
report = new
|
40
53
|
report.attach_data(request)
|
41
54
|
report
|
@@ -47,15 +60,11 @@ module Contrast
|
|
47
60
|
#
|
48
61
|
# @param request [Contrast::Agent::Request]
|
49
62
|
def attach_data request
|
63
|
+
# REMOVE_DTM_REQUEST
|
64
|
+
@dtm = request.dtm
|
50
65
|
@body = request.body
|
51
66
|
@headers = {}
|
52
|
-
request
|
53
|
-
# We need to change from the uppercase _ format to capitalized - format.
|
54
|
-
header = key.split('_')
|
55
|
-
header.each(&:capitalize!)
|
56
|
-
header = header.join('-')
|
57
|
-
headers[header] = value.split
|
58
|
-
end
|
67
|
+
extract_headers(request)
|
59
68
|
@method = request.request_method
|
60
69
|
@parameters = {}
|
61
70
|
request.parameters.each_pair { |key, value| @parameters[key] = Array(value) }
|
@@ -64,6 +73,14 @@ module Contrast
|
|
64
73
|
@query_string = request.query_string
|
65
74
|
@uri = request.normalized_uri
|
66
75
|
@version = request.version
|
76
|
+
@ip = request.ip || ''
|
77
|
+
@body_binary = if omit_body?(request)
|
78
|
+
OMITTED_BODY
|
79
|
+
else
|
80
|
+
Contrast::Utils::StringUtils.force_utf8(request.body)
|
81
|
+
end
|
82
|
+
@cookies = {}
|
83
|
+
@cookies = request.cookies unless request.cookies.empty?
|
67
84
|
end
|
68
85
|
|
69
86
|
# Convert the instance variables on the class, and other information, into the identifiers required for
|
@@ -92,12 +109,30 @@ module Contrast
|
|
92
109
|
}
|
93
110
|
end
|
94
111
|
|
112
|
+
def omit_body? request
|
113
|
+
return true if ::Contrast::AGENT.omit_body?
|
114
|
+
return false if request.document_type != :NORMAL
|
115
|
+
|
116
|
+
request.media_type&.include?('multipart/form-data')
|
117
|
+
end
|
118
|
+
|
95
119
|
def validate
|
96
120
|
unless method && !method.empty? # rubocop:disable Security/Object/Method
|
97
121
|
raise(ArgumentError, "#{ self } did not have a proper method. Unable to continue.")
|
98
122
|
end
|
99
123
|
raise(ArgumentError, "#{ self } did not have a proper uri. Unable to continue.") unless uri && !uri.empty?
|
100
124
|
end
|
125
|
+
|
126
|
+
# @param request [Contrast::Agent::Request]
|
127
|
+
def extract_headers request
|
128
|
+
request.headers.each_pair do |key, value|
|
129
|
+
# We need to change from the uppercase _ format to capitalized - format.
|
130
|
+
header = key.split('_')
|
131
|
+
header.each(&:capitalize!)
|
132
|
+
header = header.join('-')
|
133
|
+
headers[header] = value.split
|
134
|
+
end
|
135
|
+
end
|
101
136
|
end
|
102
137
|
end
|
103
138
|
end
|
@@ -27,8 +27,8 @@ module Contrast
|
|
27
27
|
|
28
28
|
def initialize
|
29
29
|
@app_language = Contrast::Utils::ObjectShare::RUBY
|
30
|
-
@app_name = ::Contrast::APP_CONTEXT.
|
31
|
-
@app_version = ::Contrast::APP_CONTEXT.
|
30
|
+
@app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
|
31
|
+
@app_version = ::Contrast::APP_CONTEXT.version
|
32
32
|
@routes = []
|
33
33
|
end
|
34
34
|
|
@@ -18,12 +18,6 @@ module Contrast
|
|
18
18
|
dtm.cs__is_a?(Contrast::Api::Dtm::Finding)
|
19
19
|
end
|
20
20
|
|
21
|
-
# @param dtm [Contrast::Api::Dtm::Finding,Object]
|
22
|
-
# @return [Boolean]
|
23
|
-
def activity? dtm
|
24
|
-
dtm.cs__is_a?(Contrast::Api::Dtm::Activity)
|
25
|
-
end
|
26
|
-
|
27
21
|
# Converts DTM message to Reporting Event for those messages that have conversion methods crated. We use this
|
28
22
|
# as we work to move away from requiring the Service.
|
29
23
|
#
|
@@ -32,7 +26,6 @@ module Contrast
|
|
32
26
|
def dtm_to_event dtm
|
33
27
|
# For the others, we convert them.
|
34
28
|
return Contrast::Agent::Reporting::Finding.convert(dtm) if finding?(dtm)
|
35
|
-
return Contrast::Agent::Reporting::ApplicationActivity.convert(dtm) if activity?(dtm)
|
36
29
|
|
37
30
|
nil
|
38
31
|
end
|
@@ -153,7 +153,7 @@ module Contrast
|
|
153
153
|
def app_name
|
154
154
|
return @_app_name unless @_app_name.nil?
|
155
155
|
|
156
|
-
@_app_name = ::Contrast::APP_CONTEXT.
|
156
|
+
@_app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
|
157
157
|
end
|
158
158
|
|
159
159
|
# @return [String,nil]
|
@@ -18,12 +18,12 @@ module Contrast
|
|
18
18
|
CONTENT_TYPE = 'application/json'
|
19
19
|
|
20
20
|
def initialize
|
21
|
-
@app_name = Base64.strict_encode64(Contrast::APP_CONTEXT.
|
21
|
+
@app_name = Base64.strict_encode64(Contrast::APP_CONTEXT.name) # rubocop:disable Security/Module/Name
|
22
22
|
@api_key = Contrast::API.api_key
|
23
23
|
@agent_version = [RUBY, Contrast::Agent::VERSION].join(SPACE)
|
24
24
|
@app_language = RUBY
|
25
25
|
@app_path = Base64.strict_encode64(Contrast::APP_CONTEXT.path)
|
26
|
-
@app_version = Contrast::APP_CONTEXT.
|
26
|
+
@app_version = Contrast::APP_CONTEXT.version
|
27
27
|
@authorization = Base64.strict_encode64("#{ Contrast::API.user_name }:#{ Contrast::API.service_key }")
|
28
28
|
@server_name = Base64.strict_encode64(Contrast::APP_CONTEXT.server_name)
|
29
29
|
@server_path = Base64.strict_encode64(Contrast::APP_CONTEXT.server_path)
|
@@ -43,7 +43,6 @@ module Contrast
|
|
43
43
|
# @param connection [Net::HTTP] open connection
|
44
44
|
def startup! connection
|
45
45
|
return if status.startup_messages_sent?
|
46
|
-
return unless Contrast::Agent::Reporter.enabled?
|
47
46
|
|
48
47
|
send_agent_startup(connection)
|
49
48
|
end
|
@@ -57,6 +56,8 @@ module Contrast
|
|
57
56
|
def send_event event, connection
|
58
57
|
return unless connection
|
59
58
|
|
59
|
+
logger.debug('Preparing to send reporting event', event_class: event.cs__class)
|
60
|
+
|
60
61
|
request = build_request(event)
|
61
62
|
response = connection.request(request)
|
62
63
|
audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable
|
@@ -107,7 +107,7 @@ module Contrast
|
|
107
107
|
suspend_reporting(message, ready_after, error_message) if mode == @_mode.resending
|
108
108
|
return unless mode == @_mode.disabled
|
109
109
|
|
110
|
-
stop_reporting(message, application: Contrast::APP_CONTEXT.
|
110
|
+
stop_reporting(message, application: Contrast::APP_CONTEXT.name, error_message: error_message) # rubocop:disable Security/Module/Name
|
111
111
|
rescue StandardError => e
|
112
112
|
logger.debug('Could not handle Response error information', error: e)
|
113
113
|
end
|
@@ -142,6 +142,8 @@ module Contrast
|
|
142
142
|
@_body
|
143
143
|
end
|
144
144
|
|
145
|
+
# REMOVE_DTM_REQUEST
|
146
|
+
#
|
145
147
|
# Unlike most of our translation, which is called where needed for each
|
146
148
|
# message and forgotten, we'll leave this method to call the build as we
|
147
149
|
# don't want to pay to reconstruct the DTM for this Request multiple
|