Path matching

This commit is contained in:
Dan Milne
2025-11-17 12:12:17 +11:00
parent 093ee71c9f
commit 830810305b
14 changed files with 721 additions and 45 deletions

View File

@@ -6,12 +6,10 @@
# Network rules are associated with NetworkRange objects for rich context.
class Rule < ApplicationRecord
# Rule enums (prefix needed to avoid rate_limit collision)
enum :waf_action, { allow: 0, deny: 1, rate_limit: 2, redirect: 3, log: 4, challenge: 5 }, prefix: :action
# Canonical WAF action order - aligned with Agent and Event models
enum :waf_action, { deny: 0, allow: 1, redirect: 2, challenge: 3, log: 4 }, prefix: :action
enum :waf_rule_type, { network: 0, rate_limit: 1, path_pattern: 2 }, prefix: :type
# Legacy string constants for backward compatibility
RULE_TYPES = %w[network rate_limit path_pattern].freeze
ACTIONS = %w[allow deny rate_limit redirect log challenge].freeze
SOURCES = %w[manual auto:scanner_detected auto:rate_limit_exceeded auto:bot_detected imported default manual:surgical_block manual:surgical_exception policy].freeze
# Associations
@@ -27,14 +25,6 @@ class Rule < ApplicationRecord
validates :enabled, inclusion: { in: [true, false] }
validates :source, inclusion: { in: SOURCES }
# Legacy enum definitions (disabled to prevent conflicts)
# enum :action, { allow: "allow", deny: "deny", rate_limit: "rate_limit", redirect: "redirect", log: "log", challenge: "challenge" }, scopes: false
# enum :rule_type, { network: "network", rate_limit: "rate_limit", path_pattern: "path_pattern" }, scopes: false
# Legacy validations for backward compatibility during transition
# validates :rule_type, presence: true, inclusion: { in: RULE_TYPES }, allow_nil: true
# validates :action, presence: true, inclusion: { in: ACTIONS }, allow_nil: true
# Custom validations
validate :validate_conditions_by_type
validate :validate_metadata_by_action
@@ -356,12 +346,12 @@ class Rule < ApplicationRecord
[block_rule, exception_rule]
end
def self.create_rate_limit_rule(cidr, limit:, window:, user: nil, **options)
def self.create_rate_limit_rule(cidr, limit:, window:, user: nil, action: 'deny', **options)
network_range = NetworkRange.find_or_create_by_cidr(cidr, user: user, source: 'user_created')
create!(
waf_rule_type: 'rate_limit',
waf_action: 'rate_limit',
waf_action: action, # Action to take when rate limit exceeded (deny, redirect, challenge, log)
network_range: network_range,
conditions: { cidr: cidr, scope: 'ip' },
metadata: {
@@ -514,10 +504,6 @@ class Rule < ApplicationRecord
if challenge_type_value && !%w[captcha javascript proof_of_work].include?(challenge_type_value)
errors.add(:metadata, "challenge_type must be one of: captcha, javascript, proof_of_work")
end
when "rate_limit"
unless metadata&.dig("limit").present? && metadata&.dig("window").present?
errors.add(:metadata, "must include 'limit' and 'window' for rate_limit action")
end
end
end