Skip to content

Commit

Permalink
#8 Target filter implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
stklcode committed Jun 4, 2017
1 parent 0cf4548 commit 152a800
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 63 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ This plugin adds customizable blacklist to Statify to allow blocking of referer
#### Referer Blacklist ####
Add a list of domains (for simplicity only second-level, e.g. _example.com_ which blocks _everything.example.com_).

#### Target Blacklist ####
Add a list of target pages (e.g. _/test/page/_, _/?page_id=123_) that will be excluded from tracking.

#### IP Blacklist ####
Add a list of IP addresses or subnets (e.g. _192.0.2.123_, _198.51.100.0/24_, _2001:db8:a0b:12f0::/64_).

Expand Down Expand Up @@ -74,6 +77,7 @@ Because of this, an IP blacklist can only be applied while processing the reques

### 1.4.0 / work in progress ###
* IP blacklist implemented (#7)
* Target page blacklist implemented (#8)

### 1.3.1 / 09.12.2016 ###
* Continue filtering if no filter applies (#6)
Expand Down
Binary file modified assets/screenshot-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 34 additions & 3 deletions inc/statifyblacklist.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public function __construct() {

/* CronJob to clean up database */
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
if ( self::$_options['cron_referer'] == 1 ) {
if ( self::$_options['cron_referer'] == 1 || self::$_options['cron_target'] == 1 ) {
add_action( 'statify_cleanup', array( 'StatifyBlacklist_Admin', 'cleanup_database' ) );
}
}
Expand Down Expand Up @@ -119,6 +119,10 @@ protected static function defaultOptions() {
'cron_referer' => 0,
'referer' => array(),
'referer_regexp' => 0,
'active_target' => 0,
'cron_target' => 0,
'target' => array(),
'target_regexp' => 0,
'active_ip' => 0,
'ip' => array(),
'version' => self::VERSION_MAIN
Expand All @@ -130,8 +134,8 @@ protected static function defaultOptions() {
*
* @return TRUE if referer matches blacklist.
*
* @since 1.0.0
* @changed 1.4.0
* @since 1.0.0
* @since 1.4.0 Target and IP blacklist
*/
public static function apply_blacklist_filter() {
/* Referer blacklist */
Expand Down Expand Up @@ -163,6 +167,33 @@ public static function apply_blacklist_filter() {
}
}

/* Target blacklist (since 1.4.0) */
if ( isset( self::$_options['active_target'] ) && self::$_options['active_target'] != 0 ) {
/* Regular Expression filtering since 1.3.0 */
if ( isset( self::$_options['target_regexp'] ) && self::$_options['target_regexp'] > 0 ) {
/* Get full referer string */
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '/' );
/* Merge given regular expressions into one */
$regexp = '/' . implode( "|", array_keys( self::$_options['target'] ) ) . '/';
if ( self::$_options['target_regexp'] == 2 ) {
$regexp .= 'i';
}

/* Check blacklist (return NULL to continue filtering) */

return ( preg_match( $regexp, $target ) === 1 ) ? true : null;
} else {
/* Extract target page */
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '/' );
/* Get blacklist */
$blacklist = self::$_options['target'];
/* Check blacklist */
if ( isset( $blacklist[ $target ] ) ) {
return true;
}
}
}

/* IP blacklist (since 1.4.0) */
if ( isset ( self::$_options['active_ip'] ) && self::$_options['active_ip'] != 0 ) {
if ( ( $ip = self::getIP() ) !== false ) {
Expand Down
69 changes: 50 additions & 19 deletions inc/statifyblacklist_admin.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,26 +131,57 @@ public static function cleanup_database() {
die( __( 'Are you sure you want to do this?' ) );
}

global $wpdb;

if ( isset( self::$_options['referer_regexp'] ) && self::$_options['referer_regexp'] > 0 ) {
/* Merge given regular expressions into one */
$refererRegexp = implode( "|", array_keys( self::$_options['referer'] ) );
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
$cleanRef = ( self::$_options['cron_referer'] == 1 );
$cleanTrg = ( self::$_options['cron_target'] == 1 );
} else {
/* Sanitize URLs */
$referer = self::sanitizeURLs( self::$_options['referer'] );
$cleanRef = true;
$cleanTrg = true;
}


if ( $cleanRef ) {
if ( isset( self::$_options['referer_regexp'] ) && self::$_options['referer_regexp'] > 0 ) {
/* Merge given regular expressions into one */
$refererRegexp = implode( "|", array_keys( self::$_options['referer'] ) );
} else {
/* Sanitize URLs */
$referer = self::sanitizeURLs( self::$_options['referer'] );

/* Build filter regexp */
$refererRegexp = str_replace( '.', '\.', implode( '|', array_flip( $referer ) ) );
/* Build filter regexp */
$refererRegexp = str_replace( '.', '\.', implode( '|', array_flip( $referer ) ) );
}
}

if ( ! empty( $refererRegexp ) ) {
if ( $cleanTrg ) {
if ( isset( self::$_options['target_regexp'] ) && self::$_options['target_regexp'] > 0 ) {
/* Merge given regular expressions into one */
$targetRegexp = implode( "|", array_keys( self::$_options['target'] ) );
} else {
/* Build filter regexp */
$targetRegexp = str_replace( '.', '\.', implode( '|', array_flip( self::$_options['target'] ) ) );
}
}


if ( ! empty( $refererRegexp ) || ! empty( $targetRegexp ) ) {
global $wpdb;

/* Execute filter on database */
$wpdb->query(
$wpdb->prepare( "DELETE FROM `$wpdb->statify` WHERE "
. ( ( self::$_options['referer_regexp'] == 1 ) ? " BINARY " : "" )
. "referrer REGEXP %s", $refererRegexp )
);
if ( ! empty( $refererRegexp ) ) {
$wpdb->query(
$wpdb->prepare( "DELETE FROM `$wpdb->statify` WHERE "
. ( ( self::$_options['referer_regexp'] == 1 ) ? " BINARY " : "" )
. "referrer REGEXP %s", $refererRegexp )
);
}
if ( ! empty( $targetRegexp ) ) {
$wpdb->query(
$wpdb->prepare( "DELETE FROM `$wpdb->statify` WHERE "
. ( ( self::$_options['target_regexp'] == 1 ) ? " BINARY " : "" )
. "target REGEXP %s", $targetRegexp )
);
}

/* Optimize DB */
$wpdb->query( "OPTIMIZE TABLE `$wpdb->statify`" );
Expand Down Expand Up @@ -195,10 +226,10 @@ function ( $r ) {
*/
private static function sanitizeIPs( $ips ) {
return array_filter( $ips, function ( $ip ) {
return preg_match('/^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])'.
'(\/([0-9]|[1-2][0-9]|3[0-2]))?$/', $ip) ||
preg_match('/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))'.
'(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/', $ip);
return preg_match( '/^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])' .
'(\/([0-9]|[1-2][0-9]|3[0-2]))?$/', $ip ) ||
preg_match( '/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))' .
'(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/', $ip );
} );
}
}
114 changes: 113 additions & 1 deletion test/StatifyBlacklistTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public function testUpgrade() {
$optionsUpdated = get_option( 'statify-blacklist' );

/* Verify size against default options (no junk left) */
$this->assertEquals( 7, sizeof( $optionsUpdated ) );
$this->assertEquals( 11, sizeof( $optionsUpdated ) );

/* Verify that original attributes are unchanged */
$this->assertEquals( $options13['active_referer'], $optionsUpdated['active_referer'] );
Expand Down Expand Up @@ -294,6 +294,114 @@ public function testIPFilter() {
$_SERVER['HTTP_X_REAL_IP'] = '2001:db8:a0b:12f0:0::1';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}

/**
* Test simple target filter.
*/
public function testTargetFilter() {
/* Prepare Options: 2 blacklisted domains, disabled */
StatifyBlacklist::$_options = array(
'active_referer' => 0,
'cron_referer' => 0,
'referer' => array(
'example.com' => 0,
'example.net' => 1
),
'referer_regexp' => 0,
'active_target' => 0,
'cron_target' => 0,
'target' => array(
'/excluded/page/' => 0,
'/?page_id=3' => 1
),
'target_regexp' => 0,
'active_ip' => 0,
'ip' => array(),
'version' => StatifyBlacklist::VERSION_MAIN
);

/* No multisite */
StatifyBlacklist::$multisite = false;

/* Empty target */
unset( $_SERVER['REQUEST_URI'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
/* Non-blacklisted targets */
$_SERVER['REQUEST_URI'] = '';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/?page_id=1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
/* Blacklisted referer */
$_SERVER['REQUEST_URI'] = '/excluded/page/';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/?page_id=3';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );

/* Activate filter and run tests again */
StatifyBlacklist::$_options['active_target'] = 1;

unset( $_SERVER['REQUEST_URI'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );

$_SERVER['REQUEST_URI'] = '';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/?page_id=1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );

$_SERVER['REQUEST_URI'] = '/excluded/page/';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/?page_id=3';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/?page_id=3';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}

/**
* Test target filter using regular expressions.
*/
public function testTargetRegexFilter() {
/* Prepare Options: 2 regular expressions */
StatifyBlacklist::$_options = array(
'active_referer' => 1,
'cron_referer' => 0,
'referer' => array(
'example.[a-z]+' => 0,
'test' => 1
),
'referer_regexp' => 1,
'version' => 1.3
);

/* No multisite */
StatifyBlacklist::$multisite = false;

/* No referer */
unset( $_SERVER['HTTP_REFERER'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
/* Non-blacklisted referer */
$_SERVER['HTTP_REFERER'] = 'http://not.evil';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
/* Blacklisted referer */
$_SERVER['HTTP_REFERER'] = 'http://example.com';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
/* Blacklisted referer with path */
$_SERVER['HTTP_REFERER'] = 'http://foobar.net/test/me';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
/* Matching both */
$_SERVER['HTTP_REFERER'] = 'http://example.net/test/me';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
/* Mathinc with wrong case */
$_SERVER['HTTP_REFERER'] = 'http://eXaMpLe.NeT/tEsT/mE';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );

/* Set RegExp filter to case insensitive */
StatifyBlacklist::$_options['referer_regexp'] = 2;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}
}

/**
Expand Down Expand Up @@ -346,3 +454,7 @@ function update_option( $option, $value, $autoload = null ) {
global $mock_options;
$mock_options[ $option ] = $value;
}

function wp_unslash ( $value ) {
return is_string( $value ) ? stripslashes( $value ) : $value;
}
Loading

0 comments on commit 152a800

Please sign in to comment.