First commit!
This commit is contained in:
126
app/models/rule.rb
Normal file
126
app/models/rule.rb
Normal file
@@ -0,0 +1,126 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Rule < ApplicationRecord
|
||||
belongs_to :rule_set
|
||||
|
||||
validates :rule_type, presence: true, inclusion: { in: RuleSet::RULE_TYPES }
|
||||
validates :target, presence: true
|
||||
validates :action, presence: true, inclusion: { in: RuleSet::ACTIONS }
|
||||
validates :priority, presence: true, numericality: { greater_than: 0 }
|
||||
|
||||
scope :enabled, -> { where(enabled: true) }
|
||||
scope :by_priority, -> { order(priority: :desc, created_at: :desc) }
|
||||
scope :expired, -> { where("expires_at < ?", Time.current) }
|
||||
scope :not_expired, -> { where("expires_at IS NULL OR expires_at > ?", Time.current) }
|
||||
|
||||
# Check if rule is currently active
|
||||
def active?
|
||||
enabled? && (expires_at.nil? || expires_at > Time.current)
|
||||
end
|
||||
|
||||
# Check if rule matches given request context
|
||||
def matches?(context)
|
||||
return false unless active?
|
||||
|
||||
case rule_type
|
||||
when 'ip'
|
||||
match_ip_rule?(context)
|
||||
when 'cidr'
|
||||
match_cidr_rule?(context)
|
||||
when 'path'
|
||||
match_path_rule?(context)
|
||||
when 'user_agent'
|
||||
match_user_agent_rule?(context)
|
||||
when 'parameter'
|
||||
match_parameter_rule?(context)
|
||||
when 'method'
|
||||
match_method_rule?(context)
|
||||
when 'country'
|
||||
match_country_rule?(context)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def to_waf_format
|
||||
{
|
||||
id: id,
|
||||
type: rule_type,
|
||||
target: target,
|
||||
action: action,
|
||||
conditions: conditions || {},
|
||||
priority: priority,
|
||||
expires_at: expires_at,
|
||||
active: active?
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def match_ip_rule?(context)
|
||||
return false unless context[:ip_address]
|
||||
|
||||
target == context[:ip_address]
|
||||
end
|
||||
|
||||
def match_cidr_rule?(context)
|
||||
return false unless context[:ip_address]
|
||||
|
||||
begin
|
||||
range = IPAddr.new(target)
|
||||
range.include?(context[:ip_address])
|
||||
rescue IPAddr::InvalidAddressError
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def match_path_rule?(context)
|
||||
return false unless context[:request_path]
|
||||
|
||||
# Support exact match and regex patterns
|
||||
if conditions&.dig('regex') == true
|
||||
Regexp.new(target).match?(context[:request_path])
|
||||
else
|
||||
context[:request_path].start_with?(target)
|
||||
end
|
||||
end
|
||||
|
||||
def match_user_agent_rule?(context)
|
||||
return false unless context[:user_agent]
|
||||
|
||||
# Support exact match and regex patterns
|
||||
if conditions&.dig('regex') == true
|
||||
Regexp.new(target, Regexp::IGNORECASE).match?(context[:user_agent])
|
||||
else
|
||||
context[:user_agent].downcase.include?(target.downcase)
|
||||
end
|
||||
end
|
||||
|
||||
def match_parameter_rule?(context)
|
||||
return false unless context[:query_params]
|
||||
|
||||
param_name = conditions&.dig('parameter_name') || target
|
||||
param_value = context[:query_params][param_name]
|
||||
|
||||
return false unless param_value
|
||||
|
||||
# Check if parameter value matches pattern
|
||||
if conditions&.dig('regex') == true
|
||||
Regexp.new(target, Regexp::IGNORECASE).match?(param_value.to_s)
|
||||
else
|
||||
param_value.to_s.downcase.include?(target.downcase)
|
||||
end
|
||||
end
|
||||
|
||||
def match_method_rule?(context)
|
||||
return false unless context[:request_method]
|
||||
|
||||
target.upcase == context[:request_method].upcase
|
||||
end
|
||||
|
||||
def match_country_rule?(context)
|
||||
return false unless context[:country_code]
|
||||
|
||||
target.upcase == context[:country_code].upcase
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user