lib/contrast/agent/assess/policy/trigger_method.rb in contrast-agent-6.14.0 vs lib/contrast/agent/assess/policy/trigger_method.rb in contrast-agent-6.15.0
- old
+ new
@@ -1,12 +1,13 @@
# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: true
require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
-require 'contrast/agent/excluder'
+require 'contrast/agent/excluder/excluder'
require 'contrast/components/logger'
require 'contrast/utils/object_share'
+require 'contrast/utils/duck_utils'
require 'contrast/utils/sha256_builder'
require 'contrast/utils/assess/trigger_method_utils'
require 'contrast/agent/assess/events/event_data'
require 'contrast/agent/reporting/reporting_events/preflight'
require 'contrast/agent/reporting/reporting_events/application_activity'
@@ -97,10 +98,11 @@
finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
finding.attach_data(trigger_node, source, object, ret, request, *args)
return if excluded_by_input_and_rule?(request, finding, trigger_node.rule_id)
finding.hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source, request)
+ check_for_stored_xss(finding)
finding
rescue StandardError => e
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
nil
end
@@ -185,9 +187,42 @@
Contrast::SETTINGS.excluder.assess_excluded_by_url_and_rule?(request, rule_id)
end
def excluded_by_input_and_rule? request, finding, rule_id
Contrast::SETTINGS.excluder.assess_excluded_by_input_and_rule?(request, finding, rule_id)
+ end
+
+ # Handles the Stored Xss rule. If a vector is stored in the database
+ def check_for_stored_xss finding
+ return unless finding && finding.rule_id == 'reflected-xss'
+
+ # Check for database tainted event propagation:
+ if finding.events.select { |event| event.reportable_tags.include?('DATABASE_WRITE') }.
+ any? && !Contrast::ASSESS.disabled_rules.include?('stored-xss')
+
+ # Override 'reflected-xss' => 'stored-xss'
+ finding.instance_variable_set(:@rule_id, 'stored-xss')
+ extract_dynamic_source_info(finding)
+ finding
+ end
+ end
+
+ def extract_dynamic_source_info finding
+ return unless finding
+
+ creation_events = finding.events.select { |e| e.action == :CREATION }
+ source_event_policy = creation_events[0].policy_node if creation_events.any?
+ properties = source_event_policy.properties if source_event_policy
+ # Properties example:
+ # => {"dynamic_source_id"=>"Assess:Source:Comment#message",
+ # "dynamic_source_name"=>"Comment.message",
+ # "readTable"=>"Comment",
+ # "readColumn"=>:message,
+ # "writeDateTimeUtc"=>1675087341948,
+ # "writeRequestUrl"=>"/comments"}
+ return if Contrast::Utils::DuckUtils.empty_duck?(properties)
+
+ finding.instance_variable_set(:@properties, finding.properties.merge(properties))
end
end
end
end
end