Path matching
This commit is contained in:
85
app/services/path_rule_matcher.rb
Normal file
85
app/services/path_rule_matcher.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# PathRuleMatcher - Service to match Events against path_pattern Rules
|
||||
#
|
||||
# This service provides path pattern matching logic for evaluating whether
|
||||
# an event matches a path_pattern rule. Used for hub-side testing and validation
|
||||
# before agent deployment.
|
||||
#
|
||||
# Match Types:
|
||||
# - exact: All segments must match exactly
|
||||
# - prefix: Event path must start with rule segments
|
||||
# - suffix: Event path must end with rule segments
|
||||
# - contains: Rule segments must appear consecutively somewhere in event path
|
||||
class PathRuleMatcher
|
||||
def self.matches?(rule, event)
|
||||
return false unless rule.path_pattern_rule?
|
||||
return false if event.request_segment_ids.blank?
|
||||
|
||||
rule_segments = rule.path_segment_ids
|
||||
event_segments = event.request_segment_ids
|
||||
|
||||
return false if rule_segments.blank?
|
||||
|
||||
case rule.path_match_type
|
||||
when 'exact'
|
||||
exact_match?(event_segments, rule_segments)
|
||||
when 'prefix'
|
||||
prefix_match?(event_segments, rule_segments)
|
||||
when 'suffix'
|
||||
suffix_match?(event_segments, rule_segments)
|
||||
when 'contains'
|
||||
contains_match?(event_segments, rule_segments)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Find all path_pattern rules that match the given event
|
||||
def self.matching_rules(event)
|
||||
return [] if event.request_segment_ids.blank?
|
||||
|
||||
Rule.path_pattern_rules.active.select do |rule|
|
||||
matches?(rule, event)
|
||||
end
|
||||
end
|
||||
|
||||
# Evaluate an event against path rules and return the first matching action
|
||||
def self.evaluate(event)
|
||||
matching_rule = matching_rules(event).first
|
||||
matching_rule&.waf_action || 'allow'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Exact match: all segments must match exactly
|
||||
# Example: [1, 2, 3] matches [1, 2, 3] only
|
||||
def self.exact_match?(event_segments, rule_segments)
|
||||
event_segments == rule_segments
|
||||
end
|
||||
|
||||
# Prefix match: event path must start with rule segments
|
||||
# Example: rule [1, 2] matches events [1, 2], [1, 2, 3], [1, 2, 3, 4]
|
||||
def self.prefix_match?(event_segments, rule_segments)
|
||||
return false if event_segments.length < rule_segments.length
|
||||
event_segments[0...rule_segments.length] == rule_segments
|
||||
end
|
||||
|
||||
# Suffix match: event path must end with rule segments
|
||||
# Example: rule [2, 3] matches events [2, 3], [1, 2, 3], [0, 1, 2, 3]
|
||||
def self.suffix_match?(event_segments, rule_segments)
|
||||
return false if event_segments.length < rule_segments.length
|
||||
event_segments[-rule_segments.length..-1] == rule_segments
|
||||
end
|
||||
|
||||
# Contains match: rule segments must appear consecutively somewhere in event path
|
||||
# Example: rule [2, 3] matches [1, 2, 3, 4], [2, 3], [0, 2, 3, 5]
|
||||
def self.contains_match?(event_segments, rule_segments)
|
||||
return false if event_segments.length < rule_segments.length
|
||||
|
||||
# Check if rule_segments appear consecutively anywhere in event_segments
|
||||
(0..event_segments.length - rule_segments.length).any? do |i|
|
||||
event_segments[i, rule_segments.length] == rule_segments
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user