Files
baffle-hub/app/jobs/generate_waf_rules_job.rb
2025-11-03 17:37:28 +11:00

171 lines
5.0 KiB
Ruby

# frozen_string_literal: true
class GenerateWafRulesJob < ApplicationJob
queue_as :waf_rules
def perform(project_id:, event_id:)
project = Project.find(project_id)
event = Event.find(event_id)
# Only analyze blocked events for rule generation
return unless event.blocked?
# Generate different types of rules based on patterns
generate_ip_rules(project, event)
generate_path_rules(project, event)
generate_user_agent_rules(project, event)
generate_parameter_rules(project, event)
# Notify project of new rules
project.broadcast_rules_refresh
rescue => e
Rails.logger.error "Error generating WAF rules: #{e.message}"
Rails.logger.error e.backtrace.join("\n")
end
private
def generate_ip_rules(project, event)
return unless event.ip_address.present?
# Check if this IP has multiple violations
violation_count = project.events
.by_ip(event.ip_address)
.blocked
.where(timestamp: 24.hours.ago..Time.current)
.count
# Auto-block IPs with 10+ violations in 24 hours
if violation_count >= 10 && !project.blocked_ips.include?(event.ip_address)
project.add_ip_rule(
event.ip_address,
'block',
expires_at: 7.days.from_now,
reason: "Auto-generated: #{violation_count} violations in 24 hours"
)
Rails.logger.info "Auto-blocked IP #{event.ip_address} for project #{project.slug}"
end
end
def generate_path_rules(project, event)
return unless event.request_path.present?
# Look for repeated attack patterns on specific paths
path_violations = project.events
.where(request_path: event.request_path)
.blocked
.where(timestamp: 1.hour.ago..Time.current)
.count
# Suggest path rules if 20+ violations on same path
if path_violations >= 20
suggest_path_rule(project, event.request_path, path_violations)
end
end
def generate_user_agent_rules(project, event)
return unless event.user_agent.present?
# Look for malicious user agents
ua_violations = project.events
.by_user_agent(event.user_agent)
.blocked
.where(timestamp: 1.hour.ago..Time.current)
.count
# Suggest user agent rules if 15+ violations from same UA
if ua_violations >= 15
suggest_user_agent_rule(project, event.user_agent, ua_violations)
end
end
def generate_parameter_rules(project, event)
params = event.query_params
return unless params.present?
# Look for suspicious parameter patterns
params.each do |key, value|
next unless value.is_a?(String)
# Check for common attack patterns in parameter values
if contains_attack_pattern?(value)
param_violations = project.events
.where("payload LIKE ?", "%#{key}%#{value}%")
.blocked
.where(timestamp: 6.hours.ago..Time.current)
.count
if param_violations >= 5
suggest_parameter_rule(project, key, value, param_violations)
end
end
end
end
def suggest_path_rule(project, path, violation_count)
# Create an issue for manual review
Issue.create!(
project: project,
title: "Suggested Path Rule",
description: "Path '#{path}' has #{violation_count} violations in 1 hour",
severity: "low",
metadata: {
type: "path_rule",
path: path,
violation_count: violation_count,
suggested_action: "block"
}
)
end
def suggest_user_agent_rule(project, user_agent, violation_count)
# Create an issue for manual review
Issue.create!(
project: project,
title: "Suggested User Agent Rule",
description: "User Agent '#{user_agent}' has #{violation_count} violations in 1 hour",
severity: "low",
metadata: {
type: "user_agent_rule",
user_agent: user_agent,
violation_count: violation_count,
suggested_action: "block"
}
)
end
def suggest_parameter_rule(project, param_name, param_value, violation_count)
# Create an issue for manual review
Issue.create!(
project: project,
title: "Suggested Parameter Rule",
description: "Parameter '#{param_name}' with suspicious values has #{violation_count} violations",
severity: "medium",
metadata: {
type: "parameter_rule",
param_name: param_name,
param_value: param_value,
violation_count: violation_count,
suggested_action: "block"
}
)
end
def contains_attack_pattern?(value)
# Common attack patterns
attack_patterns = [
/<script/i, # XSS
/union.*select/i, # SQL injection
/\.\./, # Directory traversal
/\/etc\/passwd/i, # File inclusion
/cmd\.exe/i, # Command injection
/eval\(/i, # Code injection
/javascript:/i, # JavaScript protocol
/onload=/i, # Event handler injection
]
attack_patterns.any? { |pattern| value.match?(pattern) }
end
end