contrast-agent 6.6.5 → 6.7.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/.gitignore +3 -0
- data/.gitmodules +0 -3
- data/ext/cs__scope/cs__scope.c +1 -1
- data/lib/contrast/agent/assess/contrast_event.rb +2 -24
- data/lib/contrast/agent/assess/events/source_event.rb +7 -61
- data/lib/contrast/agent/assess/finalizers/hash.rb +11 -0
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +0 -55
- data/lib/contrast/agent/assess/policy/policy_node.rb +3 -3
- data/lib/contrast/agent/assess/policy/policy_node_utils.rb +0 -1
- data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
- data/lib/contrast/agent/assess/policy/source_method.rb +24 -1
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +7 -5
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +6 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +36 -132
- data/lib/contrast/agent/assess/policy/trigger_node.rb +3 -3
- data/lib/contrast/agent/assess/property/evented.rb +2 -12
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +42 -84
- data/lib/contrast/agent/assess/rule/response/base_rule.rb +11 -27
- data/lib/contrast/agent/assess/rule/response/body_rule.rb +1 -3
- data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +77 -62
- data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +1 -1
- data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +6 -1
- data/lib/contrast/agent/assess/rule/response/header_rule.rb +5 -5
- data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +1 -1
- data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +1 -1
- data/lib/contrast/agent/assess/tracker.rb +1 -7
- data/lib/contrast/agent/excluder.rb +206 -0
- data/lib/contrast/agent/exclusion_matcher.rb +6 -0
- data/lib/contrast/agent/inventory/database_config.rb +6 -10
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +4 -0
- data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +1 -0
- data/lib/contrast/agent/protect/rule/base.rb +49 -5
- data/lib/contrast/agent/protect/rule/base_service.rb +1 -0
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +18 -105
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +129 -0
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +169 -0
- data/lib/contrast/agent/protect/rule/deserialization.rb +2 -1
- data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +51 -0
- data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +67 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +6 -31
- data/lib/contrast/agent/protect/rule/xxe.rb +2 -0
- data/lib/contrast/agent/protect/rule.rb +3 -1
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +6 -0
- data/lib/contrast/agent/reporting/details/sqli_dangerous_functions.rb +22 -0
- data/lib/contrast/agent/reporting/reporter.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_events/agent_startup.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +1 -4
- data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +0 -23
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +19 -49
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +12 -9
- data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +23 -21
- data/lib/contrast/agent/reporting/reporting_events/finding_event_stack.rb +5 -18
- data/lib/contrast/agent/reporting/reporting_events/finding_event_taint_range.rb +1 -0
- data/lib/contrast/{api/decorators/trace_taint_range_tags.rb → agent/reporting/reporting_events/finding_event_taint_range_tags.rb} +7 -6
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +10 -14
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +11 -0
- data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +3 -1
- data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +11 -23
- data/lib/contrast/agent/reporting/reporting_events/route_discovery_observation.rb +8 -26
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/build_preflight.rb +4 -7
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +3 -3
- data/lib/contrast/agent/request.rb +2 -2
- data/lib/contrast/agent/request_context.rb +8 -20
- data/lib/contrast/agent/request_context_extend.rb +15 -36
- data/lib/contrast/agent/request_handler.rb +0 -8
- data/lib/contrast/agent/response.rb +0 -18
- data/lib/contrast/agent/telemetry/events/event.rb +1 -1
- data/lib/contrast/agent/telemetry/events/metric_event.rb +1 -1
- data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +3 -3
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +2 -3
- data/lib/contrast/api/communication/socket_client.rb +4 -4
- data/lib/contrast/api/communication/speedracer.rb +4 -8
- data/lib/contrast/api/decorators/agent_startup.rb +5 -6
- data/lib/contrast/api/decorators/application_settings.rb +2 -1
- data/lib/contrast/api/decorators/application_startup.rb +6 -6
- data/lib/contrast/api/decorators/message.rb +0 -4
- data/lib/contrast/api/decorators/rasp_rule_sample.rb +0 -6
- data/lib/contrast/api/decorators.rb +0 -6
- data/lib/contrast/api/dtm.pb.rb +0 -489
- data/lib/contrast/components/agent.rb +16 -12
- data/lib/contrast/components/api.rb +10 -10
- data/lib/contrast/components/app_context.rb +3 -3
- data/lib/contrast/components/app_context_extend.rb +1 -1
- data/lib/contrast/components/assess.rb +92 -38
- data/lib/contrast/components/assess_rules.rb +36 -0
- data/lib/contrast/components/config.rb +54 -12
- data/lib/contrast/components/contrast_service.rb +8 -8
- data/lib/contrast/components/heap_dump.rb +1 -1
- data/lib/contrast/components/protect.rb +5 -5
- data/lib/contrast/components/ruby_component.rb +81 -0
- data/lib/contrast/components/sampling.rb +1 -1
- data/lib/contrast/components/security_logger.rb +23 -0
- data/lib/contrast/components/service.rb +55 -0
- data/lib/contrast/components/settings.rb +12 -4
- data/lib/contrast/config/base_configuration.rb +1 -1
- data/lib/contrast/config/protect_rules_configuration.rb +17 -3
- data/lib/contrast/config/server_configuration.rb +1 -1
- data/lib/contrast/config.rb +0 -6
- data/lib/contrast/configuration.rb +81 -17
- data/lib/contrast/extension/assess/exec_trigger.rb +3 -1
- data/lib/contrast/extension/assess/marshal.rb +3 -2
- data/lib/contrast/extension/assess/string.rb +0 -1
- data/lib/contrast/extension/extension.rb +1 -1
- data/lib/contrast/framework/base_support.rb +0 -5
- data/lib/contrast/framework/grape/support.rb +1 -23
- data/lib/contrast/framework/manager.rb +0 -10
- data/lib/contrast/framework/rails/support.rb +5 -58
- data/lib/contrast/framework/sinatra/support.rb +2 -21
- data/lib/contrast/logger/cef_log.rb +21 -3
- data/lib/contrast/logger/log.rb +1 -11
- data/lib/contrast/tasks/config.rb +4 -2
- data/lib/contrast/utils/assess/event_limit_utils.rb +5 -8
- data/lib/contrast/utils/assess/trigger_method_utils.rb +10 -18
- data/lib/contrast/utils/findings.rb +6 -5
- data/lib/contrast/utils/hash_digest.rb +9 -24
- data/lib/contrast/utils/hash_digest_extend.rb +6 -6
- data/lib/contrast/utils/invalid_configuration_util.rb +21 -58
- data/lib/contrast/utils/log_utils.rb +32 -8
- data/lib/contrast/utils/net_http_base.rb +2 -2
- data/lib/contrast/utils/patching/policy/patch_utils.rb +3 -2
- data/lib/contrast/utils/stack_trace_utils.rb +0 -25
- data/lib/contrast/utils/string_utils.rb +9 -0
- data/lib/contrast/utils/telemetry_client.rb +13 -7
- data/lib/contrast.rb +5 -10
- metadata +22 -28
- data/lib/contrast/agent/reporting/reporting_events/trace_event_source.rb +0 -30
- data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -36
- data/lib/contrast/api/decorators/activity.rb +0 -33
- data/lib/contrast/api/decorators/architecture_component.rb +0 -36
- data/lib/contrast/api/decorators/finding.rb +0 -29
- data/lib/contrast/api/decorators/route_coverage.rb +0 -91
- data/lib/contrast/api/decorators/trace_event.rb +0 -120
- data/lib/contrast/api/decorators/trace_event_object.rb +0 -63
- data/lib/contrast/api/decorators/trace_event_signature.rb +0 -69
- data/lib/contrast/api/decorators/trace_taint_range.rb +0 -52
- data/lib/contrast/config/assess_configuration.rb +0 -93
- data/lib/contrast/config/assess_rules_configuration.rb +0 -32
- data/lib/contrast/config/root_configuration.rb +0 -90
- data/lib/contrast/config/ruby_configuration.rb +0 -81
- data/lib/contrast/config/service_configuration.rb +0 -49
- data/lib/contrast/utils/preflight_util.rb +0 -13
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/assess/events/event_factory'
|
|
5
5
|
require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
|
|
6
|
+
require 'contrast/agent/excluder'
|
|
6
7
|
require 'contrast/components/logger'
|
|
7
8
|
require 'contrast/utils/object_share'
|
|
8
9
|
require 'contrast/utils/sha256_builder'
|
|
@@ -23,17 +24,13 @@ module Contrast
|
|
|
23
24
|
# Contrast::Agent::Assess::Policy::TriggerNode class. Each such method will call to this module just after
|
|
24
25
|
# invocation in order to determine if the call was done safely. In those cases where it was not, a Finding
|
|
25
26
|
# report is issued to the Service.
|
|
26
|
-
module TriggerMethod
|
|
27
|
+
module TriggerMethod
|
|
27
28
|
extend Contrast::Components::Logger::InstanceMethods
|
|
28
29
|
extend Contrast::Utils::Assess::TriggerMethodUtils
|
|
29
30
|
extend Contrast::Utils::Assess::EventLimitUtils
|
|
30
31
|
|
|
31
|
-
# The level of TeamServer compliance our traces meet when in the abnormal condition of being dataflow rules
|
|
32
|
-
# without routes.
|
|
33
|
-
MINIMUM_FINDING_VERSION = 3
|
|
34
|
-
# The level of TeamServer compliance our traces meet.
|
|
35
|
-
CURRENT_FINDING_VERSION = 4
|
|
36
32
|
# Rules that always exists outside of Request Context
|
|
33
|
+
# @return [Array<String>]
|
|
37
34
|
NON_REQUEST_RULES = [
|
|
38
35
|
'hardcoded-password', # Contrast::Agent::Assess::Rule::Provider::HardcodedPassword.NAME,
|
|
39
36
|
'hardcoded-key' # Contrast::Agent::Assess::Rule::Provider::HardcodedKey.NAME
|
|
@@ -58,7 +55,9 @@ module Contrast
|
|
|
58
55
|
# else proceed, if the flag is true we don't need to check for context we
|
|
59
56
|
# go as currently.
|
|
60
57
|
# When outside of a request, only track when the feature is enabled
|
|
61
|
-
return unless
|
|
58
|
+
return unless context ||
|
|
59
|
+
Contrast::ASSESS.non_request_tracking? ||
|
|
60
|
+
NON_REQUEST_RULES.include?(trigger_node.rule_id)
|
|
62
61
|
|
|
63
62
|
if trigger_node.sources&.any?
|
|
64
63
|
trigger_node.sources.each do |marker|
|
|
@@ -74,95 +73,57 @@ module Contrast
|
|
|
74
73
|
apply_trigger(trigger_node, source, object, ret, *args)
|
|
75
74
|
end
|
|
76
75
|
|
|
77
|
-
def append_to_finding finding, event_data, request
|
|
78
|
-
finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(event_data.policy_node.rule_id)
|
|
79
|
-
finding.version = determine_compliance_version(finding)
|
|
80
|
-
append_events(finding, event_data)
|
|
81
|
-
append_route(finding, request)
|
|
82
|
-
append_hash(finding, event_data.tagged, request)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
76
|
# This converts the source of the finding, and the events leading up to it into a Finding
|
|
86
77
|
#
|
|
87
78
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
|
88
79
|
# trigger event
|
|
89
80
|
# @param source [Object] the source of the Trigger Event
|
|
90
81
|
# @param object [Object] the Object on which the method was invoked
|
|
91
|
-
# @param ret [Object] the Return of the invoked method
|
|
82
|
+
# @param ret [Object, nil] the Return of the invoked method, or nil if void method
|
|
92
83
|
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
93
|
-
# @return [Contrast::
|
|
94
|
-
# nil if conditions were not met
|
|
84
|
+
# @return [Contrast::Agent::Reporting::Finding, nil] the finding to send to TeamServer if found
|
|
95
85
|
def build_finding trigger_node, source, object, ret, *args
|
|
96
86
|
content_type = Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type
|
|
97
|
-
|
|
98
87
|
if content_type.nil? && trigger_node.collectable?
|
|
99
88
|
Contrast::Agent::FINDINGS.collect_finding(trigger_node, source, object, ret, *args)
|
|
100
89
|
return
|
|
101
90
|
end
|
|
102
|
-
|
|
103
91
|
return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
|
|
104
92
|
|
|
105
93
|
request = find_request(source)
|
|
106
94
|
return unless reportable?(request&.env)
|
|
95
|
+
return if excluded_by_url_and_rule?(request, trigger_node.rule_id)
|
|
107
96
|
|
|
108
|
-
|
|
97
|
+
finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
|
|
98
|
+
finding.attach_data(trigger_node, source, object, ret, request, *args)
|
|
99
|
+
return if excluded_by_input_and_rule?(request, finding, trigger_node.rule_id)
|
|
100
|
+
|
|
101
|
+
finding.hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source, request)
|
|
102
|
+
finding
|
|
109
103
|
rescue StandardError => e
|
|
110
104
|
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def process_reportable_finding trigger_node, source, object, ret, request, *args
|
|
114
|
-
if Contrast::Agent::Reporter.enabled?
|
|
115
|
-
handle_new_finding(trigger_node, source, object, ret, request, *args)
|
|
116
|
-
else # TODO: RUBY-1438 -- remove
|
|
117
|
-
finding = Contrast::Api::Dtm::Finding.new
|
|
118
|
-
event_data = Contrast::Agent::Assess::Events::EventData.new(trigger_node, source, object, ret, args)
|
|
119
|
-
append_to_finding(finding, event_data, request)
|
|
120
|
-
logger.trace('Finding created', node_id: trigger_node.id, source_id: source.__id__,
|
|
121
|
-
rule: trigger_node.rule_id)
|
|
122
|
-
# check here if we need to add that finding
|
|
123
|
-
report_finding(finding, request) unless not_reported?(finding)
|
|
124
|
-
end
|
|
105
|
+
nil
|
|
125
106
|
end
|
|
126
107
|
|
|
127
108
|
# Given a finding, append it to an activity message and send it to the Service for processing. If an
|
|
128
109
|
# activity message does not exist, b/c we're invoked outside of a request context, build an activity and
|
|
129
110
|
# immediately report it with the finding.
|
|
130
111
|
#
|
|
131
|
-
#
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# @param request [Contrast::Agent::Request] our wrapper around the Rack::Request.
|
|
135
|
-
def report_finding finding, request = nil
|
|
136
|
-
context = Contrast::Agent::REQUEST_TRACKER.current
|
|
137
|
-
if context
|
|
138
|
-
# REMOVE_DTM_ACTIVITY
|
|
139
|
-
context.dtm_activity.findings << finding
|
|
140
|
-
return
|
|
141
|
-
end
|
|
112
|
+
# @param finding [Contrast::Agent::Reporting::Finding] the Finding to report.
|
|
113
|
+
def report_finding finding
|
|
114
|
+
return unless finding
|
|
142
115
|
|
|
143
|
-
|
|
116
|
+
preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding)
|
|
117
|
+
return unless preflight
|
|
144
118
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if request
|
|
148
|
-
activity.request = request.dtm
|
|
149
|
-
else
|
|
150
|
-
logger.debug('Attempted to report finding without request', finding: finding)
|
|
151
|
-
end
|
|
119
|
+
preflight_data = preflight.messages[0].data
|
|
120
|
+
return if Contrast::Agent::REQUEST_TRACKER.current&.reported_findings&.include?(preflight_data)
|
|
152
121
|
|
|
153
|
-
Contrast::Agent.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
# here we will generate new type of finding
|
|
159
|
-
ruby_finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
|
|
160
|
-
ruby_finding.attach_data(trigger_node, source, object, ret, request, *args)
|
|
161
|
-
hash_code = Contrast::Utils::HashDigest.generate_event_hash(ruby_finding, source, request)
|
|
162
|
-
ruby_finding.hash_code = hash_code
|
|
163
|
-
|
|
164
|
-
new_preflight = Contrast::Agent::Reporting::BuildPreflight.build(ruby_finding, request)
|
|
165
|
-
Contrast::Agent.reporter&.send_event(new_preflight)
|
|
122
|
+
Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding
|
|
123
|
+
if Contrast::Agent::REQUEST_TRACKER.current
|
|
124
|
+
Contrast::Agent::REQUEST_TRACKER.current.reported_findings << preflight_data
|
|
125
|
+
end
|
|
126
|
+
Contrast::Agent.reporter&.send_event(preflight)
|
|
166
127
|
end
|
|
167
128
|
|
|
168
129
|
private
|
|
@@ -204,30 +165,6 @@ module Contrast
|
|
|
204
165
|
end
|
|
205
166
|
end
|
|
206
167
|
|
|
207
|
-
def append_events finding, event_data
|
|
208
|
-
append_from_source(finding, event_data.tagged)
|
|
209
|
-
finding.events << Contrast::Agent::Assess::Events::EventFactory.build(event_data).to_dtm_event
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
def append_from_source finding, source
|
|
213
|
-
return unless source
|
|
214
|
-
return unless Contrast::Agent::Assess::Tracker.trackable?(source)
|
|
215
|
-
|
|
216
|
-
properties = Contrast::Agent::Assess::Tracker.properties(source)
|
|
217
|
-
return unless properties
|
|
218
|
-
|
|
219
|
-
build_events(finding, properties.event) if properties.event
|
|
220
|
-
|
|
221
|
-
# CSGoogle::Protobuf::Map doesn't support merge!, so we have to do this long form
|
|
222
|
-
source_props = properties.properties
|
|
223
|
-
return unless source_props
|
|
224
|
-
|
|
225
|
-
source_props.each_pair do |key, value|
|
|
226
|
-
key = Contrast::Utils::StringUtils.force_utf8(key)
|
|
227
|
-
finding.properties[key] = Contrast::Utils::StringUtils.force_utf8(value)
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
|
|
231
168
|
def build_events finding, event
|
|
232
169
|
return unless event
|
|
233
170
|
|
|
@@ -239,50 +176,17 @@ module Contrast
|
|
|
239
176
|
finding.events << event.to_dtm_event
|
|
240
177
|
end
|
|
241
178
|
|
|
242
|
-
|
|
243
|
-
context = Contrast::Agent::REQUEST_TRACKER.current
|
|
244
|
-
if context
|
|
245
|
-
finding.routes << context.route if context.route
|
|
246
|
-
elsif request&.route
|
|
247
|
-
finding.routes << request.route
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
def append_hash finding, source, request
|
|
252
|
-
hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source, request)
|
|
253
|
-
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
|
|
254
|
-
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
# Because our APIs are not versioned, TeamServer relies on a field, version, to tell it what, if any,
|
|
258
|
-
# validation it can preform on the findings we send it. Examine the finding and determine the level to
|
|
259
|
-
# which it conforms.
|
|
179
|
+
# Check if the finding should be excluded due to the assess exclusion rules.
|
|
260
180
|
#
|
|
261
|
-
# @param
|
|
262
|
-
# @
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
# finding with a route is at maximum compliance
|
|
267
|
-
return CURRENT_FINDING_VERSION if finding.routes.any?
|
|
268
|
-
# any finding without events is not of a dataflow type and therefore at maximum compliance
|
|
269
|
-
return CURRENT_FINDING_VERSION unless finding.events.any?
|
|
270
|
-
|
|
271
|
-
MINIMUM_FINDING_VERSION
|
|
181
|
+
# @param request [Contrast::Agent::Request] a wrapper around the Rack::Request for the current request
|
|
182
|
+
# @param rule_id [String]
|
|
183
|
+
# return [Boolean]
|
|
184
|
+
def excluded_by_url_and_rule? request, rule_id
|
|
185
|
+
Contrast::SETTINGS.excluder.assess_excluded_by_url_and_rule?(request, rule_id)
|
|
272
186
|
end
|
|
273
187
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
#
|
|
277
|
-
# @param finding [Contrast::Api::Dtm::Finding] Finding we're about to report
|
|
278
|
-
# @return Boolean
|
|
279
|
-
def not_reported? finding
|
|
280
|
-
return false unless (current = Contrast::Agent::REQUEST_TRACKER.current)
|
|
281
|
-
|
|
282
|
-
currently_added_findings = current.dtm_activity.findings
|
|
283
|
-
return false if currently_added_findings.empty?
|
|
284
|
-
|
|
285
|
-
currently_added_findings.any? { |f| f.rule_id == finding.rule_id && f.hash_code == finding.hash_code }
|
|
188
|
+
def excluded_by_input_and_rule? request, finding, rule_id
|
|
189
|
+
Contrast::SETTINGS.excluder.assess_excluded_by_input_and_rule?(request, finding, rule_id)
|
|
286
190
|
end
|
|
287
191
|
end
|
|
288
192
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/assess/policy/trigger/reflected_xss'
|
|
5
5
|
require 'contrast/agent/assess/policy/trigger/xpath'
|
|
6
|
-
require 'contrast/
|
|
6
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_taint_range_tags'
|
|
7
7
|
require 'contrast/components/logger'
|
|
8
8
|
|
|
9
9
|
module Contrast
|
|
@@ -185,8 +185,8 @@ module Contrast
|
|
|
185
185
|
|
|
186
186
|
tags.each do |tag|
|
|
187
187
|
raise(ArgumentError, "Rule #{ rule_id } had an invalid tag. #{ tag } is not a known value.") unless
|
|
188
|
-
Contrast::
|
|
189
|
-
Contrast::
|
|
188
|
+
Contrast::Agent::Reporting::FindingEventTaintRangeTags::VALID_TAGS.include?(tag) ||
|
|
189
|
+
Contrast::Agent::Reporting::FindingEventTaintRangeTags::VALID_SOURCE_TAGS.include?(tag)
|
|
190
190
|
end
|
|
191
191
|
end
|
|
192
192
|
|
|
@@ -42,24 +42,14 @@ module Contrast
|
|
|
42
42
|
return unless event.source_type
|
|
43
43
|
return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
|
|
44
44
|
|
|
45
|
-
append_to_ruby_object(current_request, event)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# @param current_request [Contrast::Agent::RequestContext]
|
|
49
|
-
# @param event [Contrast::Agent::Assess::Events::ContrastEvent]
|
|
50
|
-
def append_to_ruby_object current_request, event
|
|
51
45
|
if current_request.observed_route.sources.any? do |source|
|
|
52
|
-
source.type == event.
|
|
53
|
-
source.name == event.forced_source_name # rubocop:disable Security/Module/Name
|
|
46
|
+
source.type == event.source_type && source.name == event.source_name # rubocop:disable Security/Module/Name
|
|
54
47
|
end
|
|
55
48
|
|
|
56
49
|
return
|
|
57
50
|
end
|
|
58
51
|
|
|
59
|
-
event_source
|
|
60
|
-
return unless event_source
|
|
61
|
-
|
|
62
|
-
current_request.observed_route.sources << event_source
|
|
52
|
+
current_request.observed_route.sources << event.event_source if event.event_source
|
|
63
53
|
end
|
|
64
54
|
end
|
|
65
55
|
end
|
|
@@ -11,9 +11,8 @@ module Contrast
|
|
|
11
11
|
module Assess
|
|
12
12
|
module Rule
|
|
13
13
|
module Provider
|
|
14
|
-
# Hardcoded rules detect if any secret value has been written
|
|
15
|
-
#
|
|
16
|
-
# class, a provider must implement three methods:
|
|
14
|
+
# Hardcoded rules detect if any secret value has been written directly into the sourcecode of the
|
|
15
|
+
# application. To use this base class, a provider must implement three methods:
|
|
17
16
|
# 1) name_passes? : does the constant name match a given value set
|
|
18
17
|
# 2) value_node_passes? : does the value of the constant match a
|
|
19
18
|
# given value set
|
|
@@ -21,23 +20,21 @@ module Contrast
|
|
|
21
20
|
module HardcodedValueRule
|
|
22
21
|
include Contrast::Components::Logger::InstanceMethods
|
|
23
22
|
|
|
23
|
+
# @return [Boolean]
|
|
24
24
|
def disabled?
|
|
25
25
|
!::Contrast::ASSESS.enabled? || ::Contrast::ASSESS.rule_disabled?(rule_id)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# Parse the file pertaining to the given TracePoint to walk its AST
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# String or through a method call.
|
|
28
|
+
# Parse the file pertaining to the given TracePoint to walk its AST to determine if a Constant is
|
|
29
|
+
# hardcoded. For our purposes, this hard coding means directly set rather than as an interpolated String or
|
|
30
|
+
# through a method call.
|
|
32
31
|
#
|
|
33
|
-
# Note: This is a top layer check, we make no assertions about what
|
|
34
|
-
#
|
|
35
|
-
# calling a hardcoded thing, causes this check to not report.
|
|
32
|
+
# Note: This is a top layer check, we make no assertions about what the methods or interpolations do.
|
|
33
|
+
# Their presence, even if only calling a hardcoded thing, causes this check to not report.
|
|
36
34
|
#
|
|
37
|
-
# @param trace_point [TracePoint] the TracePoint event created on
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# tree of the Module defined in the TracePoint end event
|
|
35
|
+
# @param trace_point [TracePoint] the TracePoint event created on the :end of a Module being loaded
|
|
36
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node] the abstract syntax tree of the Module defined in the
|
|
37
|
+
# TracePoint end event
|
|
41
38
|
def parse trace_point, ast
|
|
42
39
|
return if disabled?
|
|
43
40
|
|
|
@@ -56,8 +53,8 @@ module Contrast
|
|
|
56
53
|
private
|
|
57
54
|
|
|
58
55
|
# @param mod [Module] the module to which this AST pertains
|
|
59
|
-
# @param ast [RubyVM::AbstractSyntaxTree::Node, Object] a node
|
|
60
|
-
#
|
|
56
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node, Object] a node within the AST, which may be a leaf, so any
|
|
57
|
+
# Object
|
|
61
58
|
def parse_ast mod, ast
|
|
62
59
|
return unless ast.cs__is_a?(RubyVM::AbstractSyntaxTree::Node)
|
|
63
60
|
return unless ast.cs__respond_to?(:children)
|
|
@@ -72,32 +69,27 @@ module Contrast
|
|
|
72
69
|
# https://www.rubydoc.info/gems/ruby-internal/Node/CDECL
|
|
73
70
|
return unless ast.type == :CDECL
|
|
74
71
|
|
|
75
|
-
# The CDECL Node has two children, the first being the Constant
|
|
76
|
-
#
|
|
77
|
-
# constant
|
|
72
|
+
# The CDECL Node has two children, the first being the Constant name as a symbol, the second as the value
|
|
73
|
+
# to assign to that constant
|
|
78
74
|
children = ast.children
|
|
79
75
|
name = children[0].to_s
|
|
80
76
|
# If that constant name doesn't pass our checks, move on.
|
|
81
77
|
return unless name_passes?(name)
|
|
82
78
|
|
|
83
79
|
value = children[1]
|
|
84
|
-
# The assignment node could be a direct value or a call of some
|
|
85
|
-
#
|
|
80
|
+
# The assignment node could be a direct value or a call of some sort. We leave it to each rule to
|
|
81
|
+
# properly handle these nodes.
|
|
86
82
|
return unless value_node_passes?(value)
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
build_finding(mod, name)
|
|
92
|
-
end
|
|
84
|
+
build_and_report(mod, name)
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
logger.error('Unable to parse AST for Hardcoded Rule analysis.', e)
|
|
93
87
|
end
|
|
94
88
|
|
|
95
|
-
# Constants can be set as frozen directly. We need to account for
|
|
96
|
-
#
|
|
97
|
-
# a :CALL, not a constant.
|
|
89
|
+
# Constants can be set as frozen directly. We need to account for this change as it means the Node given to
|
|
90
|
+
# the :CDECL call will be a :CALL, not a constant.
|
|
98
91
|
#
|
|
99
|
-
# @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to
|
|
100
|
-
# evaluate
|
|
92
|
+
# @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to evaluate
|
|
101
93
|
# @return [Boolean] is this a freeze call or not
|
|
102
94
|
def freeze_call? value_node
|
|
103
95
|
return false unless value_node.type == :CALL
|
|
@@ -109,63 +101,29 @@ module Contrast
|
|
|
109
101
|
children[1] == :freeze
|
|
110
102
|
end
|
|
111
103
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
finding =
|
|
116
|
-
|
|
117
|
-
# The only place we still use dtm activity
|
|
118
|
-
activity = Contrast::Api::Dtm::Activity.new
|
|
119
|
-
activity.findings << finding
|
|
120
|
-
Contrast::Agent.messaging_queue.send_event_eventually(activity, force: true)
|
|
121
|
-
rescue StandardError => e
|
|
122
|
-
logger.error('Unable to build a finding for Hardcoded Rule', e)
|
|
123
|
-
nil
|
|
124
|
-
end
|
|
104
|
+
# @param mod [Module] the module to which this AST pertains
|
|
105
|
+
# @param name [String] the name of the hardcoded constant
|
|
106
|
+
def build_and_report mod, name
|
|
107
|
+
finding = build_finding(mod, name)
|
|
108
|
+
return unless finding
|
|
125
109
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
# @return [Contrast::Api::Dtm::Finding]
|
|
129
|
-
def assign_finding class_name, constant_string
|
|
130
|
-
finding = Contrast::Api::Dtm::Finding.new
|
|
131
|
-
finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(rule_id)
|
|
132
|
-
finding.version = Contrast::Agent::Assess::Policy::TriggerMethod::CURRENT_FINDING_VERSION
|
|
133
|
-
|
|
134
|
-
finding.properties[SOURCE_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(class_name)
|
|
135
|
-
finding.properties[CONSTANT_NAME_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(constant_string)
|
|
136
|
-
finding.properties[CODE_SOURCE_KEY] =
|
|
137
|
-
Contrast::Utils::StringUtils.protobuf_safe_string(constant_string + redacted_marker)
|
|
138
|
-
|
|
139
|
-
hash = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
|
|
140
|
-
finding.hash_code = Contrast::Utils::StringUtils.protobuf_safe_string(hash)
|
|
141
|
-
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
|
142
|
-
finding
|
|
143
|
-
end
|
|
110
|
+
preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding)
|
|
111
|
+
return unless preflight
|
|
144
112
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
# sent to reporter
|
|
149
|
-
# and add logger message for the report of the preflight
|
|
150
|
-
new_preflight = Contrast::Agent::Reporting::Preflight.new
|
|
151
|
-
new_preflight_message = Contrast::Agent::Reporting::PreflightMessage.new
|
|
152
|
-
new_preflight_message.hash_code = hash
|
|
153
|
-
new_preflight_message.data = "#{ rule_id },#{ hash }"
|
|
154
|
-
new_preflight.messages << new_preflight_message
|
|
155
|
-
|
|
156
|
-
# extract to new method
|
|
157
|
-
# here we will generate new type of finding
|
|
158
|
-
ruby_finding = Contrast::Agent::Reporting::Finding.new(rule_id)
|
|
159
|
-
ruby_finding.hash_code = hash
|
|
160
|
-
ruby_finding.properties[SOURCE_KEY] = clazz.cs__name
|
|
161
|
-
ruby_finding.properties[CONSTANT_NAME_KEY] = constant_string
|
|
162
|
-
ruby_finding.properties[CODE_SOURCE_KEY] = constant_string + redacted_marker
|
|
163
|
-
save_and_report_finding(ruby_finding, new_preflight)
|
|
113
|
+
Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding
|
|
114
|
+
Contrast::Agent.reporter&.send_event(preflight)
|
|
164
115
|
end
|
|
165
116
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
117
|
+
# @param clazz [Class] the Class or Module containing the constant
|
|
118
|
+
# @param constant_string [String]
|
|
119
|
+
# @return [Contrast::Agent::Reporting::Finding]
|
|
120
|
+
def build_finding clazz, constant_string
|
|
121
|
+
finding = Contrast::Agent::Reporting::Finding.new(rule_id)
|
|
122
|
+
finding.properties[SOURCE_KEY] = clazz.cs__name
|
|
123
|
+
finding.properties[CONSTANT_NAME_KEY] = constant_string
|
|
124
|
+
finding.properties[CODE_SOURCE_KEY] = constant_string + redacted_marker
|
|
125
|
+
finding.hash_code = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
|
|
126
|
+
finding
|
|
169
127
|
end
|
|
170
128
|
end
|
|
171
129
|
end
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
require 'rack'
|
|
5
5
|
require 'json'
|
|
6
|
-
require 'contrast/agent/reporting/reporting_utilities/dtm_message'
|
|
7
6
|
require 'contrast/agent/reporting/reporting_utilities/build_preflight'
|
|
8
7
|
require 'contrast/utils/hash_digest'
|
|
9
|
-
require 'contrast/utils/preflight_util'
|
|
10
8
|
require 'contrast/utils/string_utils'
|
|
11
9
|
|
|
12
10
|
module Contrast
|
|
@@ -32,18 +30,11 @@ module Contrast
|
|
|
32
30
|
finding = build_finding(violation)
|
|
33
31
|
return unless finding
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Contrast::Agent.reporter&.send_event(preflight)
|
|
41
|
-
else
|
|
42
|
-
Contrast::Agent.reporter&.send_event(report)
|
|
43
|
-
end
|
|
44
|
-
else
|
|
45
|
-
Contrast::Agent::REQUEST_TRACKER.current.activity.findings << finding
|
|
46
|
-
end
|
|
33
|
+
preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding)
|
|
34
|
+
return unless preflight
|
|
35
|
+
|
|
36
|
+
Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding
|
|
37
|
+
Contrast::Agent.reporter&.send_event(preflight)
|
|
47
38
|
end
|
|
48
39
|
|
|
49
40
|
protected
|
|
@@ -75,16 +66,15 @@ module Contrast
|
|
|
75
66
|
# Convert the given evidence into a finding. The rule will populate this evidence with each of the
|
|
76
67
|
#
|
|
77
68
|
# @param evidence [Hash] the properties required to build this finding.
|
|
78
|
-
# @return [Contrast::
|
|
69
|
+
# @return [Contrast::Agent::Reporting::Finding]
|
|
79
70
|
def build_finding evidence
|
|
80
|
-
finding = Contrast::
|
|
81
|
-
finding.rule_id = rule_id
|
|
71
|
+
finding = Contrast::Agent::Reporting::Finding.new(rule_id)
|
|
82
72
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
|
83
|
-
finding.routes << context.
|
|
73
|
+
finding.routes << context.discovered_route if context&.discovered_route
|
|
84
74
|
build_evidence(evidence, finding)
|
|
75
|
+
finding.request = Contrast::Agent::Reporting::FindingRequest.convert(context.request) if context&.request
|
|
85
76
|
hash = Contrast::Utils::HashDigest.generate_config_hash(finding)
|
|
86
77
|
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash)
|
|
87
|
-
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
|
88
78
|
finding
|
|
89
79
|
end
|
|
90
80
|
|
|
@@ -92,16 +82,10 @@ module Contrast
|
|
|
92
82
|
# Change it accordingly the rule you work on
|
|
93
83
|
#
|
|
94
84
|
# @param evidence [Hash] the properties required to build this finding.
|
|
95
|
-
# @param finding [Contrast::
|
|
85
|
+
# @param finding [Contrast::Agent::Reporting::Finding] finding to attach the evidence to
|
|
96
86
|
def build_evidence evidence, finding
|
|
97
87
|
evidence.each_pair do |key, value|
|
|
98
|
-
finding.properties[key] =
|
|
99
|
-
Contrast::Utils::StringUtils.protobuf_format(value.to_json)
|
|
100
|
-
elsif value.cs__is_a?(Array)
|
|
101
|
-
value.map { Contrast::Utils::StringUtils.protobuf_format(_1) }.to_s
|
|
102
|
-
else
|
|
103
|
-
Contrast::Utils::StringUtils.protobuf_format(value)
|
|
104
|
-
end
|
|
88
|
+
finding.properties[key] = value
|
|
105
89
|
end
|
|
106
90
|
end
|
|
107
91
|
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'rack'
|
|
5
|
-
require 'contrast/agent/reporting/reporting_utilities/dtm_message'
|
|
6
5
|
require 'contrast/utils/hash_digest'
|
|
7
|
-
require 'contrast/utils/preflight_util'
|
|
8
6
|
require 'contrast/utils/string_utils'
|
|
9
7
|
require 'contrast/agent/assess/rule/response/base_rule'
|
|
10
8
|
|
|
@@ -22,7 +20,7 @@ module Contrast
|
|
|
22
20
|
START_PROP = 'start'.cs__freeze
|
|
23
21
|
END_PROP = 'end'.cs__freeze
|
|
24
22
|
FORM_START_REGEXP = /<form/i.cs__freeze
|
|
25
|
-
META_TYPE = '
|
|
23
|
+
META_TYPE = 'META tag'
|
|
26
24
|
PRAGMA = 'pragma'
|
|
27
25
|
|
|
28
26
|
# Rules discern which responses they can/should analyze.
|