diff --git a/README.md b/README.md index 6db8b92..70345d2 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Same for IPv6 prefixes like _2001:db8:a0b:12f0::/64_. ### 1.6.0 / unreleased ### * Minor accessibility fixes on settings page +* Introduced new user agent filter (#20) ### 1.5.2 / 03.09.2020 ### * Minor translation updates diff --git a/inc/class-statifyblacklist.php b/inc/class-statifyblacklist.php index 97b9c89..9b01bb7 100644 --- a/inc/class-statifyblacklist.php +++ b/inc/class-statifyblacklist.php @@ -91,7 +91,10 @@ class StatifyBlacklist { self::update_options(); // Add Filter to statify hook if enabled. - if ( 0 !== self::$options['referer']['active'] || 0 !== self::$options['target']['active'] || 0 !== self::$options['ip']['active'] ) { + if ( 0 !== self::$options['referer']['active'] || + 0 !== self::$options['target']['active'] || + 0 !== self::$options['ip']['active'] || + 0 !== self::$options['ua']['active'] ) { add_filter( 'statify__skip_tracking', array( 'StatifyBlacklist', 'apply_blacklist_filter' ) ); } @@ -156,6 +159,11 @@ class StatifyBlacklist { 'active' => 0, 'blacklist' => array(), ), + 'ua' => array( + 'active' => 0, + 'regexp' => 0, + 'blacklist' => array(), + ), 'version' => self::VERSION_MAIN, ); } @@ -264,6 +272,59 @@ class StatifyBlacklist { } } + // User agent filter (since 1.6). + if ( isset( self::$options['ua']['active'] ) && 0 !== self::$options['ua']['active'] ) { + // Determine filter mode. + $mode = isset( self::$options['ua']['regexp'] ) ? intval( self::$options['ua']['regexp'] ) : 0; + + // Get full user agent string. + if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) { + $user_agent = filter_var( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), FILTER_SANITIZE_STRING ); + + if ( $user_agent ) { + switch ( $mode ) { + + // Regular Expression filtering since 1.3.0. + case self::MODE_REGEX: + case self::MODE_REGEX_CI: + // Merge given regular expressions into one. + $regexp = self::regex( + array_keys( self::$options['ua']['blacklist'] ), + self::MODE_REGEX_CI === self::$options['ua']['regexp'] + ); + + // Check filter (no return to continue filtering #12). + if ( 1 === preg_match( $regexp, $user_agent ) ) { + return true; + } + break; + + // Keyword filter since 1.5.0 (#15). + case self::MODE_KEYWORD: + // Get filter. + $blacklist = self::$options['ua']['blacklist']; + + foreach ( array_keys( $blacklist ) as $keyword ) { + if ( false !== strpos( strtolower( $user_agent ), strtolower( $keyword ) ) ) { + return true; + } + } + break; + + // Standard exact filter. + default: + // Get filter. + $blacklist = self::$options['ua']['blacklist']; + + // Check filter. + if ( isset( $blacklist[ $user_agent ] ) ) { + return true; + } + } + } + } + } + // Skip and continue (return NULL), if all filters are inactive. return null; } diff --git a/test/StatifyBlacklist_Test.php b/test/StatifyBlacklist_Test.php index e896199..2742c4e 100644 --- a/test/StatifyBlacklist_Test.php +++ b/test/StatifyBlacklist_Test.php @@ -68,6 +68,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase { 'active' => 0, 'blacklist' => array(), ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), 'version' => StatifyBlacklist::VERSION_MAIN, ); @@ -130,6 +135,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase { 'active' => 0, 'blacklist' => array(), ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), 'version' => StatifyBlacklist::VERSION_MAIN, ); @@ -187,6 +197,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase { 'active' => 0, 'blacklist' => array(), ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), 'version' => StatifyBlacklist::VERSION_MAIN, ); @@ -426,6 +441,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase { '2001:db8:a0b:12f0::1', ), ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), 'version' => StatifyBlacklist::VERSION_MAIN, ); @@ -498,6 +518,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase { 'active' => 0, 'blacklist' => array(), ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), 'version' => StatifyBlacklist::VERSION_MAIN, ); @@ -544,6 +569,69 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase { // TODO: Test target regex filter. + /** + * Test user agent filter (#20). + * + * @return void + */ + public function test_ua_filter() { + // Prepare Options: 2 filtered IPs, disabled. + StatifyBlacklist::$options = array( + 'referer' => array( + 'active' => 0, + 'cron' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), + 'target' => array( + 'active' => 0, + 'cron' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), + 'ip' => array( + 'active' => 0, + 'blacklist' => array(), + ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array( + 'TestBot/1.23' => 0, + ), + ), + 'version' => StatifyBlacklist::VERSION_MAIN, + ); + + // No multisite. + StatifyBlacklist::$multisite = false; + + // Set matching user agent. + $_SERVER['HTTP_USER_AGENT'] = 'TestBot/1.23'; + $this->assertNull( StatifyBlacklist::apply_blacklist_filter() ); + // Activate filter. + StatifyBlacklist::$options['ua']['active'] = 1; + $this->assertTrue( StatifyBlacklist::apply_blacklist_filter() ); + // Non-matching addresses. + $_SERVER['HTTP_USER_AGENT'] = 'Another Browser 4.5.6 (Linux)'; + $this->assertNull( StatifyBlacklist::apply_blacklist_filter() ); + $_SERVER['HTTP_USER_AGENT'] = 'TestBot/2.34'; + $this->assertNull( StatifyBlacklist::apply_blacklist_filter() ); + // Keyword matching. + StatifyBlacklist::$options['ua']['blacklist'] = array( 'TestBot' => 0 ); + StatifyBlacklist::$options['ua']['regexp'] = StatifyBlacklist::MODE_KEYWORD; + $this->assertTrue( StatifyBlacklist::apply_blacklist_filter() ); + // RegEx. + StatifyBlacklist::$options['ua']['blacklist'] = array( 'T[a-z]+B[a-z]+' => 0 ); + StatifyBlacklist::$options['ua']['regexp'] = StatifyBlacklist::MODE_REGEX; + $this->assertTrue( StatifyBlacklist::apply_blacklist_filter() ); + StatifyBlacklist::$options['ua']['blacklist'] = array( 't[a-z]+' => 0 ); + $this->assertNull( StatifyBlacklist::apply_blacklist_filter() ); + StatifyBlacklist::$options['ua']['regexp'] = StatifyBlacklist::MODE_REGEX_CI; + $this->assertTrue( StatifyBlacklist::apply_blacklist_filter() ); + } + + /** * Test combined filters. * @@ -576,6 +664,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase { '192.0.2.123', ), ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), 'version' => StatifyBlacklist::VERSION_MAIN, ); diff --git a/views/settings-page.php b/views/settings-page.php index 5a61c82..b7debe2 100755 --- a/views/settings-page.php +++ b/views/settings-page.php @@ -88,6 +88,26 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) { ); } + // TODO: Extract user agent array. + if ( isset( $_POST['statifyblacklist']['ua']['blacklist'] ) ) { + $ua_string = sanitize_textarea_field( wp_unslash( $_POST['statifyblacklist']['ua']['blacklist'] ) ); + } + if ( empty( trim( $ua_string ) ) ) { + $ua = array(); + } else { + $ua = array_filter( + array_map( + function ( $a ) { + return trim( $a ); + }, + explode( "\r\n", str_replace( '\\\\', '\\', $ua_string ) ) + ), + function ( $a ) { + return ! empty( $a ); + } + ); + } + // Update options (data will be sanitized). $statifyblacklist_update_result = StatifyBlacklist_Admin::update_options( array( @@ -114,6 +134,13 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) { ? (int) $_POST['statifyblacklist']['ip']['active'] : 0, 'blacklist' => $ip, ), + 'ua' => array( + 'active' => isset( $_POST['statifyblacklist']['ua']['active'] ) + ? (int) $_POST['statifyblacklist']['ua']['active'] : 0, + 'regexp' => isset( $_POST['statifyblacklist']['ua']['regexp'] ) + ? (int) $_POST['statifyblacklist']['ua']['regexp'] : 0, + 'blacklist' => $ua, + ), 'version' => StatifyBlacklist::VERSION_MAIN, ) ); @@ -374,6 +401,77 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) { +

+ + + + + + + + + + + + + + + + +
+ + + > +

+ +
+ +

+
+ + + + +

+ - +
+ - +
+ - +

+
+ : + + + +

+ + MyBot/1.23 +

+
+