# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/components/logger' require 'contrast/utils/object_share' require 'contrast/utils/duck_utils' require 'contrast/agent/reporting/reporting_events/reportable_hash' require 'contrast/agent/reporting/attack_result/response_type' require 'contrast/agent/reporting/reporting_events/application_defend_attack_activity' module Contrast module Agent module Reporting # This is the new AttackerActivity class which will includes the attacker information discovered during this # activity period. class ApplicationDefendAttackerActivity < Contrast::Agent::Reporting::ReportableHash # @return [Hash] map of rule-id to violated # samples for that rule attr_accessor :protection_rules # @return [String, nil] the IP address of the request from which the attack originated; used to identify unique # attackers attr_reader :source_ip # @return [String, nil] the X-Forwarded-For Header of the request from which the attack originated; used to # identify unique attackers attr_reader :source_forwarded_for # @param ia_request [Contrast::Agent::Reporting::FindingRequest, nil] Worthwatching worker input_analysis # saved request. def initialize ia_request: nil @protection_rules = {} if (req = ia_request || Contrast::Agent::REQUEST_TRACKER.current&.request) @source_ip = req.ip || Contrast::Utils::ObjectShare::EMPTY_STRING @source_forwarded_for = req.headers['X-Forwarded-For'] end @event_type = :application_defend_attacker_activity super() end def to_controlled_hash validate { protectionRules: process_protection_rules, source: { ip: source_ip, xForwardedFor: source_forwarded_for } } end def validate if Contrast::Utils::DuckUtils.empty_duck?(protection_rules) # rubocop:disable Style/GuardClause raise(ArgumentError, 'Protection Rules are not presented') elsif Contrast::Utils::DuckUtils.empty_duck?(source_ip) raise(ArgumentError, 'Source is not presented') end nil end # @param attack_result [Contrast::Agent::Reporting::AttackResult] def attach_data attack_result @protection_rules[attack_result.rule_id] = Contrast::Agent::Reporting::ApplicationDefendAttackActivity.new. tap do |activity| activity.attach_data(attack_result) end end def process_protection_rules hsh = {} @protection_rules.each_pair do |rule_id, attack_activity| hsh[rule_id] = attack_activity.to_controlled_hash end hsh end end end end end