126 lines
3.8 KiB
Ruby
126 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class ProcessWafAnalyticsJob < ApplicationJob
|
|
queue_as :waf_analytics
|
|
|
|
def perform(project_id:, event_id:)
|
|
project = Project.find(project_id)
|
|
event = Event.find(event_id)
|
|
|
|
# Analyze event patterns
|
|
analyze_traffic_patterns(project, event)
|
|
analyze_geographic_distribution(project, event)
|
|
analyze_attack_vectors(project, event)
|
|
|
|
# Update project analytics cache
|
|
update_project_analytics(project)
|
|
|
|
rescue => e
|
|
Rails.logger.error "Error processing WAF analytics: #{e.message}"
|
|
Rails.logger.error e.backtrace.join("\n")
|
|
end
|
|
|
|
private
|
|
|
|
def analyze_traffic_patterns(project, event)
|
|
# Look for unusual traffic spikes
|
|
recent_events = project.events.where(timestamp: 5.minutes.ago..Time.current)
|
|
|
|
if recent_events.count > project.rate_limit_threshold * 5
|
|
# High traffic detected - create an issue
|
|
Issue.create!(
|
|
project: project,
|
|
title: "High Traffic Spike Detected",
|
|
description: "Detected #{recent_events.count} requests in the last 5 minutes",
|
|
severity: "medium",
|
|
event_id: event.id,
|
|
metadata: {
|
|
event_count: recent_events.count,
|
|
time_window: "5 minutes",
|
|
threshold: project.rate_limit_threshold * 5
|
|
}
|
|
)
|
|
end
|
|
end
|
|
|
|
def analyze_geographic_distribution(project, event)
|
|
return unless event.country_code.present?
|
|
|
|
# Check if this country is unusual for this project
|
|
country_events = project.events
|
|
.where(country_code: event.country_code)
|
|
.where(timestamp: 1.hour.ago..Time.current)
|
|
|
|
# If this is the first event from this country or unusual spike
|
|
if country_events.count == 1 || country_events.count > 100
|
|
Rails.logger.info "Unusual geographic activity from #{event.country_code} for project #{project.slug}"
|
|
end
|
|
end
|
|
|
|
def analyze_attack_vectors(project, event)
|
|
return unless event.blocked?
|
|
|
|
# Analyze common attack patterns
|
|
analyze_ip_reputation(project, event)
|
|
analyze_user_agent_patterns(project, event)
|
|
analyze_path_attacks(project, event)
|
|
end
|
|
|
|
def analyze_ip_reputation(project, event)
|
|
return unless event.ip_address.present?
|
|
|
|
# Count recent blocks from this IP
|
|
recent_blocks = project.events
|
|
.by_ip(event.ip_address)
|
|
.blocked
|
|
.where(timestamp: 1.hour.ago..Time.current)
|
|
|
|
if recent_blocks.count >= 5
|
|
# Suggest automatic IP block
|
|
project.add_ip_rule(
|
|
event.ip_address,
|
|
'block',
|
|
expires_at: 24.hours.from_now,
|
|
reason: "Automated block: #{recent_blocks.count} violations in 1 hour"
|
|
)
|
|
end
|
|
end
|
|
|
|
def analyze_user_agent_patterns(project, event)
|
|
return unless event.user_agent.present?
|
|
|
|
# Look for common bot/user agent patterns
|
|
suspicious_patterns = [
|
|
/bot/i, /crawler/i, /spider/i, /scanner/i,
|
|
/python/i, /curl/i, /wget/i, /nmap/i
|
|
]
|
|
|
|
if suspicious_patterns.any? { |pattern| event.user_agent.match?(pattern) }
|
|
# Log suspicious user agent for potential rule generation
|
|
Rails.logger.info "Suspicious user agent detected: #{event.user_agent}"
|
|
end
|
|
end
|
|
|
|
def analyze_path_attacks(project, event)
|
|
return unless event.request_path.present?
|
|
|
|
# Look for common attack paths
|
|
attack_patterns = [
|
|
/\.\./, # Directory traversal
|
|
/admin/i, # Admin panel access
|
|
/wp-admin/i, # WordPress admin
|
|
/\.php/i, # PHP files
|
|
/union.*select/i, # SQL injection
|
|
/script.*>/i # XSS attempts
|
|
]
|
|
|
|
if attack_patterns.any? { |pattern| event.request_path.match?(pattern) }
|
|
Rails.logger.info "Potential attack path detected: #{event.request_path}"
|
|
end
|
|
end
|
|
|
|
def update_project_analytics(project)
|
|
# Update cached analytics for faster dashboard loading
|
|
Rails.cache.delete("project_#{project.id}_analytics")
|
|
end
|
|
end |