Merge branch 'release/1.5.0'

This commit is contained in:
Stefan Kalscheuer 2020-05-13 19:08:14 +02:00
commit f946d3415a
19 changed files with 956 additions and 606 deletions

12
.gitattributes vendored Normal file
View File

@ -0,0 +1,12 @@
/assets export-ignore
/test export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.travis.yml export-ignore
composer.json export-ignore
composer.lock export-ignore
CONTRIBUTING.md export-ignore
package.json export-ignore
phpcs.xml export-ignore
phpunit.xml export-ignore
RoboFile.php export-ignore

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ composer.lock
/dist/
.idea
tests-clover.xml
tests-junit.xml
tests-junit.xml
.phpunit.result.cache

View File

@ -1,12 +1,13 @@
language: php
dist: trusty
php:
- '5.5'
- '5.6'
- '7.0'
- '7.1'
- '7.2'
- '7.3'
- '7.4'
before_script:
- composer install
script:
- composer test-all
notifications:
slack:
secure: "ScXTSMO65veI1jA6TBHGDUtvDqEMkqJykaNf7vLLbb7YIxPIHHNBiX/wcjOHVFfQXZCV3qxQrflB7Lbm9qVUsAv861jTO9x/ZkECl5QhRoc0DIznejwZoypx0HJ9tBZFYT6qNUkViXRKZ/ILAiBLU9Yw52WACtQB9hu3FNFZwmKsjipvV8Sne1qEyTkLYLaMphsbC5mtXYdKMHvdt39jsYsk91UWGeYbXQ37LkMbsaG/8YHXF724d5JO7BRGoThw6p5knKAO5fk29V7GfNqg2h+hnGyNIUOcmxujgMDMFLyFCGMZpPoBa+3jyWWgq4PgpQt0F5VZtJFGoXCGcoMQm5IbVfqkSKJ4jYhqiSIrqSebLmzoPHepWX3yn8tpfOiBWjC6K9w9esp6vcZf26rnAJcjcGkA01rMrHRwR+UEMCLvj7q0DR0qzi/AFeED6gtpODzUf93Rp42Tz1iGvWIbgeCtkCWjfPO6XLuNiqGVPEVaT5BDKqlqbijdKxxp7yh1fdt8s0fInWdIsgoWTbU9DC1W4ZiqtQW7oYO+QtFZMaD6kZWpSqJUwB3kW5JL3odAUEm8bLbRWBvK5ZjGdaGqSbOs6f9gAKcf86iQQhwzCJSOgFlLlKFv9smicjPC+BGOxgx32pgseHNPWn6tmEo/ihmmr/NbbqoOusUKX9gQbA4="

View File

@ -33,17 +33,19 @@ If the changes introduce new functionality or affect major parts of existing cod
For adding new functionality a new test case the corresponding PHPUnit test would be nice (no hard criterion though).
The `master` branch should also be target for most pull requests.
However it it features new functionality you might want to target the `develop` branch instead (see next section for details on branches).
### Branches
The `master` branch represents the current state of development.
Please ensure your initial code is up to date with it at the time you start development.
The `master` should also be target for most pull requests.
In addition, this project features a `develop` branch, which holds bleeding edge developments, not necessarily considered stable or even compatible.
Do not expect this code to run smoothly, but you might have a look into the history to see if some work on an issue has already been started there.
For fixes and features, there might be additional branches, likely prefixed by `ft-` (feature) or `hf-` (hotfix) followed by an issue number (if applicable) and/or a title.
Feel free to adapt these naming scheme to your forks.
For fixes and features, there might be additional branches, likely prefixed by `hotfix/` or `feature/` followed by an issue number (if applicable) and/or a title.
Feel free to adapt this naming scheme to your forks.
### Merge Requirements

View File

@ -1,65 +0,0 @@
var gulp = require('gulp');
var clean = require('gulp-clean');
var copy = require('gulp-copy');
var zip = require('gulp-zip');
var composer = require('gulp-composer');
var phpunit = require('gulp-phpunit');
var exec = require('child_process').exec;
var phpcs = require('gulp-phpcs');
var config = require('./package.json');
// Clean the target directory.
gulp.task('clean', function () {
console.log('Cleaning up target directory ...');
return gulp.src('dist', {read: false})
.pipe(clean());
});
// Prepare composer.
gulp.task('compose', function () {
console.log('Preparing Composer ...');
return composer('install');
});
// Execute unit tests.
gulp.task('test', ['compose'], function () {
console.log('Running PHPUnit tests ...');
return gulp.src('phpunit.xml')
.pipe(phpunit('./vendor/bin/phpunit', {debug: false}));
});
// Execute PHP Code Sniffer.
gulp.task('test-cs', function (cb) {
return exec('./vendor/bin/phpcs --config-set installed_paths vendor/wimg/php-compatibility,vendor/wp-coding-standards/wpcs', function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
if (null === err) {
console.log('Running PHP Code Sniffer tests ...');
// exec('./vendor/bin/phpcs --standard=phpcs.xml', function(err, stdout, stderr) {
// console.log(stdout);
// console.log(stderr);
// });
gulp.src(['statify-blacklist.php', 'inc/**/*.php'])
.pipe(phpcs({bin: './vendor/bin/phpcs', standard: 'phpcs.xml'}))
.pipe(phpcs.reporter('log'));
}
cb(err);
});
});
// Bundle files as required for plugin distribution..
gulp.task('bundle', ['clean'], function () {
console.log('Collecting files for package dist/' + config.name + config.version + ' ...');
return gulp.src(['**/*.php', '!RoboFile.php', '!test/**', '!vendor/**', 'README.md', 'LICENSE.md'], {base: './'})
.pipe(copy('./dist/' + config.name + '.' + config.version + '/' + config.name));
});
// Create a ZIP package of the relevant files for plugin distribution.
gulp.task('package', ['bundle'], function () {
console.log('Building package dist/' + config.name + config.version + '.zip ...');
return gulp.src('./dist/' + config.name + '.' + config.version + '/**')
.pipe(zip(config.name + '.' + config.version + '.zip'))
.pipe(gulp.dest('./dist'));
});
gulp.task('default', ['clean', 'compose', 'test', 'test-cs', 'bundle', 'package']);

View File

@ -357,5 +357,5 @@ into proprietary programs. If your program is a subroutine library,
you may consider it more useful to permit linking proprietary
applications with the library. If this is what you want to do, use the
[GNU Lesser General Public
License](http://www.gnu.org/licenses/lgpl.html) instead of this
License](https://www.gnu.org/licenses/lgpl.html) instead of this
License.

View File

@ -1,16 +1,21 @@
[![Build Status](https://travis-ci.org/stklcode/statify-blacklist.svg?branch=master)](https://travis-ci.org/stklcode/statify-blacklist)
[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=de.stklcode.web.wordpress.plugins%3Astatify-blacklist&metric=alert_status)](https://sonarcloud.io/dashboard?id=de.stklcode.web.wordpress.plugins%3Astatify-blacklist)
[![Packagist Version](https://img.shields.io/packagist/v/stklcode/statify-blacklist.svg)](https://packagist.org/packages/stklcode/statify-blacklist)
[![License](https://img.shields.io/badge/license-GPL%20v2-blue.svg)](https://github.com/stklcode/statify-blacklist/blob/master/LICENSE.md)
# Statify Blacklist #
* Contributors: Stefan Kalscheuer
* Requires at least: 4.4
* Requires at least: 4.7
* Tested up to: 5.4
* Requires PHP: 5.5
* Stable tag: 1.4.4
* Stable tag: 1.5.0
* License: GPLv2 or later
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
## Description ##
A blacklist extension for the famous [Statify](https://wordpress.org/plugins/statify/) Wordpress plugin.
This plugin adds customizable blacklist to Statify to allow blocking of referer spam or internal interactions.
This plugin adds a customizable blacklist to Statify to allow blocking of referer spam or internal interactions.
### Features ##
@ -27,7 +32,7 @@ Add a list of IP addresses or subnets (e.g. _192.0.2.123_, _198.51.100.0/24_, _2
Filters can be applied to data stored in database after modifying filter rules or for one-time clean-up.
#### Compatibility ####
This plugin requires Statify to be installed. The extension has been tested with Statify up to version 1.5.1
This plugin requires Statify to be installed. The extension has been tested with Statify up to version 1.7
The plugin is capable of handling multisite installations.
### Support & Contributions ###
@ -37,51 +42,62 @@ The plugin is capable of handling multisite installations.
### Credits ###
* Author: Stefan Kalscheuer
* Special Thanks to [pluginkollektiv](https://github.com/pluginkollektiv) for maintaining _Statify_
* Special Thanks to [pluginkollektiv](https://pluginkollektiv.org/) for maintaining _Statify_
## Installation ##
* If you dont know how to install a plugin for WordPress, [heres how](http://codex.wordpress.org/Managing_Plugins#Installing_Plugins).
* Make sure _Statify_ plugin is installed and active
* If you dont know how to install a plugin for WordPress, [heres how](https://wordpress.org/support/article/managing-plugins/#installing-plugins).
* Make sure _Statify_ plugin is installed and active
* Goto _Settings_ -> _Statify Blacklist_ to configure the plugin
### Requirements ###
* PHP 5.5 or above
* WordPress 4.4 or above
* Statify plugin installed and activated (1.5.0 or above)
* WordPress 4.7 or above
* _Statify_ plugin installed and activated (1.5 or above)
## Frequently Asked Questions ##
### What is blocked by default? ###
Nothing. By default all blacklists are empty and disabled. They can and have to be filled by the blog administrator.
Nothing. By default, all blacklists are empty and disabled. They can and have to be filled by the blog administrator.
A default blacklist is not provided, as the plugin itself is totally neutral. If you want to filter out referer spam,
visitors from search engines, just "false" referers from 301 redirects or you own IP address used for testing only depends on you.
visitors from search engines, just "false" referrers from 301 redirects or you own IP address used for testing only depends on you.
### Does the filter effect user experience? ###
No. It only prevent's _Statify_ from tracking, nothing more or less.
No. It only prevents _Statify_ from tracking, nothing more or less.
### Does live filtering impact performance? ###
Yes, but probalby not noticeable. Checking a single referer string against a (usually small) list should be negligible compared to the total loading procedure.
Yes, but probably not noticeable. Checking a single referer string against a (usually small) list should be negligible compared to the total loading procedure.
If this still is an issue for you, consider deactivating the filter and only run the one-time-cleanup or activate the cron job.
### Is any personal data collected? ###
No. The privacy policy of _Statify_ is untouched. Data is only processed, not stored or exposed to anyone.
### Are regular expression filters possible? ###
Yes, it is. Just select if you want to filter using regular expressions case sensitive or insensitive.
Note, that regular expression matching is significantly slower than the plain domain filter. Hence it is only recommended for asynchronous cron or manual execution and not for live filtering.
Yes, it is. Just select regular expressions (case-sensitive or insensitive) as matching method instead of exact or keyword match.
### Why is IP filtering only available as live filter? ###
As you might know, Statify does not store any personal information, including IP addresses in the database.
As you might know, _Statify_ does not store any personal information, including IP addresses in the database.
Because of this, an IP blacklist can only be applied while processing the request and not afterwards.
### Can whole IP subnet be blocked? ###
Yes. The plugin features subnet blacklists using CIDR notation.
For example _198.51.100.0/24_ blacklists all sources from _198.51.100.1_ to _198.51.100.254_.
Same for IPv6 prefixes like _2001:db8:a0b:12f0::/64_.
## Screenshots ##
1. Statify Blacklist settings page
## Changelog ##
### 1.5.0 / 13.05.2020 ###
* Minimum required WordPress version is 4.7
* Removed `load_plugin_textdomain()` and `Domain Path` header
* Added automatic compatibility check for WP and PHP version (#17)
* Added keyword filter mode for referer blacklist (#15)
* Layout adjustments on settings page
* Regular expression filters are validated before saving (#13)
### 1.4.4 / 19.05.2018 ###
* Fix live filter chain when regular expressions are active (#12)
@ -115,7 +131,7 @@ Because of this, an IP blacklist can only be applied while processing the reques
### 1.2.0 / 29.08.2016 ###
* Switched from `in_array()` to faster `isset()` for referer checking
* Optional cron execiton implemented
* Optional cron execution implemented
### 1.1.2 / 17.08.2016 ###
* Prepared for localization

View File

@ -8,7 +8,7 @@
* @author Stefan Kalscheuer <stefan@stklcode.de>
*
* @package Statify_Blacklist
* @version 1.4.4
* @version 1.5.0
*/
use Robo\Exception\TaskException;
@ -19,10 +19,10 @@ use Robo\Tasks;
*/
class RoboFile extends Tasks {
const PROJECT_NAME = 'statify-blacklist';
const SVN_URL = 'https://plugins.svn.wordpress.org/statify-blacklist';
const SVN_URL = 'https://plugins.svn.wordpress.org/statify-blacklist';
const OPT_TARGET = 'target';
const OPT_SKIPTEST = 'skipTests';
const OPT_TARGET = 'target';
const OPT_SKIPTEST = 'skipTests';
const OPT_SKIPSTYLE = 'skipStyle';
/**
@ -134,13 +134,21 @@ class RoboFile extends Tasks {
*/
private function bundle() {
$this->say( 'Bundling resources...' );
$this->taskCopyDir( [
'inc' => $this->target_dir . '/' . $this->final_name . '/inc',
'views' => $this->target_dir . '/' . $this->final_name . '/views',
] )->run();
$this->taskCopyDir(
[
'inc' => $this->target_dir . '/' . $this->final_name . '/inc',
'views' => $this->target_dir . '/' . $this->final_name . '/views',
]
)->run();
$this->_copy( 'statify-blacklist.php', $this->target_dir . '/' . $this->final_name . '/statify-blacklist.php' );
$this->_copy( 'README.md', $this->target_dir . '/' . $this->final_name . '/README.md' );
$this->_copy( 'LICENSE.md', $this->target_dir . '/' . $this->final_name . '/LICENSE.md' );
$this->_copy( 'README.md', $this->target_dir . '/' . $this->final_name . '/README.md' );
// Remove content before title (e.g. badges) from README file.
$this->taskReplaceInFile( $this->target_dir . '/' . $this->final_name . '/README.md' )
->regex( '/^[^\\#]*/' )
->to( '' )
->run();
}
/**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -1,6 +1,6 @@
{
"name": "stklcode/statify-blacklist",
"version": "1.4.4",
"version": "1.5.0",
"description": "A blacklist extension for the famous Statify WordPress plugin",
"keywords": [
"wordpress",
@ -19,17 +19,17 @@
"type": "wordpress-plugin",
"require": {
"php": ">=5.5",
"composer/installers": "~1.0"
"composer/installers": "~1.7"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.4",
"consolidation/robo": "^1.0.0",
"dealerdirect/phpcodesniffer-composer-installer": "^0.5",
"consolidation/robo": "^1.4",
"phpunit/phpunit": "*",
"phpunit/php-code-coverage": "*",
"slowprog/composer-copy-file": "~0.2",
"squizlabs/php_codesniffer": "^3.1",
"wimg/php-compatibility": "^8.0",
"wp-coding-standards/wpcs": "~0.14"
"squizlabs/php_codesniffer": "^3.5",
"phpcompatibility/php-compatibility": "^9.3",
"wp-coding-standards/wpcs": "^2.1"
},
"scripts": {
"build": [

View File

@ -9,8 +9,10 @@
* @since 1.0.0
*/
// Quit.
defined( 'ABSPATH' ) || exit;
// Quit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Statify Blacklist admin configuration.
@ -18,37 +20,101 @@ defined( 'ABSPATH' ) || exit;
* @since 1.0.0
*/
class StatifyBlacklist_Admin extends StatifyBlacklist {
/**
* Initialize admin-only components of the plugin.
*
* @since 1.5.0
*
* @return void
*/
public static function init() {
// Add actions.
add_action( 'wpmu_new_blog', array( 'StatifyBlacklist_System', 'install_site' ) );
add_action( 'delete_blog', array( 'StatifyBlacklist_System', 'uninstall_site' ) );
add_filter( 'plugin_row_meta', array( 'StatifyBlacklist_Admin', 'plugin_meta_link' ), 10, 2 );
if ( self::$multisite ) {
add_action( 'network_admin_menu', array( 'StatifyBlacklist_Admin', 'add_menu_page' ) );
add_filter(
'network_admin_plugin_action_links',
array(
'StatifyBlacklist_Admin',
'plugin_actions_links',
),
10,
2
);
} else {
add_action( 'admin_menu', array( 'StatifyBlacklist_Admin', 'add_menu_page' ) );
add_filter( 'plugin_action_links', array( 'StatifyBlacklist_Admin', 'plugin_actions_links' ), 10, 2 );
}
}
/**
* Update options.
*
* @since 1.1.1
*
* @param array $options Optional. New options to save.
*
* @return array|bool array of sanitized array on errors, FALSE if there were none.
* @since 1.1.1
*/
public static function update_options( $options = null ) {
if ( isset( $options ) && current_user_can( 'manage_options' ) ) {
// Sanitize URLs and remove empty inputs.
$given_referer = $options['referer']['blacklist'];
if ( 0 === $options['referer']['regexp'] ) {
$sanitized_referer = self::sanitizeURLs( $given_referer );
// Sanitize referer list.
$given_referer = $options['referer']['blacklist'];
$invalid_referer = array();
if ( self::MODE_NORMAL === $options['referer']['regexp'] ) {
// Sanitize URLs and remove empty inputs.
$sanitized_referer = self::sanitize_urls( $given_referer );
} elseif ( self::MODE_REGEX === $options['referer']['regexp'] || self::MODE_REGEX_CI === $options['referer']['regexp'] ) {
$sanitized_referer = $given_referer;
// Check regular expressions.
$invalid_referer = self::sanitize_regex( $given_referer );
} else {
$sanitized_referer = $given_referer;
}
// Sanitize IPs and Subnets and remove empty inputs.
// Sanitize target list.
$given_target = $options['target']['blacklist'];
$invalid_target = array();
if ( self::MODE_REGEX === $options['target']['regexp'] || self::MODE_REGEX_CI === $options['target']['regexp'] ) {
$sanitized_target = $given_target;
// Check regular expressions.
$invalid_target = self::sanitize_regex( $given_target );
} else {
$sanitized_target = $given_target;
}
// Sanitize IPs and subnets and remove empty inputs.
$given_ip = $options['ip']['blacklist'];
$sanitized_ip = self::sanitizeIPs( $given_ip );
$sanitized_ip = self::sanitize_ips( $given_ip );
// Abort on errors.
if ( ! empty( array_diff( array_keys( $given_referer ), array_keys( $sanitized_referer ) ) ) ) {
return array(
'referer' => $sanitized_referer,
);
} elseif ( ! empty( array_diff( $given_ip, $sanitized_ip ) ) ) {
return array(
'ip' => array_diff( $given_ip, $sanitized_ip ),
);
$errors = array(
'referer' => array(
'sanitized' => $sanitized_referer,
'diff' => array_diff( $given_referer, $sanitized_referer ),
'invalid' => $invalid_referer,
),
'target' => array(
'sanitized' => $sanitized_target,
'diff' => array_diff( $given_target, $sanitized_target ),
'invalid' => $invalid_target,
),
'ip' => array(
'sanitized' => $sanitized_ip,
'diff' => array_diff( $given_ip, $sanitized_ip ),
),
);
if ( ! empty( $errors['referer']['diff'] )
|| ! empty( $errors['referer']['invalid'] )
|| ! empty( $errors['target']['diff'] )
|| ! empty( $errors['target']['invalid'] )
|| ! empty( $errors['ip']['diff'] ) ) {
return $errors;
}
// Update database on success.
@ -74,14 +140,24 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
$title = __( 'Statify Blacklist', 'statify-blacklist' );
if ( self::$multisite ) {
add_submenu_page(
'settings.php', $title, $title, 'manage_network_plugins', 'statify-blacklist-settings', array(
'settings.php',
$title,
$title,
'manage_network_plugins',
'statify-blacklist-settings',
array(
'StatifyBlacklist_Admin',
'settings_page',
)
);
} else {
add_submenu_page(
'options-general.php', $title, $title, 'manage_options', 'statify-blacklist', array(
'options-general.php',
$title,
$title,
'manage_options',
'statify-blacklist',
array(
'StatifyBlacklist_Admin',
'settings_page',
)
@ -154,20 +230,20 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
}
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
$clean_ref = ( 1 === self::$_options['referer']['cron'] );
$clean_trg = ( 1 === self::$_options['target']['cron'] );
$clean_ref = ( 1 === self::$options['referer']['cron'] );
$clean_trg = ( 1 === self::$options['target']['cron'] );
} else {
$clean_ref = true;
$clean_trg = true;
}
if ( $clean_ref ) {
if ( isset( self::$_options['referer']['regexp'] ) && self::$_options['referer']['regexp'] > 0 ) {
if ( isset( self::$options['referer']['regexp'] ) && self::$options['referer']['regexp'] > 0 ) {
// Merge given regular expressions into one.
$referer_regexp = implode( '|', array_keys( self::$_options['referer']['blacklist'] ) );
$referer_regexp = implode( '|', array_keys( self::$options['referer']['blacklist'] ) );
} else {
// Sanitize URLs.
$referer = self::sanitizeURLs( self::$_options['referer']['blacklist'] );
$referer = self::sanitize_urls( self::$options['referer']['blacklist'] );
// Build filter regexp.
$referer_regexp = str_replace( '.', '\.', implode( '|', array_flip( $referer ) ) );
@ -175,12 +251,12 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
}
if ( $clean_trg ) {
if ( isset( self::$_options['target']['regexp'] ) && self::$_options['target']['regexp'] > 0 ) {
if ( isset( self::$options['target']['regexp'] ) && self::$options['target']['regexp'] > 0 ) {
// Merge given regular expressions into one.
$target_regexp = implode( '|', array_keys( self::$_options['target']['blacklist'] ) );
$target_regexp = implode( '|', array_keys( self::$options['target']['blacklist'] ) );
} else {
// Build filter regexp.
$target_regexp = str_replace( '.', '\.', implode( '|', array_flip( self::$_options['target']['blacklist'] ) ) );
$target_regexp = str_replace( '.', '\.', implode( '|', array_flip( self::$options['target']['blacklist'] ) ) );
}
}
@ -188,13 +264,14 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
global $wpdb;
// Execute filter on database.
// @codingStandardsIgnoreStart These statements prouce warnings, rework in future release (TODO).
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- These statements produce warnings, rework in future release (TODO).
if ( ! empty( $referer_regexp ) ) {
$wpdb->query(
$wpdb->prepare(
"DELETE FROM `$wpdb->statify` WHERE "
. ( ( 1 === self::$_options['referer']['regexp'] ) ? ' BINARY ' : '' )
. 'referrer REGEXP %s', $referer_regexp
. ( ( 1 === self::$options['referer']['regexp'] ) ? ' BINARY ' : '' )
. 'referrer REGEXP %s',
$referer_regexp
)
);
}
@ -202,12 +279,13 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
$wpdb->query(
$wpdb->prepare(
"DELETE FROM `$wpdb->statify` WHERE "
. ( ( 1 === self::$_options['target']['regexp'] ) ? ' BINARY ' : '' )
. 'target REGEXP %s', $target_regexp
. ( ( 1 === self::$options['target']['regexp'] ) ? ' BINARY ' : '' )
. 'target REGEXP %s',
$target_regexp
)
);
}
// @codingStandardsIgnoreEnd
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
// Optimize DB.
$wpdb->query( "OPTIMIZE TABLE `$wpdb->statify`" );
@ -227,7 +305,7 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
*
* @return array sanitized array.
*/
private static function sanitizeURLs( $urls ) {
private static function sanitize_urls( $urls ) {
return array_flip(
array_filter(
array_map(
@ -249,15 +327,39 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
*
* @return array sanitized array.
*/
private static function sanitizeIPs( $ips ) {
private static function sanitize_ips( $ips ) {
return array_filter(
$ips, function ( $ip ) {
$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
'/^((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
);
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
);
}
);
}
/**
* Validate regular expressions, i.e. remove duplicates and empty values and validate others.
*
* @since 1.5.0 #13
*
* @param array $expressions Given pre-sanitized array of regular expressions.
*
* @return array Array of invalid expressions.
*/
private static function sanitize_regex( $expressions ) {
return array_filter(
array_flip( $expressions ),
function ( $re ) {
// Check of preg_match() fails (warnings suppressed).
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
return false === @preg_match( StatifyBlacklist::regex( $re, false ), null );
}
);
}

View File

@ -1,6 +1,6 @@
<?php
/**
* Statify Blacklist: StatifyBlacklist_Syste, class
* Statify Blacklist: StatifyBlacklist_System class
*
* This file contains the derived class for the plugin's system operations.
*
@ -9,8 +9,10 @@
* @since 1.0.0
*/
// Quit.
defined( 'ABSPATH' ) || exit;
// Quit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Statify Blacklist system configuration.
@ -25,15 +27,14 @@ class StatifyBlacklist_System extends StatifyBlacklist {
* @since 1.0.0
*
* @param bool $network_wide Whether the plugin was activated network-wide or not.
*
* @return void
*/
public static function install( $network_wide = false ) {
// Create tables for each site in a network.
if ( $network_wide && is_multisite() ) {
if ( function_exists( 'get_sites' ) ) {
$sites = get_sites();
} elseif ( function_exists( 'wp_get_sites' ) ) {
// @codingStandardsIgnoreLine Legacy support for WP < 4.6.
$sites = wp_get_sites();
} else {
return;
}
@ -62,6 +63,8 @@ class StatifyBlacklist_System extends StatifyBlacklist {
* @since 1.4.3
*
* @param integer $site_id Site ID.
*
* @return void
*/
public static function install_site( $site_id ) {
switch_to_blog( (int) $site_id );
@ -77,6 +80,8 @@ class StatifyBlacklist_System extends StatifyBlacklist {
* Plugin uninstall handler.
*
* @since 1.0.0
*
* @return void
*/
public static function uninstall() {
if ( is_multisite() ) {
@ -85,7 +90,7 @@ class StatifyBlacklist_System extends StatifyBlacklist {
if ( function_exists( 'get_sites' ) ) {
$sites = get_sites();
} elseif ( function_exists( 'wp_get_sites' ) ) {
// @codingStandardsIgnoreLine Legacy support for WP < 4.6.
// phpcs:ignore WordPress.WP.DeprecatedFunctions.wp_get_sitesFound -- Legacy support for WP < 4.6.
$sites = wp_get_sites();
} else {
return;
@ -112,6 +117,8 @@ class StatifyBlacklist_System extends StatifyBlacklist {
* @since 1.4.3
*
* @param integer $site_id Site ID.
*
* @return void
*/
public static function uninstall_site( $site_id ) {
$old = get_current_blog_id();
@ -124,14 +131,16 @@ class StatifyBlacklist_System extends StatifyBlacklist {
* Upgrade plugin options.
*
* @since 1.2.0
*
* @return void
*/
public static function upgrade() {
self::update_options();
// Check if config array is not associative (pre 1.2.0).
if ( array_keys( self::$_options['referer'] ) === range( 0, count( self::$_options['referer'] ) - 1 ) ) {
if ( array_keys( self::$options['referer'] ) === range( 0, count( self::$options['referer'] ) - 1 ) ) {
// Flip referer array to make domains keys.
$options = self::$_options;
$options['referer'] = array_flip( self::$_options['referer'] );
$options = self::$options;
$options['referer'] = array_flip( self::$options['referer'] );
if ( self::$multisite ) {
update_site_option( 'statify-blacklist', $options );
} else {
@ -140,14 +149,14 @@ class StatifyBlacklist_System extends StatifyBlacklist {
}
// Version not set (pre 1.3.0) or older than 1.4.
if ( ! isset( self::$_options['version'] ) || self::$_options['version'] < 1.4 ) {
if ( ! isset( self::$options['version'] ) || self::$options['version'] < 1.4 ) {
// Upgrade options to new schema.
$options = array(
'referer' => array(
'active' => self::$_options['active_referer'],
'cron' => self::$_options['cron_referer'],
'regexp' => self::$_options['referer_regexp'],
'blacklist' => self::$_options['referer'],
'active' => self::$options['active_referer'],
'cron' => self::$options['cron_referer'],
'regexp' => self::$options['referer_regexp'],
'blacklist' => self::$options['referer'],
),
'target' => array(
'active' => 0,
@ -170,9 +179,9 @@ class StatifyBlacklist_System extends StatifyBlacklist {
}
// Version older than current major release.
if ( self::VERSION_MAIN > self::$_options['version'] ) {
if ( self::VERSION_MAIN > self::$options['version'] ) {
// Merge default options with current config, assuming only additive changes.
$options = array_merge_recursive( self::default_options(), self::$_options );
$options = array_merge_recursive( self::default_options(), self::$options );
$options['version'] = self::VERSION_MAIN;
if ( self::$multisite ) {
update_site_option( 'statify-blacklist', $options );

View File

@ -8,8 +8,10 @@
* @since 1.0.0
*/
// Quit.
defined( 'ABSPATH' ) || exit;
// Quit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Statify Blacklist.
@ -26,13 +28,42 @@ class StatifyBlacklist {
*/
const VERSION_MAIN = 1.4;
/**
* Operation mode "normal".
*
* @var integer MODE_NORMAL
*/
const MODE_NORMAL = 0;
/**
* Operation mode "regular expression".
*
* @var integer MODE_REGEX
*/
const MODE_REGEX = 1;
/**
* Operation mode "regular expression case insensitive".
*
* @var integer MODE_REGEX_CI
*/
const MODE_REGEX_CI = 2;
/**
* Operation mode "keyword".
*
* @since 1.5.0
* @var integer MODE_KEYWORD
*/
const MODE_KEYWORD = 3;
/**
* Plugin options.
*
* @since 1.0.0
* @var array $_options
* @var array $options
*/
public static $_options;
public static $options;
/**
* Multisite Status.
@ -42,30 +73,12 @@ class StatifyBlacklist {
*/
public static $multisite;
/**
* Class self initialize.
*
* @since 1.0.0
* @deprecated 1.4.2 Replaced by init().
*/
public static function instance() {
self::init();
}
/**
* Class constructor.
*
* @since 1.0.0
* @deprecated 1.4.2 Replaced by init().
*/
public function __construct() {
self::init();
}
/**
* Plugin initialization.
*
* @since 1.4.2
*
* @return void
*/
public static function init() {
// Skip on autosave or AJAX.
@ -80,41 +93,19 @@ 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'] ) {
add_filter( 'statify__skip_tracking', array( 'StatifyBlacklist', 'apply_blacklist_filter' ) );
}
// Admin only filters.
if ( is_admin() ) {
// Load Textdomain (only needed for backend.
load_plugin_textdomain( 'statifyblacklist', false, STATIFYBLACKLIST_DIR . '/lang/' );
// Add actions.
add_action( 'wpmu_new_blog', array( 'StatifyBlacklist_System', 'install_site' ) );
add_action( 'delete_blog', array( 'StatifyBlacklist_System', 'uninstall_site' ) );
add_filter( 'plugin_row_meta', array( 'StatifyBlacklist_Admin', 'plugin_meta_link' ), 10, 2 );
if ( self::$multisite ) {
add_action( 'network_admin_menu', array( 'StatifyBlacklist_Admin', 'add_menu_page' ) );
add_filter(
'network_admin_plugin_action_links', array(
'StatifyBlacklist_Admin',
'plugin_actions_links',
),
10,
2
);
} else {
add_action( 'admin_menu', array( 'StatifyBlacklist_Admin', 'add_menu_page' ) );
add_filter( 'plugin_action_links', array( 'StatifyBlacklist_Admin', 'plugin_actions_links' ), 10, 2 );
}
StatifyBlacklist_Admin::init();
}
// CronJob to clean up database.
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
if ( 1 === self::$_options['referer']['cron'] || 1 === self::$_options['target']['cron'] ) {
add_action( 'statify_cleanup', array( 'StatifyBlacklist_Admin', 'cleanup_database' ) );
}
if ( defined( 'DOING_CRON' ) && DOING_CRON &&
( 1 === self::$options['referer']['cron'] || 1 === self::$options['target']['cron'] ) ) {
add_action( 'statify_cleanup', array( 'StatifyBlacklist_Admin', 'cleanup_database' ) );
}
}
@ -125,6 +116,8 @@ class StatifyBlacklist {
* @since 1.2.1 update_options($options = null) Parameter with default value introduced.
*
* @param array $options Optional. New options to save.
*
* @return void
*/
public static function update_options( $options = null ) {
if ( self::$multisite ) {
@ -132,7 +125,7 @@ class StatifyBlacklist {
} else {
$o = get_option( 'statify-blacklist' );
}
self::$_options = wp_parse_args( $o, self::default_options() );
self::$options = wp_parse_args( $o, self::default_options() );
}
/**
@ -173,52 +166,72 @@ class StatifyBlacklist {
*/
public static function apply_blacklist_filter() {
// Referer blacklist.
if ( isset( self::$_options['referer']['active'] ) && 0 !== self::$_options['referer']['active'] ) {
// Regular Expression filtering since 1.3.0.
if ( isset( self::$_options['referer']['regexp'] ) && self::$_options['referer']['regexp'] > 0 ) {
// Get full referer string.
$referer = wp_get_raw_referer();
if ( ! $referer ) {
$referer = '';
}
// Merge given regular expressions into one.
$regexp = '/' . implode( '|', array_keys( self::$_options['referer']['blacklist'] ) ) . '/';
if ( 2 === self::$_options['referer']['regexp'] ) {
$regexp .= 'i';
}
if ( isset( self::$options['referer']['active'] ) && 0 !== self::$options['referer']['active'] ) {
// Determine filter mode.
$mode = isset( self::$options['referer']['regexp'] ) ? intval( self::$options['referer']['regexp'] ) : 0;
// Check blacklist (no return to continue filtering #12).
if ( 1 === preg_match( $regexp, $referer ) ) {
return true;
}
} else {
// Extract relevant domain parts.
$referer = wp_parse_url( wp_get_raw_referer() );
$referer = strtolower( ( isset( $referer['host'] ) ? $referer['host'] : '' ) );
// Get full referer string.
$referer = wp_get_raw_referer();
if ( ! $referer ) {
$referer = '';
}
// Get blacklist.
$blacklist = self::$_options['referer']['blacklist'];
switch ( $mode ) {
// Check blacklist.
if ( isset( $blacklist[ $referer ] ) ) {
return true;
}
// 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['referer']['blacklist'] ),
self::MODE_REGEX_CI === self::$options['referer']['regexp']
);
// Check blacklist (no return to continue filtering #12).
if ( 1 === preg_match( $regexp, $referer ) ) {
return true;
}
break;
// Keyword filter since 1.5.0 (#15).
case self::MODE_KEYWORD:
// Get blacklist.
$blacklist = self::$options['referer']['blacklist'];
foreach ( array_keys( $blacklist ) as $keyword ) {
if ( false !== strpos( strtolower( $referer ), strtolower( $keyword ) ) ) {
return true;
}
}
break;
// Standard domain filter.
default:
// Extract relevant domain parts.
$referer = wp_parse_url( $referer );
$referer = strtolower( ( isset( $referer['host'] ) ? $referer['host'] : '' ) );
// Get blacklist.
$blacklist = self::$options['referer']['blacklist'];
// Check blacklist.
if ( isset( $blacklist[ $referer ] ) ) {
return true;
}
}
}
// Target blacklist (since 1.4.0).
if ( isset( self::$_options['target']['active'] ) && 0 !== self::$_options['target']['active'] ) {
if ( isset( self::$options['target']['active'] ) && 0 !== self::$options['target']['active'] ) {
// Regular Expression filtering since 1.3.0.
if ( isset( self::$_options['target']['regexp'] ) && 0 < self::$_options['target']['regexp'] ) {
if ( isset( self::$options['target']['regexp'] ) && 0 < self::$options['target']['regexp'] ) {
// Get full referer string.
// @codingStandardsIgnoreStart The globals are checked.
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/' );
// @codingStandardsIgnoreEnd
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_URL ) : '/' );
// Merge given regular expressions into one.
$regexp = '/' . implode( '|', array_keys( self::$_options['target']['blacklist'] ) ) . '/';
if ( 2 === self::$_options['target']['regexp'] ) {
$regexp .= 'i';
}
$regexp = self::regex(
array_keys( self::$options['target']['blacklist'] ),
self::MODE_REGEX_CI === self::$options['target']['regexp']
);
// Check blacklist (no return to continue filtering #12).
if ( 1 === preg_match( $regexp, $target ) ) {
@ -226,11 +239,9 @@ class StatifyBlacklist {
}
} else {
// Extract target page.
// @codingStandardsIgnoreStart The globals are checked.
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/' );
// @codingStandardsIgnoreEnd
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_URL ) : '/' );
// Get blacklist.
$blacklist = self::$_options['target']['blacklist'];
$blacklist = self::$options['target']['blacklist'];
// Check blacklist.
if ( isset( $blacklist[ $target ] ) ) {
return true;
@ -239,10 +250,10 @@ class StatifyBlacklist {
}
// IP blacklist (since 1.4.0).
if ( isset( self::$_options['ip']['active'] ) && 0 !== self::$_options['ip']['active'] ) {
if ( isset( self::$options['ip']['active'] ) && 0 !== self::$options['ip']['active'] ) {
$ip = self::get_ip();
if ( false !== ( $ip ) ) {
foreach ( self::$_options['ip']['blacklist'] as $net ) {
foreach ( self::$options['ip']['blacklist'] as $net ) {
if ( self::cidr_match( $ip, $net ) ) {
return true;
}
@ -254,6 +265,37 @@ class StatifyBlacklist {
return null;
}
/**
* Preprocess regular expression provided by the user, i.e. add delimiters and optional ci flag.
*
* @param string|array $expression Original expression string or array of expressions.
* @param string|array $case_insensitive Make expression match case-insensitive.
*
* @return string Preprocessed expression ready for preg_match().
*/
protected static function regex( $expression, $case_insensitive ) {
$res = '/';
if ( is_string( $expression ) ) {
$res .= str_replace( '/', '\/', $expression );
} elseif ( is_array( $expression ) ) {
$res .= implode(
'|',
array_map(
function ( $e ) {
return str_replace( '/', '\/', $e );
},
$expression
)
);
}
$res .= '/';
if ( $case_insensitive ) {
$res .= 'i';
}
return $res;
}
/**
* Helper method to determine the client's IP address.
*
@ -277,15 +319,14 @@ class StatifyBlacklist {
'REMOTE_ADDR',
) as $k
) {
// @codingStandardsIgnoreStart The globals are checked.
if ( isset( $_SERVER[ $k ] ) ) {
// phpcs:ignore
foreach ( explode( ',', $_SERVER[ $k ] ) as $ip ) {
if ( false !== filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return $ip;
}
}
}
// @codingStandardsIgnoreEnd
}
return false;
@ -361,6 +402,6 @@ class StatifyBlacklist {
}
return ( 0 === substr_compare( sprintf( '%032b', ip2long( $ip ) ), sprintf( '%032b', ip2long( $base ) ), 0, $mask ) );
} // End if().
}
}
}

View File

@ -1,17 +1,7 @@
{
"name": "statify-blacklist",
"version": "1.4.4",
"version": "1.5.0",
"description": "A blacklist extension for the famous Statify WordPress plugin",
"author": "Stefan Kalscheuer",
"license": "GPL-2.0+",
"devDependencies": {
"gulp": "^3.9.1",
"gulp-clean": "^0.3.2",
"gulp-copy": "^1.0.1",
"gulp-zip": "^4.0.0",
"gulp-composer": "^0.4.4",
"gulp-phpunit": "^0.24.1",
"gulp-phpcs": "^2.1.0",
"child_process": "^1.0.2"
}
"license": "GPL-2.0+"
}

View File

@ -2,23 +2,20 @@
<ruleset name="StatifyBlacklist">
<description>Derived from WordPress Coding Standard</description>
<arg value="psvn"/>
<arg value="psv"/>
<arg name="colors"/>
<!-- Files to sniff -->
<file>inc</file>
<file>statify-blacklist.php</file>
<file>inc</file>
<file>views</file>
<!-- Compliance with WordPress Coding Standard -->
<config name="minimum_supported_wp_version" value="4.4"/>
<config name="minimum_supported_wp_version" value="4.7"/>
<rule ref="WordPress">
<!-- The plugin uses switch_to_blog for multisite handling. -->
<exclude name="WordPress.VIP.RestrictedFunctions.switch_to_blog"/>
<exclude name="WordPress.VIP.RestrictedFunctions.switch_to_blog_switch_to_blog"/>
<!-- Direct queries used to clean up statify table. -->
<exclude name="WordPress.VIP.DirectDatabaseQuery.DirectQuery"/>
<exclude name="WordPress.VIP.DirectDatabaseQuery.NoCaching"/>
<exclude name="WordPress.DB.DirectDatabaseQuery.DirectQuery"/>
<exclude name="WordPress.DB.DirectDatabaseQuery.NoCaching"/>
</rule>
<!-- PHP compatibility level -->

View File

@ -12,6 +12,6 @@
</filter>
<logging>
<log type="coverage-clover" target="tests-clover.xml"/>
<log type="junit" target="tests-junit.xml" logIncompleteSkipped="false"/>
<log type="junit" target="tests-junit.xml"/>
</logging>
</phpunit>

View File

@ -10,11 +10,10 @@
* Plugin Name: Statify Blacklist
* Plugin URI: https://wordpress.org/plugins/statify-blacklist/
* Description: Extension for the Statify plugin to add a customizable blacklists.
* Version: 1.4.4
* Version: 1.5.0
* Author: Stefan Kalscheuer (@stklcode)
* Author URI: https://www.stklcode.de
* Text Domain: statify-blacklist
* Domain Path: /lang
* License: GPLv2 or later
*
* Statify Blacklist is free software: you can redistribute it and/or modify
@ -28,34 +27,42 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Statify Blacklist. If not, see http://www.gnu.org/licenses/gpl-2.0.html.
* along with Statify Blacklist. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
*/
// Quit.
defined( 'ABSPATH' ) || exit;
// Quit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Constants.
define( 'STATIFYBLACKLIST_FILE', __FILE__ );
define( 'STATIFYBLACKLIST_DIR', dirname( __FILE__ ) );
define( 'STATIFYBLACKLIST_BASE', plugin_basename( __FILE__ ) );
// System Hooks.
add_action( 'plugins_loaded', array( 'StatifyBlacklist', 'init' ) );
// Check for compatibility.
if ( statify_blacklist_compatibility_check() ) {
// System Hooks.
add_action( 'plugins_loaded', array( 'StatifyBlacklist', 'init' ) );
register_activation_hook( STATIFYBLACKLIST_FILE, array( 'StatifyBlacklist_System', 'install' ) );
register_activation_hook( STATIFYBLACKLIST_FILE, array( 'StatifyBlacklist_System', 'install' ) );
register_uninstall_hook( STATIFYBLACKLIST_FILE, array( 'StatifyBlacklist_System', 'uninstall' ) );
register_uninstall_hook( STATIFYBLACKLIST_FILE, array( 'StatifyBlacklist_System', 'uninstall' ) );
// Upgrade hook.
register_activation_hook( STATIFYBLACKLIST_FILE, array( 'StatifyBlacklist_System', 'upgrade' ) );
// Upgrade hook.
register_activation_hook( STATIFYBLACKLIST_FILE, array( 'StatifyBlacklist_System', 'upgrade' ) );
// Autoload.
spl_autoload_register( 'statify_blacklist_autoload' );
// Autoload.
spl_autoload_register( 'statify_blacklist_autoload' );
} else {
// Disable plugin, if active.
add_action( 'admin_init', 'statify_blacklist_disable' );
}
/**
* Autoloader for StatifyBlacklist classes.
*
* @param string $class Name of the class to load.
* @param string $class Name of the class to load.
*
* @since 1.0.0
*/
@ -74,3 +81,59 @@ function statify_blacklist_autoload( $class ) {
);
}
}
/**
* Check for compatibility with PHP and WP version.
*
* @since 1.5.0
*
* @return boolean Whether minimum WP and PHP versions are met.
*/
function statify_blacklist_compatibility_check() {
return version_compare( $GLOBALS['wp_version'], '4.7', '>=' ) &&
version_compare( phpversion(), '5.5', '>=' );
}
/**
* Disable plugin if active and incompatible.
*
* @since 1.5.0
*
* @return void
*/
function statify_blacklist_disable() {
if ( is_plugin_active( STATIFYBLACKLIST_BASE ) ) {
deactivate_plugins( STATIFYBLACKLIST_BASE );
add_action( 'admin_notices', 'statify_blacklist_disabled_notice' );
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['activate'] ) ) {
unset( $_GET['activate'] );
}
// phpcs:enable
}
}
/**
* Admin notification for unmet requirements.
*
* @since 1.5.0
*
* @return void
*/
function statify_blacklist_disabled_notice() {
echo '<div class="notice notice-error is-dismissible"><p><strong>';
printf(
/* translators: minimum version numbers for WordPress and PHP inserted at placeholders */
esc_html__( 'Statify Blacklist requires at least WordPress %1$s and PHP %2$s.', 'statify-blacklist' ),
'4.7',
'5.5'
);
echo '<br>';
printf(
/* translators: current version numbers for WordPress and PHP inserted at placeholders */
esc_html__( 'Your site is running WordPress %1$s on PHP %2$s, thus the plugin has been disabled.', 'statify-blacklist' ),
esc_html( $GLOBALS['wp_version'] ),
esc_html( phpversion() )
);
echo '</strong></p></div>';
}

View File

@ -20,17 +20,17 @@ const ABSPATH = false;
/**
* The StatifyBlacklist base class.
*/
require_once( 'inc/class-statifyblacklist.php' );
require_once __DIR__ . '/../inc/class-statifyblacklist.php';
/**
* The StatifyBlacklist system class.
*/
require_once( 'inc/class-statifyblacklist-system.php' );
require_once __DIR__ . '/../inc/class-statifyblacklist-system.php';
/**
* The StatifyBlacklist admin class.
*/
require_once( 'inc/class-statifyblacklist-admin.php' );
require_once __DIR__ . '/../inc/class-statifyblacklist-admin.php';
/**
* Class StatifyBlacklistTest.
@ -43,10 +43,12 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
/**
* Test simple referer filter.
*
* @return void
*/
public function test_referer_filter() {
// Prepare Options: 2 blacklisted domains, disabled.
StatifyBlacklist::$_options = array(
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
@ -86,7 +88,7 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter and run tests again.
StatifyBlacklist::$_options['referer']['active'] = 1;
StatifyBlacklist::$options['referer']['active'] = 1;
unset( $_SERVER['HTTP_REFERER'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
@ -103,10 +105,12 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
/**
* Test referer filter using regular expressions.
*
* @return void
*/
public function testRefererRegexFilter() {
public function test_referer_regex_filter() {
// Prepare Options: 2 regular expressions.
StatifyBlacklist::$_options = array(
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 1,
'cron' => 0,
@ -147,19 +151,74 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
// Matching both.
$_SERVER['HTTP_REFERER'] = 'http://example.net/test/me';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Mathinc with wrong case.
// Matching 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;
StatifyBlacklist::$options['referer']['regexp'] = 2;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}
/**
* Test referer filter using keywords.
*
* @return void
*/
public function test_referer_keyword_filter() {
// Prepare Options: 2 regular expressions.
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 1,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_KEYWORD,
'blacklist' => array(
'example' => 0,
'test' => 1,
),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'ip' => array(
'active' => 0,
'blacklist' => array(),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// 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() );
// Matching with wrong case.
$_SERVER['HTTP_REFERER'] = 'http://eXaMpLe.NeT/tEsT/mE';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}
/**
* Test the upgrade methodology for configuration options.
*
* @return void
*/
public function testUpgrade() {
public function test_upgrade() {
// Create configuration of version 1.3.
$options13 = array(
'active_referer' => 1,
@ -179,138 +238,141 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
StatifyBlacklist_System::upgrade();
// Retrieve updated options.
$optionsUpdated = get_option( 'statify-blacklist' );
$options_updated = get_option( 'statify-blacklist' );
// Verify size against default options (no junk left).
$this->assertEquals( 4, count( $optionsUpdated ) );
$this->assertEquals( 4, count( $optionsUpdated['referer'] ) );
$this->assertEquals( 4, count( $optionsUpdated['target'] ) );
$this->assertEquals( 2, count( $optionsUpdated['ip'] ) );
$this->assertEquals( 4, count( $options_updated ) );
$this->assertEquals( 4, count( $options_updated['referer'] ) );
$this->assertEquals( 4, count( $options_updated['target'] ) );
$this->assertEquals( 2, count( $options_updated['ip'] ) );
// Verify that original attributes are unchanged.
$this->assertEquals( $options13['active_referer'], $optionsUpdated['referer']['active'] );
$this->assertEquals( $options13['cron_referer'], $optionsUpdated['referer']['cron'] );
$this->assertEquals( $options13['referer'], $optionsUpdated['referer']['blacklist'] );
$this->assertEquals( $options13['referer_regexp'], $optionsUpdated['referer']['regexp'] );
$this->assertEquals( $options13['active_referer'], $options_updated['referer']['active'] );
$this->assertEquals( $options13['cron_referer'], $options_updated['referer']['cron'] );
$this->assertEquals( $options13['referer'], $options_updated['referer']['blacklist'] );
$this->assertEquals( $options13['referer_regexp'], $options_updated['referer']['regexp'] );
// Verify that new attributes are present in config and filled with default values (disabled, empty).
$this->assertEquals( 0, $optionsUpdated['target']['active'] );
$this->assertEquals( 0, $optionsUpdated['target']['cron'] );
$this->assertEquals( 0, $optionsUpdated['target']['regexp'] );
$this->assertEquals( array(), $optionsUpdated['target']['blacklist'] );
$this->assertEquals( 0, $optionsUpdated['ip']['active'] );
$this->assertEquals( array(), $optionsUpdated['ip']['blacklist'] );
$this->assertEquals( 0, $options_updated['target']['active'] );
$this->assertEquals( 0, $options_updated['target']['cron'] );
$this->assertEquals( 0, $options_updated['target']['regexp'] );
$this->assertEquals( array(), $options_updated['target']['blacklist'] );
$this->assertEquals( 0, $options_updated['ip']['active'] );
$this->assertEquals( array(), $options_updated['ip']['blacklist'] );
// Verify that version number has changed to current release.
$this->assertEquals( StatifyBlacklist::VERSION_MAIN, $optionsUpdated['version'] );
$this->assertEquals( StatifyBlacklist::VERSION_MAIN, $options_updated['version'] );
}
/**
* Test CIDR address matching for IP filter (#7)
* Test CIDR address matching for IP filter (#7).
*
* @return void
*/
public function testCidrMatch() {
public function test_cidr_match() {
// IPv4 tests.
$this->assertTrue( invokeStatic( StatifyBlacklist::class, 'cidr_match', array( '127.0.0.1', '127.0.0.1' ) ) );
$this->assertTrue( invokeStatic( StatifyBlacklist::class, 'cidr_match', array( '127.0.0.1', '127.0.0.1/32' ) ) );
$this->assertTrue( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '127.0.0.1', '127.0.0.1' ) ) );
$this->assertTrue( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '127.0.0.1', '127.0.0.1/32' ) ) );
$this->assertFalse(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'127.0.0.1',
'127.0.0.1/33',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '127.0.0.1', '127.0.0.1/33' )
)
);
$this->assertFalse(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'127.0.0.1',
'127.0.0.1/-1',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '127.0.0.1', '127.0.0.1/-1' )
)
);
$this->assertTrue(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'192.0.2.123',
'192.0.2.0/24',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.2.123', '192.0.2.0/24' )
)
);
$this->assertFalse(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'192.0.3.123',
'192.0.2.0/24',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.3.123', '192.0.2.0/24' )
)
);
$this->assertTrue(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'192.0.2.123',
'192.0.2.120/29',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.2.123', '192.0.2.120/29' )
)
);
$this->assertFalse(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'192.0.2.128',
'192.0.2.120/29',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.2.128', '192.0.2.120/29' )
)
);
$this->assertTrue( invokeStatic( StatifyBlacklist::class, 'cidr_match', array( '10.11.12.13', '10.0.0.0/8' ) ) );
$this->assertTrue( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '10.11.12.13', '10.0.0.0/8' ) ) );
$this->assertFalse(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'10.11.12.345',
'10.0.0.0/8',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '10.11.12.345', '10.0.0.0/8' )
)
);
// IPv6 tests.
$this->assertTrue( invokeStatic( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1' ) ) );
$this->assertTrue( invokeStatic( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1/128' ) ) );
$this->assertFalse( invokeStatic( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1/129' ) ) );
$this->assertFalse( invokeStatic( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1/-1' ) ) );
$this->assertTrue( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1' ) ) );
$this->assertTrue( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1/128' ) ) );
$this->assertFalse( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1/129' ) ) );
$this->assertFalse( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '::1', '::1/-1' ) ) );
$this->assertTrue(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'2001:db8:a0b:12f0:1:2:3:4',
'2001:db8:a0b:12f0::1/64 ',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '2001:db8:a0b:12f0:1:2:3:4', '2001:db8:a0b:12f0::1/64 ' )
)
);
$this->assertTrue(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'2001:db8:a0b:12f0::123:456',
'2001:db8:a0b:12f0::1/96 ',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '2001:db8:a0b:12f0::123:456', '2001:db8:a0b:12f0::1/96 ' )
)
);
$this->assertFalse(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'2001:db8:a0b:12f0::1:132:465',
'2001:db8:a0b:12f0::1/96 ',
)
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '2001:db8:a0b:12f0::1:132:465', '2001:db8:a0b:12f0::1/96 ' )
)
);
}
/**
* Test sanitization of IP addresses
* Test sanitization of IP addresses.
*
* @return void
*/
public function testSanitizeIPs() {
public function test_sanitize_ips() {
// IPv4 tests.
$valid = array( '192.0.2.123', '192.0.2.123/32', '192.0.2.0/24', '192.0.2.128/25' );
$invalid = array( '12.34.56.789', '192.0.2.123/33', '192.0.2.123/-1' );
$result = invokeStatic( StatifyBlacklist_Admin::class, 'sanitizeIPs', array( array_merge( $valid, $invalid ) ) );
$result = invoke_static( StatifyBlacklist_Admin::class, 'sanitize_ips', array( array_merge( $valid, $invalid ) ) );
$this->assertNotFalse( $result );
$this->assertInternalType( 'array', $result );
/*
* Unfortunately this is necessary as long as we run PHP 5 tests, because "assertInternalType" is deprecated
* as of PHPUnit 8, but "assertIsArray" has been introduces in PHPUnit 7.5 which requires PHP >= 7.1.
*/
if ( method_exists( $this, 'assertIsArray' ) ) {
$this->assertIsArray( $result );
} else {
$this->assertInternalType( 'array', $result );
}
$this->assertEquals( $valid, $result );
// IPv6 tests.
@ -327,28 +389,34 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
'2001:db8:a0b:12f0::/129',
'1:2:3:4:5:6:7:8:9',
);
$result = invokeStatic( StatifyBlacklist_Admin::class, 'sanitizeIPs', array( array_merge( $valid, $invalid ) ) );
$result = invoke_static( StatifyBlacklist_Admin::class, 'sanitize_ips', array( array_merge( $valid, $invalid ) ) );
$this->assertNotFalse( $result );
$this->assertInternalType( 'array', $result );
if ( method_exists( $this, 'assertIsArray' ) ) {
$this->assertIsArray( $result );
} else {
$this->assertInternalType( 'array', $result );
}
$this->assertEquals( $valid, $result );
}
/**
* Test IP filter (#7).
*
* @return void
*/
public function testIPFilter() {
public function test_ip_filter() {
// Prepare Options: 2 blacklisted IPs, disabled.
StatifyBlacklist::$_options = array(
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'ip' => array(
@ -368,7 +436,7 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter.
StatifyBlacklist::$_options['ip']['active'] = 1;
StatifyBlacklist::$options['ip']['active'] = 1;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Try matching v6 address.
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::1';
@ -379,11 +447,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::2';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Subnet matching.
StatifyBlacklist::$_options['ip']['blacklist'] = array(
StatifyBlacklist::$options['ip']['blacklist'] = array(
'192.0.2.0/25',
'2001:db8:a0b:12f0::/96',
);
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
@ -405,20 +473,22 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
/**
* Test simple target filter.
*
* @return void
*/
public function testTargetFilter() {
public function test_target_filter() {
// Prepare Options: 2 blacklisted domains, disabled.
StatifyBlacklist::$_options = array(
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(
'/excluded/page/' => 0,
'/?page_id=3' => 1,
@ -451,7 +521,7 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter and run tests again.
StatifyBlacklist::$_options['target']['active'] = 1;
StatifyBlacklist::$options['target']['active'] = 1;
unset( $_SERVER['REQUEST_URI'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
@ -483,11 +553,11 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
*/
public function test_combined_filters() {
// Prepare Options: simple referer + simple target + ip.
StatifyBlacklist::$_options = array(
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 1,
'cron' => 0,
'regexp' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(
'example.com' => 0,
),
@ -495,15 +565,15 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
'target' => array(
'active' => 1,
'cron' => 0,
'regexp' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(
'/excluded/page/' => 0
'/excluded/page/' => 0,
),
),
'ip' => array(
'active' => 1,
'blacklist' => array(
'192.0.2.123'
'192.0.2.123',
),
),
'version' => StatifyBlacklist::VERSION_MAIN,
@ -514,8 +584,8 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
// No match.
$_SERVER['HTTP_REFERER'] = 'https://example.net';
$_SERVER['REQUEST_URI'] = '/normal/page/';
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
$_SERVER['REQUEST_URI'] = '/normal/page/';
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
unset( $_SERVER['HTTP_X_FORWARDED_FOR'] );
unset( $_SERVER['HTTP_X_REAL_IP'] );
@ -525,7 +595,7 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
// Matching target.
$_SERVER['HTTP_REFERER'] = 'https://example.net';
$_SERVER['REQUEST_URI'] = '/excluded/page/';
$_SERVER['REQUEST_URI'] = '/excluded/page/';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Matching IP.
@ -535,16 +605,21 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
// Same for RegExp filters.
StatifyBlacklist::$_options['referer']['regexp'] = 1;
StatifyBlacklist::$_options['referer']['blacklist'] = array( 'example\.com' => 0 );
StatifyBlacklist::$_options['target']['regexp'] = 1;
StatifyBlacklist::$_options['target']['blacklist'] = array( '\/excluded\/.*' => 0 );
StatifyBlacklist::$options['referer']['regexp'] = StatifyBlacklist::MODE_REGEX;
StatifyBlacklist::$options['referer']['blacklist'] = array( 'example\.com' => 0 );
StatifyBlacklist::$options['target']['regexp'] = StatifyBlacklist::MODE_REGEX;
StatifyBlacklist::$options['target']['blacklist'] = array( '/excluded/.*' => 0 );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'https://example.com';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Check case-insensitive match.
$_SERVER['HTTP_REFERER'] = 'https://eXaMpLe.com';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
StatifyBlacklist::$options['referer']['regexp'] = StatifyBlacklist::MODE_REGEX_CI;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'https://example.net';
$_SERVER['REQUEST_URI'] = '/excluded/page/';
$_SERVER['REQUEST_URI'] = '/excluded/page/';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/normal/page/';
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
@ -556,9 +631,9 @@ class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
/** @ignore */
function invokeStatic( $class, $methodName, $parameters = array() ) {
function invoke_static( $class, $method_name, $parameters = array() ) {
$reflection = new \ReflectionClass( $class );
$method = $reflection->getMethod( $methodName );
$method = $reflection->getMethod( $method_name );
$method->setAccessible( true );
return $method->invokeArgs( null, $parameters );

View File

@ -9,6 +9,8 @@
* @since 1.0.0
*/
// phpcs:disable WordPress.WhiteSpace.PrecisionAlignment.Found
// Quit.
defined( 'ABSPATH' ) || exit;
@ -19,7 +21,7 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) {
// Check user capabilities.
if ( ! current_user_can( 'manage_options' ) ) {
die( __( 'Are you sure you want to do this?' ) );
die( esc_html__( 'Are you sure you want to do this?' ) );
}
if ( ! empty( $_POST['cleanUp'] ) ) {
@ -27,24 +29,63 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) {
StatifyBlacklist_Admin::cleanup_database();
} else {
// Extract referer array.
if ( empty( trim( $_POST['statifyblacklist']['referer']['blacklist'] ) ) ) {
if ( isset( $_POST['statifyblacklist']['referer']['blacklist'] ) ) {
$referer_str = sanitize_textarea_field( wp_unslash( $_POST['statifyblacklist']['referer']['blacklist'] ) );
}
if ( empty( trim( $referer_str ) ) ) {
$referer = array();
} else {
$referer = explode( "\r\n", $_POST['statifyblacklist']['referer']['blacklist'] );
$referer = array_filter(
array_map(
function ( $a ) {
return trim( $a );
},
explode( "\r\n", $referer_str )
),
function ( $a ) {
return ! empty( $a );
}
);
}
// Extract target array.
if ( empty( trim( $_POST['statifyblacklist']['target']['blacklist'] ) ) ) {
if ( isset( $_POST['statifyblacklist']['target']['blacklist'] ) ) {
$target_str = sanitize_textarea_field( wp_unslash( $_POST['statifyblacklist']['target']['blacklist'] ) );
}
if ( empty( trim( $target_str ) ) ) {
$target = array();
} else {
$target = explode( "\r\n", str_replace( '\\\\', '\\', $_POST['statifyblacklist']['target']['blacklist'] ) );
$target = array_filter(
array_map(
function ( $a ) {
return trim( $a );
},
explode( "\r\n", str_replace( '\\\\', '\\', $target_str ) )
),
function ( $a ) {
return ! empty( $a );
}
);
}
// Extract IP array.
if ( empty( trim( $_POST['statifyblacklist']['ip']['blacklist'] ) ) ) {
if ( isset( $_POST['statifyblacklist']['ip']['blacklist'] ) ) {
$ip_str = sanitize_textarea_field( wp_unslash( $_POST['statifyblacklist']['ip']['blacklist'] ) );
}
if ( empty( trim( $ip_str ) ) ) {
$ip = array();
} else {
$ip = explode( "\r\n", $_POST['statifyblacklist']['ip']['blacklist'] );
$ip = array_filter(
array_map(
function ( $a ) {
return trim( $a );
},
explode( "\r\n", $ip_str )
),
function ( $a ) {
return ! empty( $a );
}
);
}
// Update options (data will be sanitized).
@ -79,21 +120,33 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) {
// Generate messages.
if ( false !== $statifyblacklist_update_result ) {
if ( array_key_exists( 'referer', $statifyblacklist_update_result ) ) {
$statifyblacklist_post_warning = __( 'Some URLs are invalid and have been sanitized.', 'statify-blacklist' );
} elseif ( array_key_exists( 'ip', $statifyblacklist_update_result ) ) {
$statifyblacklist_post_warning = array();
if ( ! empty( $statifyblacklist_update_result['referer']['diff'] ) ) {
$statifyblacklist_post_warning[] = __( 'Some URLs are invalid and have been sanitized.', 'statify-blacklist' );
}
if ( ! empty( $statifyblacklist_update_result['referer']['invalid'] ) ) {
$statifyblacklist_post_warning[] = __( 'Some regular expressions are invalid:', 'statify-blacklist' ) . '<br>' . implode( '<br>', $statifyblacklist_update_result['referer']['invalid'] );
}
if ( ! empty( $statifyblacklist_update_result['ip']['diff'] ) ) {
// translators: List of invalid IP addresses (comma separated).
$statifyblacklist_post_warning = sprintf( __( 'Some IPs are invalid : %s', 'statify-blacklist' ), implode( ', ', $statifyblacklist_update_result['ip'] ) );
$statifyblacklist_post_warning[] = sprintf( __( 'Some IPs are invalid: %s', 'statify-blacklist' ), implode( ', ', $statifyblacklist_update_result['ip']['diff'] ) );
}
} else {
$statifyblacklist_post_success = __( 'Settings updated successfully.', 'statify-blacklist' );
}
} // End if().
} // End if().
}
}
/*
* Disable some code style rules that are impractical for textarea content:
*
* phpcs:disable Squiz.PHP.EmbeddedPhp.ContentBeforeOpen
* phpcs:disable Squiz.PHP.EmbeddedPhp.ContentAfterEnd
*/
?>
<div class="wrap">
<h1><?php esc_html_e( 'Statify Blacklist', 'statify-blacklist' ) ?></h1>
<h1><?php esc_html_e( 'Statify Blacklist', 'statify-blacklist' ); ?></h1>
<?php
if ( is_plugin_inactive( 'statify/statify.php' ) ) {
print '<div class="notice notice-warning"><p>';
@ -101,11 +154,12 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) {
print '</p></div>';
}
if ( isset( $statifyblacklist_post_warning ) ) {
print '<div class="notice notice-warning"><p>' .
esc_html( $statifyblacklist_post_warning );
print '<br/>';
esc_html_e( 'Settings have not been saved yet.', 'statify-blacklist' );
print '</p></div>';
foreach ( $statifyblacklist_post_warning as $w ) {
print '<div class="notice notice-warning"><p>' .
wp_kses( $w, array( 'br' => array() ) ) .
'</p></div>';
}
print '<div class="notice notice-warning"><p>' . esc_html__( 'Settings have not been saved yet.', 'statify-blacklist' ) . '</p></div>';
}
if ( isset( $statifyblacklist_post_success ) ) {
print '<div class="notice notice-success"><p>' .
@ -114,179 +168,223 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) {
}
?>
<form action="" method="post" id="statify-blacklist-settings">
<fieldset>
<h2><?php esc_html_e( 'Referer blacklist', 'statify-blacklist' ); ?></h2>
<ul style="list-style: none;">
<li>
<label for="statify-blacklist_active_referer">
<input type="checkbox" name="statifyblacklist[referer][active]"
id="statifyblacklist_active_referer"
value="1" <?php checked( StatifyBlacklist::$_options['referer']['active'], 1 ); ?> />
<?php esc_html_e( 'Activate live fiter', 'statify-blacklist' ); ?>
</label>
</li>
<li>
<label for="statify-blacklist_cron_referer">
<input type="checkbox" name="statifyblacklist[referer][cron]" id="statifyblacklist_cron_referer"
value="1" <?php checked( StatifyBlacklist::$_options['referer']['cron'], 1 ); ?> />
<?php esc_html_e( 'CronJob execution', 'statify-blacklist' ); ?>
<small>(<?php esc_html_e( 'Clean database periodically in background', 'statify-blacklist' ); ?>
)
</small>
</label>
</li>
<li>
<label for="statify-blacklist_referer_regexp">
<?php esc_html_e( 'Use regular expressions', 'statify-blacklist' ); ?>:
<br />
<select name="statifyblacklist[referer][regexp]" id="statifyblacklist_referer_regexp">
<option value="0" <?php selected( StatifyBlacklist::$_options['referer']['regexp'], 0 ); ?>>
<?php esc_html_e( 'Disabled', 'statify-blacklist' ); ?>
</option>
<option value="1" <?php selected( StatifyBlacklist::$_options['referer']['regexp'], 1 ); ?>>
<?php esc_html_e( 'Case-sensitive', 'statify-blacklist' ); ?>
</option>
<option value="2" <?php selected( StatifyBlacklist::$_options['referer']['regexp'], 2 ); ?>>
<?php esc_html_e( 'Case-insensitive', 'statify-blacklist' ); ?>
</option>
</select>
<small>
(<?php esc_html_e( 'Performance slower than standard filter. Recommended for cron or manual execition only.', 'statify-blacklist' ); ?>
)
</small>
</label>
</li>
<li>
<label for="statify-blacklist_referer">
<?php esc_html_e( 'Referer blacklist', 'statify-blacklist' ); ?>:<br />
<textarea cols="40" rows="5" name="statifyblacklist[referer][blacklist]" id="statify-blacklist_referer"><?php
if ( isset( $statifyblacklist_update_result['referer'] ) ) {
print esc_html( implode( "\r\n", array_keys( $statifyblacklist_update_result['referer'] ) ) );
} else {
print esc_html( implode( "\r\n", array_keys( StatifyBlacklist::$_options['referer']['blacklist'] ) ) );
}
?></textarea>
<br />
<small>
(<?php esc_html_e( 'Add one domain (without subdomains) each line, e.g. example.com', 'statify-blacklist' ); ?>
)
</small>
</label>
</li>
</ul>
</fieldset>
<fieldset>
<h2><?php esc_html_e( 'Target blacklist', 'statify-blacklist' ); ?></h2>
<ul style="list-style: none;">
<li>
<label for="statify-blacklist_active_target">
<input type="checkbox" name="statifyblacklist[target][active]"
id="statifyblacklist_active_target"
value="1" <?php checked( StatifyBlacklist::$_options['target']['active'], 1 ); ?> />
<?php esc_html_e( 'Activate live fiter', 'statify-blacklist' ); ?>
</label>
</li>
<li>
<label for="statify-blacklist_cron_target">
<input type="checkbox" name="statifyblacklist[target][cron]" id="statifyblacklist_cron_target"
value="1" <?php checked( StatifyBlacklist::$_options['target']['cron'], 1 ); ?> />
<?php esc_html_e( 'CronJob execution', 'statify-blacklist' ); ?>
<small>(<?php esc_html_e( 'Clean database periodically in background', 'statify-blacklist' ); ?>
)
</small>
</label>
</li>
<li>
<label for="statify-blacklist_target_regexp">
<?php esc_html_e( 'Use regular expressions', 'statify-blacklist' ); ?>:
<br />
<select name="statifyblacklist[target][regexp]" id="statifyblacklist_target_regexp">
<option value="0" <?php selected( StatifyBlacklist::$_options['target']['regexp'], 0 ); ?>>
<?php esc_html_e( 'Disabled', 'statify-blacklist' ); ?>
</option>
<option value="1" <?php selected( StatifyBlacklist::$_options['target']['regexp'], 1 ); ?>>
<?php esc_html_e( 'Case-sensitive', 'statify-blacklist' ); ?>
</option>
<option value="2" <?php selected( StatifyBlacklist::$_options['target']['regexp'], 2 ); ?>>
<?php esc_html_e( 'Case-insensitive', 'statify-blacklist' ); ?>
</option>
</select>
<small>
(<?php esc_html_e( 'Performance slower than standard filter. Recommended for cron or manual execition only.', 'statify-blacklist' ); ?>
)
</small>
</label>
</li>
<li>
<label for="statify-blacklist_target">
<?php esc_html_e( 'Target blacklist', 'statify-blacklist' ); ?>:<br />
<textarea cols="40" rows="5" name="statifyblacklist[target][blacklist]" id="statify-blacklist_target"><?php
if ( isset( $statifyblacklist_update_result['target'] ) ) {
print esc_html( implode( "\r\n", array_keys( $statifyblacklist_update_result['target'] ) ) );
} else {
print esc_html( implode( "\r\n", array_keys( StatifyBlacklist::$_options['target']['blacklist'] ) ) );
}
?></textarea>
<br />
<small>
(<?php esc_html_e( 'Add one target URL each line, e.g.', 'statify-blacklist' );
print ' /, /test/page/, /?page_id=123' ?>
)
</small>
</label>
</li>
</ul>
</fieldset>
<fieldset>
<h2><?php esc_html_e( 'IP blacklist', 'statify-blacklist' ); ?></h2>
<ul style="list-style: none;">
<li>
<label for="statify-blacklist_active_ip">
<input type="checkbox" name="statifyblacklist[ip][active]" id="statifyblacklist_active_ip"
value="1" <?php checked( StatifyBlacklist::$_options['ip']['active'], 1 ); ?> />
<?php esc_html_e( 'Activate live fiter', 'statify-blacklist' ); ?>
</label>
</li>
<li>
<small>
(<?php esc_html_e( 'Cron execution is not possible for IP filter, because IP addresses are not stored.', 'statify-blacklist' ); ?>
)
</small>
</li>
<li>
<label for="statify-blacklist_ip">
<?php esc_html_e( 'IP blacklist', 'statify-blacklist' ); ?>:<br />
<textarea cols="40" rows="5" name="statifyblacklist[ip][blacklist]" id="statify-blacklist_ip"><?php
if ( isset( $statifyblacklist_update_result['ip'] ) ) {
print esc_html( $_POST['statifyblacklist']['ip']['blacklist'] );
} else {
print esc_html( implode( "\r\n", StatifyBlacklist::$_options['ip']['blacklist'] ) );
}
?></textarea>
<br />
<small>
(<?php esc_html_e( 'Add one IP address or range per line, e.g.', 'statify-blacklist' ) ?>
127.0.0.1, 192.168.123.0/24, 2001:db8:a0b:12f0::1/64
)
</small>
</label>
</li>
</ul>
</fieldset>
<?php wp_nonce_field( 'statify-blacklist-settings' ); ?>
<h2><?php esc_html_e( 'Referer blacklist', 'statify-blacklist' ); ?></h2>
<table class="form-table">
<tbody>
<tr>
<th scope="row">
<label for="statify-blacklist_active_referer">
<?php esc_html_e( 'Activate live filter', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<input type="checkbox" name="statifyblacklist[referer][active]"
id="statify-blacklist_active_referer"
value="1" <?php checked( StatifyBlacklist::$options['referer']['active'], 1 ); ?>>
<p class="description">
<?php esc_html_e( 'Filter at time of tracking, before anything is stored', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_cron_referer">
<?php esc_html_e( 'CronJob execution', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<input type="checkbox" name="statifyblacklist[referer][cron]" id="statify-blacklist_cron_referer"
value="1" <?php checked( StatifyBlacklist::$options['referer']['cron'], 1 ); ?>>
<p class="description"><?php esc_html_e( 'Periodically clean up database in background', 'statify-blacklist' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_referer_regexp"><?php esc_html_e( 'Matching method', 'statify-blacklist' ); ?></label>
</th>
<td>
<select name="statifyblacklist[referer][regexp]" id="statify-blacklist_referer_regexp">
<option value="<?php print esc_attr( StatifyBlacklist::MODE_NORMAL ); ?>" <?php selected( StatifyBlacklist::$options['referer']['regexp'], StatifyBlacklist::MODE_NORMAL ); ?>>
<?php esc_html_e( 'Domain', 'statify-blacklist' ); ?>
</option>
<option value="<?php print esc_attr( StatifyBlacklist::MODE_KEYWORD ); ?>" <?php selected( StatifyBlacklist::$options['referer']['regexp'], StatifyBlacklist::MODE_KEYWORD ); ?>>
<?php esc_html_e( 'Keyword', 'statify-blacklist' ); ?>
</option>
<option value="<?php print esc_attr( StatifyBlacklist::MODE_REGEX ); ?>" <?php selected( StatifyBlacklist::$options['referer']['regexp'], StatifyBlacklist::MODE_REGEX ); ?>>
<?php esc_html_e( 'RegEx case-sensitive', 'statify-blacklist' ); ?>
</option>
<option value="<?php print esc_attr( StatifyBlacklist::MODE_REGEX_CI ); ?>" <?php selected( StatifyBlacklist::$options['referer']['regexp'], StatifyBlacklist::MODE_REGEX_CI ); ?>>
<?php esc_html_e( 'RegEx case-insensitive', 'statify-blacklist' ); ?>
</option>
</select>
<p class="description">
<?php esc_html_e( 'Domain', 'statify-blacklist' ); ?> - <?php esc_html_e( 'Match given domain including subdomains', 'statify-blacklist' ); ?>
<br>
<?php esc_html_e( 'Keyword', 'statify-blacklist' ); ?> - <?php esc_html_e( 'Match every referer that contains one of the keywords', 'statify-blacklist' ); ?>
<br>
<?php esc_html_e( 'RegEx', 'statify-blacklist' ); ?> - <?php esc_html_e( 'Match referer by regular expression', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_referer"><?php esc_html_e( 'Referer blacklist', 'statify-blacklist' ); ?></label>
</th>
<td>
<textarea cols="40" rows="5" name="statifyblacklist[referer][blacklist]" id="statify-blacklist_referer"><?php
if ( empty( $statifyblacklist_update_result['referer'] ) ) {
print esc_html( implode( "\r\n", array_keys( StatifyBlacklist::$options['referer']['blacklist'] ) ) );
} else {
print esc_html( implode( "\r\n", array_keys( $statifyblacklist_update_result['referer']['sanitized'] ) ) );
}
?></textarea>
<p class="description">
<?php esc_html_e( 'Add one domain (without subdomains) each line, e.g. example.com', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
</tbody>
</table>
<h2><?php esc_html_e( 'Target blacklist', 'statify-blacklist' ); ?></h2>
<table class="form-table">
<tbody>
<tr>
<th scope="row">
<label for="statify-blacklist_active_target">
<?php esc_html_e( 'Activate live filter', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<input type="checkbox" name="statifyblacklist[target][active]"
id="statify-blacklist_active_target"
value="1" <?php checked( StatifyBlacklist::$options['target']['active'], 1 ); ?>>
<p class="description">
<?php esc_html_e( 'Filter at time of tracking, before anything is stored', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_cron_target">
<?php esc_html_e( 'CronJob execution', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<input type="checkbox" name="statifyblacklist[target][cron]" id="statify-blacklist_cron_target"
value="1" <?php checked( StatifyBlacklist::$options['target']['cron'], 1 ); ?>>
<p class="description">
<?php esc_html_e( 'Clean database periodically in background', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_target_regexp">
<?php esc_html_e( 'Matching method', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<select name="statifyblacklist[target][regexp]" id="statify-blacklist_referer_regexp">
<option value="<?php print esc_attr( StatifyBlacklist::MODE_NORMAL ); ?>" <?php selected( StatifyBlacklist::$options['target']['regexp'], StatifyBlacklist::MODE_NORMAL ); ?>>
<?php esc_html_e( 'Exact', 'statify-blacklist' ); ?>
</option>
<option value="<?php print esc_attr( StatifyBlacklist::MODE_REGEX ); ?>" <?php selected( StatifyBlacklist::$options['target']['regexp'], StatifyBlacklist::MODE_REGEX ); ?>>
<?php esc_html_e( 'RegEx case-sensitive', 'statify-blacklist' ); ?>
</option>
<option value="<?php print esc_attr( StatifyBlacklist::MODE_REGEX_CI ); ?>" <?php selected( StatifyBlacklist::$options['target']['regexp'], StatifyBlacklist::MODE_REGEX_CI ); ?>>
<?php esc_html_e( 'RegEx case-insensitive', 'statify-blacklist' ); ?>
</option>
</select>
<p class="description">
<?php esc_html_e( 'Exact', 'statify-blacklist' ); ?> - <?php esc_html_e( 'Match only given targets', 'statify-blacklist' ); ?>
<br>
<?php esc_html_e( 'RegEx', 'statify-blacklist' ); ?> - <?php esc_html_e( 'Match target by regular expression', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_target">
<?php esc_html_e( 'Target blacklist', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<textarea cols="40" rows="5" name="statifyblacklist[target][blacklist]" id="statify-blacklist_target"><?php
if ( empty( $statifyblacklist_update_result['target'] ) ) {
print esc_html( implode( "\r\n", array_keys( StatifyBlacklist::$options['target']['blacklist'] ) ) );
} else {
print esc_html( implode( "\r\n", array_keys( $statifyblacklist_update_result['target']['sanitized'] ) ) );
}
?></textarea>
<p class="description">
(<?php esc_html_e( 'Add one target URL each line, e.g.', 'statify-blacklist' ); ?> /, /test/page/, /?page_id=123)
</p>
</td>
</tr>
</tbody>
</table>
<h2><?php esc_html_e( 'IP blacklist', 'statify-blacklist' ); ?></h2>
<table class="form-table">
<tbody>
<tr>
<th scope="row">
<label for="statify-blacklist_active_ip">
<?php esc_html_e( 'Activate live filter', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<input type="checkbox" name="statifyblacklist[ip][active]" id="statify-blacklist_active_ip"
value="1" <?php checked( StatifyBlacklist::$options['ip']['active'], 1 ); ?>>
<p class="description">
<?php esc_html_e( 'Filter at time of tracking, before anything is stored', 'statify-blacklist' ); ?>
<br>
<?php esc_html_e( 'Cron execution is not possible for IP filter, because IP addresses are not stored.', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_ip"><?php esc_html_e( 'IP blacklist', 'statify-blacklist' ); ?></label>:
</th>
<td>
<textarea cols="40" rows="5" name="statifyblacklist[ip][blacklist]" id="statify-blacklist_ip"><?php
if ( empty( $statifyblacklist_update_result['ip'] ) ) {
print esc_html( implode( "\r\n", StatifyBlacklist::$options['ip']['blacklist'] ) );
} else {
print esc_html( implode( "\r\n", $statifyblacklist_update_result['ip']['sanitized'] ) );
}
?></textarea>
<p class="description">
<?php esc_html_e( 'Add one IP address or range per line, e.g.', 'statify-blacklist' ); ?>
127.0.0.1, 192.168.123.0/24, 2001:db8:a0b:12f0::1/64
</p>
</td>
</tr>
</tbody>
</table>
<p class="submit">
<input class="button-primary" type="submit" name="submit" value="<?php esc_html_e( 'Save Changes' ) ?>">
<hr />
<input class="button-secondary" type="submit" name="cleanUp"
value="<?php esc_html_e( 'CleanUp Database', 'statify-blacklist' ) ?>"
onclick="return confirm('Do you really want to apply filters to database? This cannot be undone.');">
<br />
<small><?php esc_html_e( 'Applies referer and target filter (even if disabled) to data stored in database.', 'statify-blacklist' ); ?>
<em><?php esc_html_e( 'This cannot be undone!', 'statify-blacklist' ); ?></em></small>
<input class="button-primary" type="submit" name="submit" value="<?php esc_html_e( 'Save Changes' ); ?>">
<hr>
<input class="button-secondary" type="submit" name="cleanUp"
value="<?php esc_html_e( 'CleanUp Database', 'statify-blacklist' ); ?>"
onclick="return confirm('Do you really want to apply filters to database? This cannot be undone.');">
<br>
<p class="description">
<?php esc_html_e( 'Applies referer and target filter (even if disabled) to data stored in database.', 'statify-blacklist' ); ?>
<em><?php esc_html_e( 'This cannot be undone!', 'statify-blacklist' ); ?></em>
</p>
</p>
</form>
</div>