86 lines
2.9 KiB
Ruby
86 lines
2.9 KiB
Ruby
# 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
|