Files
baffle-hub/app/jobs/process_waf_policies_job.rb
2025-11-11 16:54:52 +11:00

115 lines
4.3 KiB
Ruby

# frozen_string_literal: true
# ProcessWafPoliciesJob - Process firewall policies for a network range
#
# This job checks network ranges against active WAF policies and generates
# specific rules when matches are found.
class ProcessWafPoliciesJob < ApplicationJob
queue_as :waf_policies
retry_on StandardError, wait: 5.seconds, attempts: 3
def perform(network_range_id:, event_id: nil)
# Find the network range
network_range = NetworkRange.find_by(id: network_range_id)
return if network_range.nil?
Rails.logger.debug "Processing WAF policies for network range #{network_range.cidr}"
# Use WafPolicyMatcher to find and generate rules
begin
matcher = WafPolicyMatcher.new(network_range: network_range)
result = matcher.match_and_generate_rules
rescue => e
Rails.logger.error "WafPolicyMatcher failed for network range #{network_range.cidr}: #{e.message}"
result = { matching_policies: [], generated_rules: [] }
end
# Log results
if result[:matching_policies].any?
Rails.logger.info "Network range #{network_range.cidr} matched #{result[:matching_policies].length} policies"
result[:matching_policies].each do |policy|
Rails.logger.info " - Matched policy: #{policy.name} (#{policy.policy_type}: #{policy.action})"
end
end
if result[:generated_rules].any?
Rails.logger.info "Generated #{result[:generated_rules].length} rules for network range #{network_range.cidr}"
result[:generated_rules].each do |rule|
Rails.logger.info " - Rule: #{rule.rule_type} #{rule.action} for #{rule.network_range&.cidr} (ID: #{rule.id})"
# Log if this is a redirect or challenge rule
if rule.redirect_action?
Rails.logger.info " Redirect to: #{rule.redirect_url} (#{rule.redirect_status})"
elsif rule.challenge_action?
Rails.logger.info " Challenge type: #{rule.challenge_type}"
end
end
else
Rails.logger.debug "No matching policies found for network range #{network_range.cidr}"
end
# Mark network range as evaluated
network_range.update_column(:policies_evaluated_at, Time.current)
# Update event record if provided
if event_id.present?
event = Event.find_by(id: event_id)
if event.present?
# Add policy match information to event metadata
# Handle potential nil payload or type issues
current_payload = event.payload || {}
# Ensure payload is a hash before merging
unless current_payload.is_a?(Hash)
Rails.logger.warn "Event #{event_id} has invalid payload type: #{current_payload.class}, resetting to hash"
current_payload = {}
end
event.update!(payload: current_payload.merge({
policy_matches: {
matching_policies_count: result[:matching_policies].length,
generated_rules_count: result[:generated_rules].length,
processed_at: Time.current.iso8601
}
}))
else
Rails.logger.warn "Event #{event_id} not found for ProcessWafPoliciesJob, skipping update"
end
end
end
# Class method for batch processing multiple network ranges
def self.process_network_ranges(network_range_ids)
network_range_ids.each do |network_range_id|
perform_later(network_range_id: network_range_id)
end
end
# Class method to reprocess all network ranges for a specific policy
def self.reprocess_for_policy(waf_policy)
waf_policy_id = waf_policy.is_a?(WafPolicy) ? waf_policy.id : waf_policy
# Find all network ranges that could match this policy type
network_ranges = case waf_policy.policy_type
when 'country'
NetworkRange.where.not(country: nil)
when 'asn'
NetworkRange.where.not(asn: nil)
when 'company'
NetworkRange.where.not(company: nil)
when 'network_type'
NetworkRange.where("is_datacenter = ? OR is_proxy = ? OR is_vpn = ?", true, true, true)
else
NetworkRange.none
end
Rails.logger.info "Reprocessing #{network_ranges.count} network ranges for policy #{waf_policy_id}"
network_ranges.find_each do |network_range|
perform_later(network_range_id: network_range.id)
end
end
end