From b1739a588f2c3b059bfc418042c60aa2d399b4c7 Mon Sep 17 00:00:00 2001 From: fossifer Date: Sun, 8 May 2022 09:14:40 +0800 Subject: [PATCH] Add ip_in_ranges function Added support for ip_in_ranges which allow multiple ranges to be checked at the same time. If the IP is in any of the ranges, the function returns true. Bug: T305017 Change-Id: Ic75c87ecd4cacf47ce2ff1b04173405230ff81d0 --- i18n/en.json | 1 + i18n/qqq.json | 1 + includes/KeywordsManager.php | 1 + includes/Parser/FilterEvaluator.php | 30 ++++++++++++++++++++++++ tests/parserTests/ip-in-ranges.t | 21 +++++++++++++++++ tests/phpunit/unit/Parser/ParserTest.php | 3 +++ 6 files changed, 57 insertions(+) create mode 100644 tests/parserTests/ip-in-ranges.t diff --git a/i18n/en.json b/i18n/en.json index 48051b5eb..38f4dc230 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -337,6 +337,7 @@ "abusefilter-edit-builder-funcs-rmwhitespace": "Remove whitespace (rmwhitespace)", "abusefilter-edit-builder-funcs-rmspecials": "Remove special characters (rmspecials)", "abusefilter-edit-builder-funcs-ip_in_range": "Is IP in range? (ip_in_range)", + "abusefilter-edit-builder-funcs-ip_in_ranges": "Is IP in any of the ranges? (ip_in_ranges)", "abusefilter-edit-builder-funcs-contains-any": "Search string for multiple substrings in OR mode. (contains_any)", "abusefilter-edit-builder-funcs-contains-all": "Search string for multiple substrings in AND mode. (contains_all)", "abusefilter-edit-builder-funcs-equals-to-any": "Check if a given argument is equal (===) to any of the following arguments (equals_to_any)", diff --git a/i18n/qqq.json b/i18n/qqq.json index 86eead6b3..e9e64d9e1 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -379,6 +379,7 @@ "abusefilter-edit-builder-funcs-rmwhitespace": "{{doc-important|Do not translate \"rmwhitespace\"}} Abuse filter syntax option in a dropdown from the group {{msg-mw|abusefilter-edit-builder-group-funcs}}.", "abusefilter-edit-builder-funcs-rmspecials": "{{doc-important|Do not translate \"'''rmspecials'''\".}} Abuse filter syntax option in a dropdown from the group {{msg-mw|abusefilter-edit-builder-group-funcs}}.", "abusefilter-edit-builder-funcs-ip_in_range": "{{doc-important|Do not translate \"'''ip_in_range'''\".}} Abuse filter syntax option in a dropdown from the group {{msg-mw|abusefilter-edit-builder-group-funcs}}.", + "abusefilter-edit-builder-funcs-ip_in_ranges": "{{doc-important|Do not translate \"'''ip_in_ranges'''\".}} Abuse filter syntax option in a dropdown from the group {{msg-mw|abusefilter-edit-builder-group-funcs}}.", "abusefilter-edit-builder-funcs-contains-any": "{{doc-important|Do not translate \"'''contains_any'''\".}} Abuse filter syntax option in a dropdown from the group {{msg-mw|abusefilter-edit-builder-group-funcs}}.", "abusefilter-edit-builder-funcs-contains-all": "{{doc-important|Do not translate \"'''contains_all'''\".}} Abuse filter syntax option in a dropdown from the group {{msg-mw|abusefilter-edit-builder-group-funcs}}.", "abusefilter-edit-builder-funcs-equals-to-any": "{{doc-important|Do not translate === and equals_to_any (but feel free to change brackets to reflect bracket rules of your language).}} Abuse filter syntax option in a dropdown from the group {{msg-mw|abusefilter-edit-builder-group-funcs}}.", diff --git a/includes/KeywordsManager.php b/includes/KeywordsManager.php index 37518ccd7..9ef44486e 100644 --- a/includes/KeywordsManager.php +++ b/includes/KeywordsManager.php @@ -62,6 +62,7 @@ class KeywordsManager { 'rmwhitespace(text)' => 'rmwhitespace', 'rmspecials(text)' => 'rmspecials', 'ip_in_range(ip, range)' => 'ip_in_range', + 'ip_in_ranges(ip, range1, range2, ...)' => 'ip_in_ranges', 'contains_any(haystack,needle1,needle2,...)' => 'contains-any', 'contains_all(haystack,needle1,needle2,...)' => 'contains-all', 'equals_to_any(haystack,needle1,needle2,...)' => 'equals-to-any', diff --git a/includes/Parser/FilterEvaluator.php b/includes/Parser/FilterEvaluator.php index a650c2d6e..b73fea4ca 100644 --- a/includes/Parser/FilterEvaluator.php +++ b/includes/Parser/FilterEvaluator.php @@ -52,6 +52,7 @@ class FilterEvaluator { 'rcount' => 'funcRCount', 'get_matches' => 'funcGetMatches', 'ip_in_range' => 'funcIPInRange', + 'ip_in_ranges' => 'funcIPInRanges', 'contains_any' => 'funcContainsAny', 'contains_all' => 'funcContainsAll', 'equals_to_any' => 'funcEqualsToAny', @@ -89,6 +90,7 @@ class FilterEvaluator { 'rcount' => [ 1, 2 ], 'get_matches' => [ 2, 2 ], 'ip_in_range' => [ 2, 2 ], + 'ip_in_ranges' => [ 3, INF ], 'contains_any' => [ 2, INF ], 'contains_all' => [ 2, INF ], 'equals_to_any' => [ 2, INF ], @@ -1011,6 +1013,34 @@ class FilterEvaluator { return new AFPData( AFPData::DBOOL, $result ); } + /** + * @param array $args + * @param int $position + * @return AFPData + * @throws UserVisibleException + */ + protected function funcIPInRanges( $args, int $position ) { + $ip = array_shift( $args )->toString(); + + foreach ( $args as $range ) { + $range = $range->toString(); + + if ( !IPUtils::isValidRange( $range ) && !IPUtils::isIPAddress( $range ) ) { + throw new UserVisibleException( + 'invalidiprange', + $position, + [ $range ] + ); + } + + if ( IPUtils::isInRange( $ip, $range ) ) { + return new AFPData( AFPData::DBOOL, true ); + } + } + + return new AFPData( AFPData::DBOOL, false ); + } + /** * @param array $args * @return AFPData diff --git a/tests/parserTests/ip-in-ranges.t b/tests/parserTests/ip-in-ranges.t new file mode 100644 index 000000000..dce3d3de0 --- /dev/null +++ b/tests/parserTests/ip-in-ranges.t @@ -0,0 +1,21 @@ +ip_in_ranges( '12.34.56.78', '12.34.56.0/24', '12.34.0.0/16' ) === true & +ip_in_ranges( '12.34.56.78', '65.43.0.0/16', '12.34.56.78/32' ) === true & +ip_in_ranges( '12.34.56.78', '12.0.0.0/8', '13.0.0.0/8' ) === true & +ip_in_ranges( '12.34.56.78', '12.34.56.78', '2001:db8::/16' ) === true & +ip_in_ranges( '12.34.56.78', '12.1.2.255/8', '::' ) === true & +ip_in_ranges( '1.1.1.1', '1.1.1.1/32', '2.2.2.2/32' ) === true & +ip_in_ranges( '1.1.1.1', '1.1.1.1', '1.1.1.1', '1.1.1.1/32' ) === true & +ip_in_ranges( '1.1.1.1', '0.0.0.0/0', '0.0.0.0/1', '0.0.0.0/2', '0.0.0.0/3' ) === true & +ip_in_ranges( '1.1.1.1', '::', '::/0', '::/1' ) === false & +ip_in_ranges( '123.123.123.123', '123.123.123.123', '123.123.123.123' ) === true & +ip_in_ranges( '123.123.123.123', '123.0.0.0-123.122.0.0', '123.124.0.0-124.122.0.0' ) === false & +ip_in_ranges( '123.123.123.123', '125.0.0.0 - 127.0.0.0', '123.0.0.0-124.0.0.0' ) === true & +ip_in_ranges( '123.123.123.123', '123.0.0.0-127.0.0.0', '123.123.0.0 - 124.0.0.0' ) === true & +ip_in_ranges( '123.123.123.123', '127.0.0.0-123.0.0.0', '120.0.0.0-123.0.0.0' ) === false & +ip_in_ranges( '123.123.123.123', '127.0.0.0-123.0.0.0', '123.0.0.0-127.0.0.0' ) === true & +ip_in_ranges( '11.11.11.11', '11.11.11.1', '1.11.11.11', '11.1.11.11', '11.11.1.11' ) === false & +ip_in_ranges( '1.1.1.1', '::-ffff::', '0.0.0.0-255.255.255.255' ) === true & +ip_in_ranges( '2001:db8:85a3::8a2e:0370:7334', '::-ffff::', '0.0.0.0-255.255.255.255' ) === true & +ip_in_ranges( '2001:db8:85a3::8a2e:0370:7334', '2001:db8:85a3::8a2e:370:7334/113', '2001:db8:85a3::8a2e:370:0000-2001:db8:85a3::8a2e:370:8888' ) === true & +ip_in_ranges( '1.1.1.1', '::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', '0.0.0.0' ) === false & +ip_in_ranges( '2001:db8:85a3::8a2e:370:0000', '0.0.0.0-255.255.255.255', '::' ) === false diff --git a/tests/phpunit/unit/Parser/ParserTest.php b/tests/phpunit/unit/Parser/ParserTest.php index 7475463af..1afdbc295 100644 --- a/tests/phpunit/unit/Parser/ParserTest.php +++ b/tests/phpunit/unit/Parser/ParserTest.php @@ -575,6 +575,7 @@ class ParserTest extends ParserTestCase { public function invalidIPRange() { return [ [ "ip_in_range('0.0.0.0', 'lol')", 'funcIPInRange' ], + [ "ip_in_ranges('0.0.0.0', ':', '0.0.0.256')", 'funcIPInRanges' ], ]; } @@ -679,6 +680,7 @@ class ParserTest extends ParserTestCase { public function threeParamsFuncs() { return [ [ 'str_replace' ], + [ 'ip_in_ranges' ], ]; } @@ -1132,6 +1134,7 @@ class ParserTest extends ParserTestCase { [ 'added_lines contains string(3/0)', 'dividebyzero' ], [ 'norm(new_text) irlike ")"', 'regexfailure' ], [ 'ip_in_range( user_name, "foobar" )', 'invalidiprange' ], + [ 'ip_in_ranges( user_name, "foo", "bar" )', 'invalidiprange' ], ]; }