Module: Sqreen::Rules::Matcher

Included in:
BindingAccessorMatcherCB::MatcherElem, MatcherRuleCB
Defined in:
lib/sqreen/rules/matcher_rule.rb

Overview

matcher behavior

Constant Summary collapse

ANYWHERE_OPT =
'anywhere'.freeze
MATCH_PREDICATE =
Regexp.new('').respond_to?(:match?)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#min_sizeObject (readonly)

Returns the value of attribute min_size.



12
13
14
# File 'lib/sqreen/rules/matcher_rule.rb', line 12

def min_size
  @min_size
end

Class Method Details

.prepare_re_pattern(value, options, case_sensitive) ⇒ Object



13
14
15
16
17
18
19
20
# File 'lib/sqreen/rules/matcher_rule.rb', line 13

def self.prepare_re_pattern(value, options, case_sensitive)
  res = 0
  res |= Regexp::MULTILINE  if options.include?('multiline')
  res |= Regexp::IGNORECASE unless case_sensitive
  r = Regexp.compile(value, res)
  r =~ ''
  r
end

Instance Method Details

#match(str) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/sqreen/rules/matcher_rule.rb', line 83

def match(str)
  return if str.nil? || str.empty? || !str.is_a?(String)
  str = enforce_encoding(str) unless str.ascii_only?
  istr = str.downcase unless @string.empty?

  @string.each do |fun, cases|
    cases.each do |case_type, patterns|
      input_str = case_type == :ci ? istr : str
      patterns.each do |pat|
        return pat if fun.call(pat, input_str)
      end
    end
  end

  if defined?(Encoding)
    if MATCH_PREDICATE
      @regexp_patterns.each do |p|
        next unless Encoding.compatible?(p, str)
        return p if p.match?(str)
      end
    else
      @regexp_patterns.each do |p|
        next unless Encoding.compatible?(p, str)
        return p if p.match(str)
      end
    end
  else
    @regexp_patterns.each do |p|
      return p if p.match(str)
    end
  end
  nil
end

#prepare(patterns) ⇒ Object

Raises:



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/sqreen/rules/matcher_rule.rb', line 24

def prepare(patterns)
  @string = {}
  @regexp_patterns = []

  if patterns.nil?
    msg = "no key 'values' in data (had #{@data.keys})"
    raise Sqreen::Exception, msg
  end

  @funs = {
    ANYWHERE_OPT => lambda { |value, str| str.include?(value) },
    'starts_with'.freeze => lambda { |value, str| str.start_with?(value) },
    'ends_with'.freeze => lambda { |value, str| str.end_with?(value)   },
    'equals'.freeze    => lambda { |value, str| str == value           },
  }

  sizes = []
  patterns.each do |entry|
    next unless entry
    type = entry['type']
    val = entry['value']
    opts = entry['options']
    opt = ANYWHERE_OPT
    opt = opts.first.freeze if opts && opts.first && opts.first != ''
    case_sensitive = entry['case_sensitive'] || false
    case type
    when 'string'
      if case_sensitive
        case_type = :cs
      else
        case_type = :ci
        val.downcase!
      end

      way = @funs[opt]
      unless way
        Sqreen.log.debug { "Error: unknown string option '#{opt}' " }
        next
      end
      @string[way] = { :ci => [], :cs => [] } unless @string.key?(way)
      @string[way][case_type] << val
      sizes << entry.fetch('min_length') { val.size }
    when 'regexp'
      pattern = Matcher.prepare_re_pattern(val, opt, case_sensitive)
      next unless pattern
      @regexp_patterns << pattern
      sizes << entry['min_length']
    else
      raise Sqreen::Exception, "No such matcher type #{type}"
    end
  end

  @min_size = sizes.min unless sizes.any?(&:nil?)

  return unless [@regexp_patterns, @string].map(&:empty?).all?
  msg = "no key 'regexp' nor 'match' in data (had #{@data.keys})"
  raise Sqreen::Exception, msg
end