Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.33% covered (warning)
83.33%
60 / 72
82.35% covered (warning)
82.35%
14 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
Waf_Operators
83.33% covered (warning)
83.33%
60 / 72
82.35% covered (warning)
82.35%
14 / 17
60.12
0.00% covered (danger)
0.00%
0 / 1
 begins_with
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 contains
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 contains_word
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 ends_with
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 eq
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 ge
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 gt
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 le
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 lt
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 no_match
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 pm
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 get_multi_string_matcher
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 rx
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 streq
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 unconditional_match
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validate_byte_range
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
11.03
 within
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2/**
3 * Rule compiler for Jetpack Waf.
4 *
5 * @package automattic/jetpack-waf
6 */
7
8namespace Automattic\Jetpack\Waf;
9
10/**
11 * Waf_Operators class
12 */
13class Waf_Operators {
14    /**
15     * Returns true if the test string is found at the beginning of the input.
16     *
17     * @param string $input Input.
18     * @param string $test Test.
19     * @return string|false
20     */
21    public function begins_with( $input, $test ) {
22        if ( '' === $input && '' === $test ) {
23            return '';
24        }
25
26        return substr( $input, 0, strlen( $test ) ) === $test
27        ? $test
28        : false;
29    }
30
31    /**
32     * Returns true if the test string is found anywhere in the input.
33     *
34     * @param string $input Input.
35     * @param string $test Test.
36     * @return string|false
37     */
38    public function contains( $input, $test ) {
39        if ( empty( $input ) || empty( $test ) ) {
40            return false;
41        }
42
43        return strpos( $input, $test ) !== false
44        ? $test
45        : false;
46    }
47
48    /**
49     * Returns true if the test string with word boundaries is found anywhere in the input.
50     *
51     * @param string $input Input.
52     * @param string $test Test.
53     * @return string|false
54     */
55    public function contains_word( $input, $test ) {
56        return ( $input === $test || 1 === preg_match( '/\b' . preg_quote( $test, '/' ) . '\b/Ds', $input ) )
57        ? $test
58        : false;
59    }
60
61    /**
62     * Returns true if the test string is found at the end of the input.
63     *
64     * @param string $input Input.
65     * @param string $test Test.
66     * @return string|false
67     */
68    public function ends_with( $input, $test ) {
69        return ( '' === $test || substr( $input, -1 * strlen( $test ) ) === $test )
70        ? $test
71        : false;
72    }
73
74    /**
75     * Returns true if the input value is equal to the test value.
76     * If either value cannot be converted to an int it will be treated as 0.
77     *
78     * @param mixed $input Input.
79     * @param mixed $test Test.
80     * @return int|false
81     */
82    public function eq( $input, $test ) {
83        return intval( $input ) === intval( $test )
84        ? $input
85        : false;
86    }
87
88    /**
89     * Returns true if the input value is greater than or equal to the test value.
90     * If either value cannot be converted to an int it will be treated as 0.
91     *
92     * @param mixed $input Input.
93     * @param mixed $test Test.
94     * @return int|false
95     */
96    public function ge( $input, $test ) {
97        return intval( $input ) >= intval( $test )
98        ? $input
99        : false;
100    }
101
102    /**
103     * Returns true if the input value is greater than the test value.
104     * If either value cannot be converted to an int it will be treated as 0.
105     *
106     * @param mixed $input Input.
107     * @param mixed $test Test.
108     * @return int|false
109     */
110    public function gt( $input, $test ) {
111        return intval( $input ) > intval( $test )
112        ? $input
113        : false;
114    }
115
116    /**
117     * Returns true if the input value is less than or equal to the test value.
118     * If either value cannot be converted to an int it will be treated as 0.
119     *
120     * @param mixed $input Input.
121     * @param mixed $test Test.
122     * @return int|false
123     */
124    public function le( $input, $test ) {
125        return intval( $input ) <= intval( $test )
126        ? $input
127        : false;
128    }
129
130    /**
131     * Returns true if the input value is less than the test value.
132     * If either value cannot be converted to an int it will be treated as 0.
133     *
134     * @param mixed $input Input.
135     * @param mixed $test Test.
136     * @return int|false
137     */
138    public function lt( $input, $test ) {
139        return intval( $input ) < intval( $test )
140        ? $input
141        : false;
142    }
143
144    /**
145     * Returns false.
146     *
147     * @return false
148     */
149    public function no_match() {
150        return false;
151    }
152
153    /**
154     * Uses a multi-string matching algorithm to search through $input for a number of given $words.
155     *
156     * @param string   $input Input.
157     * @param string[] $words \AhoCorasick\MultiStringMatcher $matcher.
158     * @return string[]|false Returns the words that were found in $input, or FALSE if no words were found.
159     */
160    public function pm( $input, $words ) {
161        $results = $this->get_multi_string_matcher( $words )->searchIn( $input );
162
163        return isset( $results[0] )
164        ? array_map(
165            function ( $r ) {
166                return $r[1]; },
167            $results
168        )
169        : false;
170    }
171
172    /**
173     * The last-used pattern-matching algorithm.
174     *
175     * @var array
176     */
177    private $last_multi_string_matcher = array( null, null );
178
179    /**
180     * Creates a matcher that uses the Aho-Corasick algorithm to efficiently find a number of words in an input string.
181     * Caches the last-used matcher so that the same word list doesn't have to be compiled multiple times.
182     *
183     * @param string[] $words Words.
184     * @return \AhoCorasick\MultiStringMatcher
185     */
186    private function get_multi_string_matcher( $words ) {
187        // only create a new matcher entity if we don't have one already for this word list.
188        if ( $this->last_multi_string_matcher[0] !== $words ) {
189            $this->last_multi_string_matcher = array( $words, new \AhoCorasick\MultiStringMatcher( $words ) );
190        }
191
192        return $this->last_multi_string_matcher[1];
193    }
194
195    /**
196     * Performs a regular expression match on the input subject using the given pattern.
197     * Returns false if the pattern does not match, or the substring(s) of the input
198     * that were matched by the pattern.
199     *
200     * @param string $subject Subject.
201     * @param string $pattern Pattern.
202     * @return string[]|false
203     */
204    public function rx( $subject, $pattern ) {
205        $matched = preg_match( $pattern, $subject, $matches );
206        return 1 === $matched
207            ? $matches
208            : false;
209    }
210
211    /**
212     * Returns true if the given input string matches the test string.
213     *
214     * @param string $input Input.
215     * @param string $test Test.
216     * @return string|false
217     */
218    public function streq( $input, $test ) {
219        return $input === $test
220        ? $test
221        : false;
222    }
223
224    /**
225     * Returns true.
226     *
227     * @param string $input Input.
228     * @return bool
229     */
230    public function unconditional_match( $input ) {
231        return $input;
232    }
233
234    /**
235     * Checks to see if the input string only contains characters within the given byte range
236     *
237     * @param string $input Input.
238     * @param array  $valid_range Valid range.
239     * @return string
240     */
241    public function validate_byte_range( $input, $valid_range ) {
242        if ( '' === $input ) {
243            // an empty string is considered "valid".
244            return false;
245        }
246        $i = 0;
247        while ( isset( $input[ $i ] ) ) {
248            $n = ord( $input[ $i ] );
249            if ( $n < $valid_range['min'] || $n > $valid_range['max'] ) {
250                return $input[ $i ];
251            }
252            $valid = false;
253            foreach ( $valid_range['range'] as $b ) {
254                if ( $n === $b || is_array( $b ) && $n >= $b[0] && $n <= $b[1] ) {
255                    $valid = true;
256                    break;
257                }
258            }
259            if ( ! $valid ) {
260                return $input[ $i ];
261            }
262            ++$i;
263        }
264
265        // if there weren't any invalid bytes, return false.
266        return false;
267    }
268
269    /**
270     * Returns true if the input value is found anywhere inside the test value
271     * (i.e. the inverse of @contains)
272     *
273     * @param mixed $input Input.
274     * @param mixed $test Test.
275     * @return string|false
276     */
277    public function within( $input, $test ) {
278        if ( '' === $input || '' === $test ) {
279            return false;
280        }
281
282        return strpos( $test, $input ) !== false
283        ? $input
284        : false;
285    }
286}