85 Commits

Author SHA1 Message Date
2bf0bcb0fd bump stable tag to 1.6.1 2021-05-28 11:55:21 +02:00
eb49dbc7db fix output for user agent filter on settings page 2021-05-28 11:49:31 +02:00
be173c6428 prepare release 1.6.1 2021-05-28 11:43:37 +02:00
6ffa650254 fix storage of user agent filter list (#28)
The user agent filer list is not flipped with the actual values as keys
like the lists for referrer and target. Hence the numeric keys are
compared against the actual user agent. We now flip the values in the
upgrade hook.
2021-05-28 11:11:30 +02:00
6fdaa8bd5a split unit test into separate test classes 2021-05-25 12:35:57 +02:00
e64122a5c6 skip sonar analysis for pull requests 2021-05-24 19:54:50 +02:00
5981971ddb enable CI for pull requests 2021-05-24 19:18:54 +02:00
eee20e4d05 remove RoboFile
The build process is trivial and does not require an additional task
runner and deployments are done by the GH action, so we can remove the
Robo dependency and clean up the build environment.
2021-05-22 19:30:04 +02:00
0a3102ee38 use GitHub actions for CI and automate Sonarcloud analysis 2021-05-17 20:47:21 +02:00
5f3fd8b554 minor dev-dependency updates 2021-05-16 10:11:12 +02:00
2f4428c0e4 use "stable" branch instead of "master" 2021-05-16 10:11:12 +02:00
e794758d77 declare compatibility with WordPress 5.7 2021-03-14 11:16:40 +01:00
91b612425b introduce GitHub actions for automated plugin/asset deployment 2021-03-14 11:16:17 +01:00
a5e4225261 prepare release of v1.6.0 2020-12-09 12:44:14 +01:00
1a621b8274 add PHP 8.0 to CI roster 2020-12-09 12:13:51 +01:00
f424909515 reduce redundancies for recurring matching loops
Referer, target and user agent filter share most of their logic
for different matching methods. We introduce a common routine for
all of them to not repeat ourselves. Passing a value extractor by
reference allows lazy evaluation (filter might be disabled) and
overrides for different cases (i.e. domain extractor for exact match).
2020-10-26 19:23:32 +01:00
a6cc821089 implement user agent filter
A new live-only filter block for user agent strings is now available.
It features the known exact, keyword and regular expression modes.
2020-10-26 19:23:32 +01:00
e5c30c2183 rename test class files to comply with PHPUnit 8.5+ conventions 2020-10-19 16:19:23 +02:00
2f8939b363 ui: correct label association for target matching method 2020-10-19 12:01:23 +02:00
06a7b1677a rename plugin to "Statify Filter"
The plugins purpose is to exclude or filter certain requests from
tracking by Statify. However the current name "Statify Blacklist" is not
actually inline with today's understanding of such terms. In WordPress
Core 5.5 the word "blacklist" among others has been replaced by more
precise wording where possible.

The term "filter" has been used in various places already and clearly
describes the behavior of this plugin. So we rename the plugin to
"Statify Filter" and rephrase front-end texts.

Plugin slug (permalink), textdomain and all public class and constant
names left untouched for now, to not introduce breaking changes at this
point. To be cleaned up with next major release.
2020-10-19 10:34:09 +02:00
af2d2c5142 docs: update Travis CI badge 2020-09-09 08:58:08 +02:00
c30f07e02f prepare release of 1.5.2 and declare WP 5.5 compatibility 2020-09-03 20:49:01 +02:00
cb61210685 minor (dev-)dependency updates 2020-09-03 20:49:01 +02:00
e4fd34d036 do not rely on WP core translation and add textdomain for all texts 2020-09-03 20:49:01 +02:00
2dd8e60bd8 prepare release 1.5.1 2020-05-20 10:33:05 +02:00
902e211552 postpone jump out on AJAX calls for Statify 1.7 compatibility (#22)
Statify 1.7 changed the JS tacking endpoint to WP AJAX, so we must not
skip the complete initialization on AJAX calls anymore. The routine now
breaks after adding the filter (if necessary).
2020-05-20 10:26:57 +02:00
9caa7b0678 Merge branch 'release/1.5.0' 2020-05-13 19:08:14 +02:00
9e70acbace Bump version to 1.5.0 and prepare relaase 2020-05-13 18:41:34 +02:00
d3eda0fc47 remove keyword description from referer blacklist settings section
This mode is not actually available, so is must not be documented.
Plus minor rewording of copy&paste mistakes.
2020-05-13 18:34:14 +02:00
a3741d939e README++
* minor corrections
* update Statify versions
* add FAQ section for IP subnets
* CHANGELOG++
2020-05-13 18:23:12 +02:00
aa9980106e update phpcs ignore statements; PHPdoc indentation 2020-05-10 12:13:39 +02:00
31c04d6b92 sanitize referer URI 2020-05-10 12:00:41 +02:00
d343dda6ff update settings page screenshot 2020-05-10 11:46:25 +02:00
4a6cc49fce fix use of $options variable in settings view
Underscore prefix has been removed from this field, but the change
did not reach the settings page yet.
2020-05-10 11:37:17 +02:00
ac73b2316d typo fixes 2020-05-10 11:35:18 +02:00
7f0523a647 declare compatibility with WP 5.4
skip ci
2020-04-09 10:51:44 +02:00
736cec1d12 fix use of esc_html__() for translated escape in settings page 2020-01-07 19:30:41 +01:00
253d2fadd2 use only supported PHP versions + 5.6 for CI builds 2020-01-07 19:30:40 +01:00
ebc44c722e use long array syntax
For some reason short syntax is discouraged in the latest WPCS ruleset.
To stay in line with WPCS we use long syntax now.
2020-01-07 19:30:38 +01:00
4c9d96e5a0 Merge branch 'master' into develop 2020-01-07 19:30:14 +01:00
c84eae50dd declare compatibility with WP 5.3
[skip ci]
2019-11-13 17:12:23 +01:00
3f5990f1f3 update PHPCS ruleset and re-enable warnings 2019-10-06 18:07:42 +02:00
84cf79fd04 remove underscore prefix from options field 2019-10-06 17:52:08 +02:00
a88a89c442 update WP Codex and license links [skip ci] 2019-10-06 17:34:32 +02:00
82667dcf93 update Composer dependencies 2019-08-18 18:48:46 +02:00
0b07697db8 check if POST values are actually set before sanitization 2019-08-18 18:21:57 +02:00
353ca4792f Declare compatibility with WordPress 5.2 [skip ci] 2019-05-30 15:34:18 +02:00
22373d2308 CONTRIBUTING++ [skip ci]
Adapt branch names to git flow and add a sentence on pull requests.
2019-03-17 17:43:29 +01:00
44ee7ee839 Check regular expressions and prevent saving invalid settings (#13) 2019-03-17 17:34:57 +01:00
1c69ba31bb Preprocessing of regular expression in separate funciton 2019-03-17 17:34:47 +01:00
39dcce3eeb Harmonize helper funcitons to snake_case 2019-03-17 17:25:43 +01:00
84ce89b127 Add .gitattributes
Set development files, tests and assets to export ignore list to clean
up the package distributed via Composer/Packagist.
2019-03-17 16:46:59 +01:00
124b4ecb75 dependency corrections for PHP 5.5
composer-copy-file 0.3 requires PHP 5.6, so reverted back to ~0.2
2019-03-12 19:39:43 +01:00
b7c3b51873 implement keyword filter for referer blacklist (closes #15)
In addition to the pre-existing normal and regular expression filters a
keyword mode is added. This filter matches if the referer string
contains a given keyword (case insensitive).
2019-03-12 19:37:48 +01:00
0822537f0e adjustments for PHPUnit 8
Added result cache to .gitignore and replaced assertInternalType() by
assertIsArray() with backwords compatibility for PHP 5 builds.
2019-03-12 17:57:26 +01:00
2eb08ce673 update dev dependencies 2019-03-12 17:32:32 +01:00
b691f2c618 adjust sanitization of settings and warning messages 2019-03-06 17:23:06 +01:00
c511dcb517 rework settings page
The settings page now features the 2 column layout like generated by  WP
settings API. The regular expression selects are slightly rewritten in
preparation of additional mathing methods.
2019-03-06 15:06:47 +01:00
1c146a9d72 Merge branch 'master' into develop 2019-03-06 14:18:53 +01:00
f14ceac400 Bump WP compat to 5.1 [skip CI] 2019-03-02 17:08:20 +01:00
4ce3a8f336 Add Slack notification to Travis 2018-11-02 16:05:15 +01:00
abcaab7a33 Fixed ReadMe badge links [skip ci] 2018-11-01 21:58:06 +01:00
36a65482e2 Add badges to ReadMe [skip ci] 2018-11-01 20:31:58 +01:00
f60c6ec2ff Fixed textdomain for compatibility notice 2018-10-30 19:32:52 +01:00
7e51c7d63e Add PHP 7.3 to CI matrix 2018-10-27 19:09:34 +02:00
35b6d5592b Cleaned up labels and added PHPCS rule for settings view 2018-10-27 19:05:09 +02:00
74f2e0f9a7 Removed deprecated instance() and __construct() methods from base class 2018-10-27 18:42:46 +02:00
74826384a8 Moved admin initialization to admin class 2018-10-27 18:34:23 +02:00
8e6cb5c553 Added compatibility check for WP and PHP version (closes #17) 2018-10-27 18:33:00 +02:00
59ff52cdef Merge if-clauses for cron job detection 2018-10-27 17:44:13 +02:00
4dcd52e137 Merge branch 'master' into develop 2018-10-27 17:38:41 +02:00
14ad793baf Bump WP compat to 5.0 2018-10-20 10:15:08 +02:00
bcd42bde2a Minor code style fixes 2018-10-20 10:12:38 +02:00
f34b761942 Removed load_plugin_textdomain and domain path header
Translation is handled via translate.wordpress.org and minimum required
version is greater than 4.6, so the local translation artifacts are
dropped.
2018-05-19 15:09:57 +02:00
075441d6f3 Bump version to 1.4.4 2018-05-19 14:36:23 +02:00
74be5a2334 Merge branch 'hf12-regexSkip' into develop 2018-05-19 14:31:47 +02:00
92f8496926 Fix #12: do not skip filter chain on non-matching regex filter
Corrected the regular expression methods and unit-tested combined filters.
2018-05-19 14:22:15 +02:00
c77e1ee012 Apply coding standards to unit test (except some mocks) 2018-05-19 13:53:27 +02:00
5e2dd4b6e1 Removed Gulp build script
Build process is now handled by Composer and Robo, so clean up old stuff
2018-03-21 18:27:42 +01:00
8c556ca509 Removed legacy code for WP prior to 4.6
Statify itself requires WP 4.7, so no reason to stick to legacy code
anymore.
2018-03-21 18:16:25 +01:00
66ddada63e Updated deprecated SPDX license code and removed PHP from require-dev
[ci skip]
2018-03-08 19:45:51 +01:00
fcba292698 Fixed README deploy path in Robo script [ci skip] 2018-03-08 17:21:18 +01:00
1e811957f8 Minor correction in required Statify version
'Tested up to' no longer holds with 1.6.0 now released, so now the
minimum version required is named instead of updating the maximum.
2018-03-08 17:15:50 +01:00
b9017a7b8d Update License file
Hopefully GitHub now recognizes the GPL license again...
2018-02-04 12:00:29 +01:00
7329574d09 Contributing ++ 2018-01-20 12:38:20 +01:00
27 changed files with 2009 additions and 1494 deletions

15
.distignore Normal file
View File

@ -0,0 +1,15 @@
/.git
/.github
/assets
/test
/.distignore
/.gitattributes
/.gitignore
/.travis.yml
/composer.json
/composer.lock
/CONTRIBUTING.md
/package.json
/package-lock.json
/phpcs.xml
/phpunit.xml

13
.gitattributes vendored Normal file
View File

@ -0,0 +1,13 @@
/.github export-ignore
/assets export-ignore
/test export-ignore
.distignore 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

54
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,54 @@
name: CI
on: [ push, pull_request ]
jobs:
unit-test:
runs-on: ubuntu-latest
strategy:
matrix:
php: [ '5.6', '7.3', '7.4', '8.0' ]
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer
- name: Install
run: composer install --no-interaction
- name: Unit tests
run: |
composer test
sed -i "s#<file name=\"${GITHUB_WORKSPACE}#<file name=\"/github/workspace#g" tests-clover.xml
- name: Analyze with SonarCloud
if: matrix.php == '8.0' && github.event_name != 'pull_request'
uses: sonarsource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.organization=stklcode-github
-Dsonar.projectKey=stklcode:statify-blacklist
-Dsonar.sources=inc,views,statify-blacklist.php
-Dsonar.tests=test
-Dsonar.php.tests.reportPath=tests-junit.xml
-Dsonar.php.coverage.reportPaths=tests-clover.xml
-Dsonar.coverage.exclusions=test/**/*.php
quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
tools: composer
- name: Install
run: composer install --no-interaction
- name: Code style checks for PHP
run: composer test-cs

View File

@ -0,0 +1,20 @@
name: Plugin asset/readme update
on:
push:
branches:
- stable
jobs:
master:
name: Push to stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Clean README.md
run: tail -n +6 README.md > README.md.tmp && mv README.md.tmp README.md
- name: WordPress.org plugin asset/readme update
uses: 10up/action-wordpress-plugin-asset-update@stable
env:
ASSETS_DIR: assets
README_NAME: README.md
SVN_PASSWORD: ${{ secrets.WP_SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.WP_SVN_USERNAME }}

View File

@ -0,0 +1,20 @@
name: Deploy to WordPress.org
on:
push:
tags:
- "v*"
- "!v*-*"
jobs:
tag:
name: New tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Clean README.md
run: tail -n +6 README.md > README.md.tmp && mv README.md.tmp README.md
- name: WordPress Plugin Deploy
uses: 10up/action-wordpress-plugin-deploy@stable
env:
ASSETS_DIR: assets
SVN_PASSWORD: ${{ secrets.WP_SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.WP_SVN_USERNAME }}

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 +0,0 @@
language: php
dist: trusty
php:
- '5.5'
- '5.6'
- '7.0'
- '7.1'
- '7.2'
before_script:
- composer install
script:
- composer test-all

View File

@ -2,7 +2,7 @@
As for all great Open Source projects, contributions in form of bug reports and code are welcome and important to keep the project alive.
In general, this project follows the [Git Flow](https://guides.github.com/introduction/flow/).
In general, this project follows the [GitHub Flow](https://guides.github.com/introduction/flow/).
Fork the project, commit your changes to your branch, open a pull request and it will probably be merged.
However, to ensure maintainability and quality of the code, there are some guidelines you might be more or less familiar with.
For that purpose, this document describes the important points.
@ -14,7 +14,7 @@ If you experience any issues with the plugin or the code, don't hesitate to file
### Bug Reports
Think you found a bug?
Please clearly state what happens and describe your environment.
Please clearly state what happens and describe your environment to help tracking down the issue.
* Which version of the plugin are you running?
* Which version of WordPress?
@ -31,19 +31,21 @@ No problem, please open an issue and describe what and why you think this change
If you want to contribute your code to solve an issue or implement a desired feature yourself, you might open a pull request.
If the changes introduce new functionality or affect major parts of existing code, please consider opening an issue for discussion first.
For adding new functionality a new test case the corresponding PHPUnit test would be nice (no hard criterion).
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 hold cutting edge developments, not necessarily considered stable or even compatible.
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
@ -62,7 +64,7 @@ If your patch fixes an issue, reference the ID in the first line.
If you feel like you have to _briefly_ explain your changes, do it (for long explanations and discussion, consider opening an issue or describe in the PR).
**Example commit:**
```
```text
Fix nasty bug from #1337
This example commit fixes the issue that some people write non-speaking commit messages like 'done magic'.
@ -75,7 +77,7 @@ You might sign your work, although that's no must.
Short answer: When it makes sense.
Bugfixes should be merges in time - assuming they pass the above criteria.
Bugfixes should be merged in time - assuming they pass the above criteria.
New features might be assigned to a certain milestone and as a result of this be scheduled according to the planned release cycle.
## Compatibility
@ -88,9 +90,17 @@ If you are unsure if your code matches these versions, the test will probably te
In case you think, your change is more important than maintaining backwards compatibility, please start a discussion to see,
if we might increase the minimum version or find a workaround for legacy systems.
## Build Environment
All you need to start off - besides your favorite IDE of course - is [Composer](https://getcomposer.org).
Running `composer install` will fetch all dependencies and build tools required.
You might have noticed that this project contains a [Robo](http://robo.li) build script.
Running the _build_-task (`./vendor/bin/robo build`) executes the full chain from cleanup over test to bundling the final product in the `dist` directory.
A complete list of the available tasks and options can be shown by running `robo list`.
## Unit Tests
The PHP code is tested by a PHPUnit test.
The PHP code is tested by a PHPUnit tests.
All test files are located in the `test` directory.
Files ending with `-test.php` will be automatically included into the test suite.
The coverage is not yet perfect, but be invited to write tests for methods not yet covered or newly introduced by your patch.
@ -102,7 +112,7 @@ Please make sure that you are at least roughly familiar with those guidelines.
The code style is automatically checked for commits (including pull requests) using [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer).
You can check your code against the configured ruleset yourself by running
`./vendor/bin/phpcs --standard=phpcs.xml your-edited-file.php` (assuming `composer install` has been executed).
`./vendor/bin/phpcs --standard=phpcs.xml your-edited-file.php` (assuming `composer install` has been executed) or the Robo task `robo test:cs` for a complete scan.
Please see these standards as guidelines.
If code style this is the only test that fails and your code's semantics are fine, don't hesitate to submit your pull request anyway.
@ -113,6 +123,9 @@ We probably find a solution for that.
Automated tests are run using [Travis CI](https://travis-ci.org/stklcode/statify-blacklist) for every commit including pull requests.
They ensure compatibility with the supported PHP versions and the WP Coding Standards.
There is also a semi-automated code quality analysis pushing results to [SonarCloud](https://sonarcloud.io/dashboard?id=de.stklcode.web.wordpress.plugins%3Astatify-blacklist).
Keep in mind that the ruleset is not yet perfect, so not every minor issue has to be fixed immediately.
## Still Open Questions?
If anything is still left unanswered and you're unsure if you got it right, don't hesitate to contact a team member.

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.
License](https://www.gnu.org/licenses/lgpl.html) instead of this
License.

111
README.md
View File

@ -1,33 +1,41 @@
# Statify Blacklist #
[![Build Status](https://github.com/stklcode/statify-blacklist/actions/workflows/test.yml/badge.svg?branch=stable)](https://github.com/stklcode/statify-blacklist/actions/workflows/test.yml?query=branch%3Astable)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=stklcode%3Astatify-blacklist&metric=alert_status)](https://sonarcloud.io/dashboard?id=stklcode%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/stable/LICENSE.md)
# Statify Filter #
* Contributors: Stefan Kalscheuer
* Requires at least: 4.4
* Tested up to: 4.9
* Requires at least: 4.7
* Tested up to: 5.7
* Requires PHP: 5.5
* Stable tag: 1.4.3
* Stable tag: 1.6.1
* 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.
A filter 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 customizable filters to Statify to allow blocking of referer spam or internal interactions.
### Features ##
#### Referer Blacklist ####
#### Referer Filter ####
Add a list of domains (for simplicity only second-level, e.g. _example.com_ which blocks _everything.example.com_).
#### Target Blacklist ####
#### Target Filter ####
Add a list of target pages (e.g. _/test/page/_, _/?page_id=123_) that will be excluded from tracking.
#### IP Blacklist ####
#### IP Filter ####
Add a list of IP addresses or subnets (e.g. _192.0.2.123_, _198.51.100.0/24_, _2001:db8:a0b:12f0::/64_).
#### User Agent Filter ####
Add a list of (partial) user agent strings to exclude (e.g. _curl_, _my/bot_, _Firefox_).
#### CleanUp Database ####
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.8
The plugin is capable of handling multisite installations.
### Support & Contributions ###
@ -37,51 +45,96 @@ 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
* Goto _Settings_ -> _Statify Blacklist_ to configure the plugin
* 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 Filter_ to configure the plugin
### Requirements ###
* PHP 5.5 or above
* WordPress 4.4 or above
* Statify plugin installed and activated (tested up to 1.5.4)
* 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 filters 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.
A default filter is not provided, as the plugin itself is totally neutral. If you want to filter out referer spam,
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.
Yes, it is. Just select regular expressions (case-sensitive or insensitive) as matching method instead of exact or keyword match.
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.
### Why is IP and User Agent filtering only available as live filter? ###
As you might know, _Statify_ does not store any personal information, including IP addresses in the database.
Because of this, these filters can only be applied while processing the request and not afterwards.
### 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.
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 filters using CIDR notation.
For example _198.51.100.0/24_ filters 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
1. Statify Filter settings page
## Upgrade Notice ##
### 1.6.1 ###
This is a bugfix release that corrects storage and evaluation of the user agent filter list.
### 1.6.0 ###
The plugin has been renamed from _Statify Blacklist_ to _Statify Filter_.
This does not imply any changes in functionality, rather than using a better wording.
In addition, there is a new filter by User Agent along with some minor corrections.
This version should be compatible with latest WordPress 5.6.
## Changelog ##
### 1.6.1 / 28.05.2021 ###
* Fix storage of user agent filter list (#28, props @BananaSquishee)
### 1.6.0 / 09.12.2020 ###
Plugin renamed to _Statify Filter_.
* Minor accessibility fixes on settings page
* Introduced new user agent filter (#20)
* Declared compatibility with WordPress 5.6
### 1.5.2 / 03.09.2020 ###
* Minor translation updates
* Declared compatibility with WordPress 5.5
### 1.5.1 / 20.05.2020 ###
* Fix initialization on AJAX calls for _Statify_ 1.7 compatibility (#22)
### 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)
### 1.4.3 / 09.01.2018 ###
* Fix issues with multisite installation (#11)
@ -112,7 +165,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

@ -1,388 +0,0 @@
<?php
/**
* Statify Blacklist Robo build script.
*
* This file contains the Robo tasks for building a distributable plugin package.
* Should not be included in final package.
*
* @author Stefan Kalscheuer <stefan@stklcode.de>
*
* @package Statify_Blacklist
* @version 1.4.3
*/
use Robo\Exception\TaskException;
use Robo\Tasks;
/**
* Class RoboFile
*/
class RoboFile extends Tasks {
const PROJECT_NAME = 'statify-blacklist';
const SVN_URL = 'https://plugins.svn.wordpress.org/statify-blacklist';
const OPT_TARGET = 'target';
const OPT_SKIPTEST = 'skipTests';
const OPT_SKIPSTYLE = 'skipStyle';
/**
* Version tag (read from composer.json).
*
* @var string
*/
private $version;
/**
* Target directory path.
*
* @var string
*/
private $target_dir;
/**
* Final package name.
*
* @var string
*/
private $final_name;
/**
* RoboFile constructor
*
* @param array $opts Options.
*
* @return void
*/
public function __construct( $opts = [ self::OPT_TARGET => 'dist' ] ) {
// Read composer configuration and extract version number..
$composer = json_decode( file_get_contents( __DIR__ . '/composer.json' ) );
// Extract parameter from options.
$this->version = $composer->version;
$this->target_dir = $opts[ self::OPT_TARGET ];
$this->final_name = self::PROJECT_NAME . '.' . $this->version;
}
/**
* Clean up target directory
*
* @param array $opts Options.
*
* @return void
*/
public function clean( $opts = [ self::OPT_TARGET => 'dist' ] ) {
$this->say( 'Cleaning target directory...' );
if ( is_dir( $this->target_dir . '/' . $this->final_name ) ) {
$this->_deleteDir( [ $this->target_dir . '/' . $this->final_name ] );
}
if ( is_file( $this->target_dir . '/' . $this->final_name . '.zip' ) ) {
$this->_remove( $this->target_dir . '/' . $this->final_name . '.zip' );
}
}
/**
* Run PHPUnit tests
*
* @return void
*/
public function test() {
$this->say( 'Executing PHPUnit tests...' );
$this->taskPhpUnit()->configFile( __DIR__ . '/phpunit.xml' )->run();
}
/**
* Run code style tests
*
* @return void
*/
public function testCS() {
$this->say( 'Executing PHPCS tests...' );
$this->_exec( __DIR__ . '/vendor/bin/phpcs --standard=phpcs.xml -s' );
}
/**
* Build a distributable bundle.
*
* @param array $opts Options.
*
* @return void
*/
public function build(
$opts = [
self::OPT_TARGET => 'dist',
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
]
) {
$this->clean( $opts );
if ( isset( $opts[ self::OPT_SKIPTEST ] ) && true === $opts[ self::OPT_SKIPTEST ] ) {
$this->say( 'Tests skipped' );
} else {
$this->test();
}
if ( isset( $opts[ self::OPT_SKIPSTYLE ] ) && true === $opts[ self::OPT_SKIPSTYLE ] ) {
$this->say( 'Style checks skipped' );
} else {
$this->testCS();
}
$this->bundle();
}
/**
* Bundle global resources.
*
* @return void
*/
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->_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' );
}
/**
* Create ZIP package from distribution bundle.
*
* @param array $opts Options.
*
* @return void
*/
public function package(
$opts = [
self::OPT_TARGET => 'dist',
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
]
) {
$this->build( $opts );
$this->say( 'Packaging...' );
$this->taskPack( $this->target_dir . '/' . $this->final_name . '.zip' )
->addDir( '', $this->target_dir . '/' . $this->final_name )
->run();
}
/**
* Deploy development version (trunk).
*
* @param array $opts Options.
*
* @return void
* @throws TaskException On errors.
*/
public function deployTrunk(
$opts = [
self::OPT_TARGET => 'dist',
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
]
) {
// First execute build job.
$this->build( $opts );
// Prepare VCS, either checkout or update local copy.
$this->prepareVCS();
$this->say( 'Preparing deployment directory...' );
$this->updateVCStrunk();
// Update remote repository.
$this->say( 'Deploying...' );
$this->commitVCS(
'--force trunk/*',
'Updated ' . self::PROJECT_NAME . ' trunk'
);
}
/**
* Deploy current version tag.
*
* @param array $opts Options.
*
* @return void
* @throws TaskException On errors.
*/
public function deployTag(
$opts = [
self::OPT_TARGET => 'dist',
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
]
) {
// First execute build job.
$this->build( $opts );
// Prepare VCS, either checkout or update local copy.
$this->prepareVCS();
$this->say( 'Preparing deployment directory...' );
$this->updateVCStag();
// Update remote repository.
$this->say( 'Deploying...' );
$this->commitVCS(
'tags/' . $this->version,
'Updated ' . self::PROJECT_NAME . ' v' . $this->version
);
}
/**
* Deploy current version tag.
*
* @param array $opts Options.
*
* @return void
* @throws TaskException On errors.
*/
public function deployReadme(
$opts = [
self::OPT_TARGET => 'dist',
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
]
) {
// First execute build job.
$this->build( $opts );
// Prepare VCS, either checkout or update local copy.
$this->prepareVCS();
$this->updateVCSreadme();
// Update remote repository.
$this->say( 'Deploying...' );
$this->commitVCS(
'--force trunk/README.md',
'Updated ' . self::PROJECT_NAME . ' ReadMe'
);
}
/**
* Deploy current version tag and trunk.
*
* @param array $opts Options.
*
* @return void
* @throws TaskException On errors.
*/
public function deployAll(
$opts = [
self::OPT_TARGET => 'dist',
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
]
) {
// First execute build job.
$this->build( $opts );
// Prepare VCS, either checkout or update local copy.
$this->prepareVCS();
$this->say( 'Preparing deployment directory...' );
$this->updateVCStrunk();
$this->updateVCStag();
// Update remote repository.
$this->say( 'Deploying...' );
$this->commitVCS(
[
'--force trunk/*',
'--force tags/' . $this->version,
],
'Updated ' . self::PROJECT_NAME . ' v' . $this->version
);
}
/**
* Prepare VCS direcory.
*
* Checkout or update local copy of SVN repository.
*
* @return void
* @throws TaskException On errors.
*/
private function prepareVCS() {
if ( is_dir( $this->target_dir . '/svn' ) ) {
$this->taskSvnStack()
->stopOnFail()
->dir( $this->target_dir . '/svn/statify-blacklist' )
->update()
->run();
} else {
$this->_mkdir( $this->target_dir . '/svn' );
$this->taskSvnStack()
->dir( $this->target_dir . '/svn' )
->checkout( self::SVN_URL )
->run();
}
}
/**
* Commit VCS changes
*
* @param string|array $to_add Files to add.
* @param string $msg Commit message.
*
* @return void
* @throws TaskException On errors.
*/
private function commitVCS( $to_add, $msg ) {
$task = $this->taskSvnStack()
->stopOnFail()
->dir( $this->target_dir . '/svn/statify-blacklist' );
if ( is_array( $to_add ) ) {
foreach ( $to_add as $ta ) {
$task = $task->add( $ta );
}
} else {
$task = $task->add( $to_add );
}
$task->commit( $msg )->run();
}
/**
* Update SVN readme file.
*
* @return void
*/
private function updateVCSreadme() {
$trunk_dir = $this->target_dir . '/svn/statify-blacklist/trunk';
$this->_copy( $this->target_dir . '/' . $this->final_name . 'README.md', $trunk_dir . 'README.md' );
}
/**
* Update SVN development version (trunk).
*
* @return void
*/
private function updateVCStrunk() {
// Clean trunk directory.
$trunk_dir = $this->target_dir . '/svn/statify-blacklist/trunk';
$this->taskCleanDir( $trunk_dir )->run();
// Copy built bundle to trunk.
$this->taskCopyDir( [ $this->target_dir . '/' . $this->final_name => $trunk_dir ] )->run();
}
/**
* Update current SVN version tag.
*
* @return void
*/
private function updateVCStag() {
// Clean tag directory if it exists.
$tag_dir = $this->target_dir . '/svn/statify-blacklist/tags/' . $this->version;
if ( is_dir( $tag_dir ) ) {
$this->taskCleanDir( $this->target_dir . '/svn/statify-blacklist/tags/' . $this->version )->run();
} else {
$this->_mkdir( $tag_dir );
}
// Copy built bundle to trunk.
$this->taskCopyDir( [ $this->target_dir . '/' . $this->final_name => $tag_dir ] )->run();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -1,14 +1,14 @@
{
"name": "stklcode/statify-blacklist",
"version": "1.4.3",
"description": "A blacklist extension for the famous Statify WordPress plugin",
"version": "1.6.1",
"description": "A filter extension for the famous Statify WordPress plugin",
"keywords": [
"wordpress",
"plugin",
"statistics",
"blacklist"
"filter"
],
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"authors": [
{
"name": "Stefan Kalscheuer",
@ -19,29 +19,18 @@
"type": "wordpress-plugin",
"require": {
"php": ">=5.5",
"composer/installers": "~1.0"
"composer/installers": "~1.11"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.4",
"php": ">=5.5",
"consolidation/robo": "^1.0.0",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7",
"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"
"slowprog/composer-copy-file": "~0.3",
"squizlabs/php_codesniffer": "^3.6",
"phpcompatibility/phpcompatibility-wp": "^2.1",
"wp-coding-standards/wpcs": "^2.3"
},
"scripts": {
"build": [
"robo build"
],
"package": [
"robo package"
],
"deploy": [
"robo deploy:all"
],
"test-all": [
"@test",
"@test-cs"

View File

@ -1,6 +1,6 @@
<?php
/**
* Statify Blacklist: StatifyBlacklist_Admin class
* Statify Filter: StatifyBlacklist_Admin class
*
* This file contains the derived class for the plugin's administration features.
*
@ -9,46 +9,112 @@
* @since 1.0.0
*/
// Quit.
defined( 'ABSPATH' ) || exit;
// Quit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Statify Blacklist admin configuration.
* Statify Filter admin configuration.
*
* @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.
@ -71,17 +137,27 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
* @since 1.0.0
*/
public static function add_menu_page() {
$title = __( 'Statify Blacklist', 'statify-blacklist' );
$title = __( 'Statify Filter', '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',
)
@ -133,7 +209,7 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
if ( STATIFYBLACKLIST_BASE === $file && current_user_can( 'manage_options' ) ) {
array_unshift(
$links,
sprintf( '<a href="%s">%s</a>', esc_attr( add_query_arg( 'page', 'statify-blacklist', $base ) ), __( 'Settings' ) )
sprintf( '<a href="%s">%s</a>', esc_attr( add_query_arg( 'page', 'statify-blacklist', $base ) ), __( 'Settings', 'statify-blacklist' ) )
);
}
@ -150,24 +226,24 @@ class StatifyBlacklist_Admin extends StatifyBlacklist {
public static function cleanup_database() {
// Check user permissions.
if ( ! current_user_can( 'manage_options' ) && ! ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
die( esc_html__( 'Are you sure you want to do this?' ) );
die( esc_html__( 'Are you sure you want to do this?', 'statify-blacklist' ) );
}
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 Filter: StatifyBlacklist_System class
*
* This file contains the derived class for the plugin's system operations.
*
@ -9,11 +9,13 @@
* @since 1.0.0
*/
// Quit.
defined( 'ABSPATH' ) || exit;
// Quit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Statify Blacklist system configuration.
* Statify Filter system configuration.
*
* @since 1.0.0
*/
@ -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,
@ -169,10 +178,34 @@ class StatifyBlacklist_System extends StatifyBlacklist {
self::update_options();
}
// Version older than 1.6.
if ( self::$options['version'] < 1.6 ) {
$options = self::$options;
if ( ! isset( $options['ua'] ) ) {
$options['ua'] = array(
'active' => 0,
'regexp' => 0,
'blacklist' => array(),
);
} elseif ( ! isset( $options['ua']['blacklist'] ) ) {
$options['ua']['blacklist'] = array();
} elseif ( isset( $options['ua'] ) ) {
// User agent strings got stored incorrectly in 1.6.0 - luckily the version was not updated, either.
$options['ua']['blacklist'] = array_flip( $options['ua']['blacklist'] );
}
$options['version'] = 1.6;
if ( self::$multisite ) {
update_site_option( 'statify-blacklist', $options );
} else {
update_option( 'statify-blacklist', $options );
}
self::update_options();
}
// 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

@ -1,6 +1,6 @@
<?php
/**
* Statify Blacklist: StatifyBlacklist class
* Statify Filter: StatifyBlacklist class
*
* This file contains the plugin's base class.
*
@ -8,13 +8,13 @@
* @since 1.0.0
*/
// Quit.
defined( 'ABSPATH' ) || exit;
// Quit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Statify Blacklist.
*
* @since 1.0.0
* Statify Filter.
*/
class StatifyBlacklist {
@ -24,15 +24,44 @@ class StatifyBlacklist {
* @since 1.4.0
* @var int VERSION_MAIN
*/
const VERSION_MAIN = 1.4;
const VERSION_MAIN = 1.6;
/**
* 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,34 +71,16 @@ 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.
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
// Skip on autosave.
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
return;
}
@ -80,41 +91,27 @@ class StatifyBlacklist {
self::update_options();
// Add Filter to statify hook if enabled.
if ( 0 !== self::$_options['referer']['active'] || 0 !== self::$_options['target']['active'] || 0 !== self::$_options['ip']['active'] ) {
if ( 0 !== self::$options['referer']['active'] ||
0 !== self::$options['target']['active'] ||
0 !== self::$options['ip']['active'] ||
0 !== self::$options['ua']['active'] ) {
add_filter( 'statify__skip_tracking', array( 'StatifyBlacklist', 'apply_blacklist_filter' ) );
}
// Statify uses WP AJAX as of 1.7, so we need to reach this point. But there are no further admin/cron actions.
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
// 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 +122,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 +131,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() );
}
/**
@ -160,85 +159,46 @@ class StatifyBlacklist {
'active' => 0,
'blacklist' => array(),
),
'ua' => array(
'active' => 0,
'regexp' => 0,
'blacklist' => array(),
),
'version' => self::VERSION_MAIN,
);
}
/**
* Apply the blacklist filter if active
* Apply the filter if active
*
* @since 1.0.0
*
* @return bool TRUE if referer matches blacklist.
* @return bool TRUE if referer matches filter.
*/
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';
}
// Check blacklist (return NULL to continue filtering).
return ( 1 === preg_match( $regexp, $referer ) ) ? true : null;
} else {
// Extract relevant domain parts.
$referer = wp_parse_url( wp_get_raw_referer() );
$referer = strtolower( ( isset( $referer['host'] ) ? $referer['host'] : '' ) );
// Get blacklist.
$blacklist = self::$_options['referer']['blacklist'];
// Check blacklist.
if ( isset( $blacklist[ $referer ] ) ) {
return true;
}
}
// Referer filter.
if (
self::apply_single_filter(
self::$options['referer'],
array(
__CLASS__,
( ! isset( self::$options['referer']['regexp'] ) || self::MODE_NORMAL === self::$options['referer']['regexp'] ) ? 'get_referer_domain' : 'get_referer',
)
)
) {
return true;
}
// Target blacklist (since 1.4.0).
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'] ) {
// Get full referer string.
// @codingStandardsIgnoreStart The globals are checked.
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/' );
// @codingStandardsIgnoreEnd
// Merge given regular expressions into one.
$regexp = '/' . implode( '|', array_keys( self::$_options['target']['blacklist'] ) ) . '/';
if ( 2 === self::$_options['target']['regexp'] ) {
$regexp .= 'i';
}
// Check blacklist (return NULL to continue filtering).
return ( 1 === preg_match( $regexp, $target ) ) ? true : null;
} else {
// Extract target page.
// @codingStandardsIgnoreStart The globals are checked.
$target = ( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/' );
// @codingStandardsIgnoreEnd
// Get blacklist.
$blacklist = self::$_options['target']['blacklist'];
// Check blacklist.
if ( isset( $blacklist[ $target ] ) ) {
return true;
}
}
// Target filter (since 1.4.0).
if ( self::apply_single_filter( self::$options['target'], array( __CLASS__, 'get_target' ) ) ) {
return true;
}
// IP blacklist (since 1.4.0).
if ( isset( self::$_options['ip']['active'] ) && 0 !== self::$_options['ip']['active'] ) {
// IP filter (since 1.4.0).
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;
}
@ -246,10 +206,145 @@ class StatifyBlacklist {
}
}
// Skip and continue (return NULL), if all blacklists are inactive.
// User agent filter (since 1.6).
if ( self::apply_single_filter( self::$options['ua'], array( __CLASS__, 'get_user_agent' ) ) ) {
return true;
}
// Skip and continue (return NULL), if all filters are inactive.
return null;
}
/**
* Apply a single filter, if active.
*
* @param array $config Configuration array from plugin options.
* @param callable $value_fn Extractor function for filterable value.
*
* @return bool TRUE if referer matches filter.
*
* @since 1.6 Extracted from "apply_blacklist_filter" to reduce redundancies.
*/
private static function apply_single_filter( $config, $value_fn ) {
// Is the filter active?
if ( ! isset( $config['active'] ) || 0 === $config['active'] ) {
return false;
}
// Extract the filterable value.
$value = call_user_func( $value_fn );
$mode = isset( $config['regexp'] ) ? intval( $config['regexp'] ) : self::MODE_NORMAL;
switch ( $mode ) {
case self::MODE_REGEX:
case self::MODE_REGEX_CI:
// Regular Expression filtering since 1.3.0.
// Merge given regular expressions into one.
$regexp = self::regex(
array_keys( $config['blacklist'] ),
self::MODE_REGEX_CI === $config['regexp']
);
// Check filter (no return to continue filtering #12).
if ( 1 === preg_match( $regexp, $value ) ) {
return true;
}
break;
case self::MODE_KEYWORD:
// Keyword filter since 1.5.0 (#15).
foreach ( array_keys( $config['blacklist'] ) as $keyword ) {
if ( false !== strpos( strtolower( $value ), strtolower( $keyword ) ) ) {
return true;
}
}
break;
default:
// Standard exact filter.
if ( isset( $config['blacklist'][ $value ] ) ) {
return true;
}
}
return false;
}
/**
* 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 referer.
*
* @return string The referer.
*/
private static function get_referer() {
$referer = wp_get_raw_referer();
if ( ! $referer ) {
$referer = '';
}
return $referer;
}
/**
* Helper method to determine the host part of the client's referer.
*
* @return string Referer domain.
*/
private static function get_referer_domain() {
$referer = wp_parse_url( self::get_referer() );
return strtolower( ( isset( $referer['host'] ) ? $referer['host'] : '' ) );
}
/**
* Helper method to determine the client's referer.
*
* @return string The referer.
*/
private static function get_target() {
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
$target = filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_URL );
if ( $target ) {
return $target;
}
}
return '';
}
/**
* Helper method to determine the client's IP address.
*
@ -273,20 +368,35 @@ 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;
}
/**
* Helper method to determine the user agent.
*
* @return string The user agent string.
*/
private static function get_user_agent() {
if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
$user_agent = filter_var( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), FILTER_SANITIZE_STRING );
if ( $user_agent ) {
return $user_agent;
}
}
return '';
}
/**
* Helper function to check if an IP address matches a given subnet.
*
@ -357,6 +467,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.3",
"description": "A blacklist extension for the famous Statify WordPress plugin",
"version": "1.6.1",
"description": "A filter 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,26 +2,29 @@
<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>
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array" value="statify-blacklist"/>
</properties>
</rule>
<!-- PHP compatibility level -->
<config name="testVersion" value="5.5-"/>
<rule ref="PHPCompatibility"/>
<rule ref="PHPCompatibilityWP"/>
</ruleset>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<phpunit bootstrap="./vendor/autoload.php">
<phpunit bootstrap="test/bootstrap.php">
<testsuites>
<testsuite name="Statify Blacklist TestSuite">
<directory suffix="-test.php">./test/</directory>
<directory suffix="_Test.php">./test/</directory>
</testsuite>
</testsuites>
<filter>
@ -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

@ -1,61 +1,68 @@
<?php
/**
* Statify Blacklist
* Statify Filter
*
* @package PluginPackage
* @author Stefan Kalscheuer <stefan@stklcode.de>
* @license GPL-2.0+
*
* @wordpress-plugin
* Plugin Name: Statify Blacklist
* Plugin Name: Statify Filter
* Plugin URI: https://wordpress.org/plugins/statify-blacklist/
* Description: Extension for the Statify plugin to add a customizable blacklists.
* Version: 1.4.3
* Description: Extension for the Statify plugin to add customizable filters. (formerly "Statify Blacklist)
* Version: 1.6.1
* 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
* Statify Filter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* Statify Blacklist is distributed in the hope that it will be useful,
* Statify Filter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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 Filter. 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 Filter 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

@ -0,0 +1,64 @@
<?php
/**
* Statify Filter: Unit Test
*
* This is a PHPunit test class for the plugin's functionality
*
* @package Statify_Blacklist
*/
/**
* Class StatifyBlacklist_Admin_Test.
*
* PHPUnit test class for StatifyBlacklist_Admin.
*/
class StatifyBlacklist_Admin_Test extends PHPUnit\Framework\TestCase {
/**
* Test sanitization of IP addresses.
*
* @return void
*/
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 = invoke_static( StatifyBlacklist_Admin::class, 'sanitize_ips', array( array_merge( $valid, $invalid ) ) );
$this->assertNotFalse( $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.
$valid = array(
'2001:db8:a0b:12f0::',
'2001:db8:a0b:12f0::1',
'2001:db8:a0b:12f0::1/128',
'2001:db8:a0b:12f0::/64',
);
$invalid = array(
'2001:db8:a0b:12f0::x',
'2001:db8:a0b:12f0:::',
'2001:fffff:a0b:12f0::1',
'2001:db8:a0b:12f0::/129',
'1:2:3:4:5:6:7:8:9',
);
$result = invoke_static( StatifyBlacklist_Admin::class, 'sanitize_ips', array( array_merge( $valid, $invalid ) ) );
$this->assertNotFalse( $result );
if ( method_exists( $this, 'assertIsArray' ) ) {
$this->assertIsArray( $result );
} else {
$this->assertInternalType( 'array', $result );
}
$this->assertEquals( $valid, $result );
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* Statify Filter: Unit Test
*
* This is a PHPunit test class for the plugin's functionality
*
* @package Statify_Blacklist
*/
/**
* Class StatifyBlacklist_System_Test.
*
* PHPUnit test class for StatifyBlacklist_System.
*/
class StatifyBlacklist_System_Test extends PHPUnit\Framework\TestCase {
/**
* Test the upgrade methodology for configuration options.
*
* @return void
*/
public function test_upgrade() {
// Create configuration of version 1.3.
$options13 = array(
'active_referer' => 1,
'cron_referer' => 0,
'referer' => array(
'example.net' => 0,
'example.com' => 1,
),
'referer_regexp' => 0,
'version' => 1.3,
);
// Set options in mock.
update_option( 'statify-blacklist', $options13 );
// Execute upgrade.
StatifyBlacklist_System::upgrade();
// Retrieve updated options.
$options_updated = get_option( 'statify-blacklist' );
// Verify size against default options (no junk left).
$this->assertEquals( 5, count( $options_updated ) );
$this->assertEquals( 4, count( $options_updated['referer'] ) );
$this->assertEquals( 4, count( $options_updated['target'] ) );
$this->assertEquals( 2, count( $options_updated['ip'] ) );
$this->assertEquals( 3, count( $options_updated['ua'] ) );
$this->assertEquals( 1.6, $options_updated['version'] );
// Verify that original attributes are unchanged.
$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, $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'] );
$this->assertEquals( 0, $options_updated['ua']['active'] );
$this->assertEquals( 0, $options_updated['ua']['regexp'] );
$this->assertEquals( array(), $options_updated['ua']['blacklist'] );
// Verify that version number has changed to current release.
$this->assertEquals( StatifyBlacklist::VERSION_MAIN, $options_updated['version'] );
// Test upgrade of incorrectly stored user agent list in 1.6.
$options_updated['version'] = 1.4;
$options_updated['ua']['blacklist'] = array( 'user agent 1', 'user agent 2' );
update_option( 'statify-blacklist', $options_updated );
// Execute upgrade.
StatifyBlacklist_System::upgrade();
// Retrieve updated options.
$options_updated = get_option( 'statify-blacklist' );
$this->assertEquals(
array(
'user agent 1' => 0,
'user agent 2' => 1,
),
$options_updated['ua']['blacklist']
);
$this->assertEquals( 1.6, $options_updated['version'] );
$this->assertEquals( StatifyBlacklist::VERSION_MAIN, $options_updated['version'] );
}
}

View File

@ -0,0 +1,601 @@
<?php
/**
* Statify Filter: Unit Test
*
* This is a PHPunit test class for the plugin's functionality
*
* @package Statify_Blacklist
* @since 1.3.0
*/
/**
* Class StatifyBlacklistTest.
*
* PHPUnit test class for StatifyBlacklist.
*
* @since 1.3.0
*/
class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
/**
* Test simple referer filter.
*
* @return void
*/
public function test_referer_filter() {
// Prepare Options: 2 filtered domains, disabled.
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(
'example.com' => 0,
'example.net' => 1,
),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(),
),
'ip' => array(
'active' => 0,
'blacklist' => array(),
),
'ua' => array(
'active' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// No referer.
unset( $_SERVER['HTTP_REFERER'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Non-filtered referer.
$_SERVER['HTTP_REFERER'] = 'http://example.org';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Filtered referer.
$_SERVER['HTTP_REFERER'] = 'http://example.com';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Filtered referer with path.
$_SERVER['HTTP_REFERER'] = 'http://example.net/foo/bar.html';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter and run tests again.
StatifyBlacklist::$options['referer']['active'] = 1;
unset( $_SERVER['HTTP_REFERER'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'http://example.org';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'http://example.com';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'http://example.net/foo/bar.html';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}
/**
* Test referer filter using regular expressions.
*
* @return void
*/
public function test_referer_regex_filter() {
// Prepare Options: 2 regular expressions.
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 1,
'cron' => 0,
'regexp' => 1,
'blacklist' => array(
'example.[a-z]+' => 0,
'test' => 1,
),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(),
),
'ip' => array(
'active' => 0,
'blacklist' => array(),
),
'ua' => array(
'active' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// No referer.
unset( $_SERVER['HTTP_REFERER'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Non-filtered referer.
$_SERVER['HTTP_REFERER'] = 'http://not.evil';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Filtered referer.
$_SERVER['HTTP_REFERER'] = 'http://example.com';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Filtered 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->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Set RegExp filter to case insensitive.
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(),
),
'ua' => array(
'active' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// No referer.
unset( $_SERVER['HTTP_REFERER'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Non-filtered referer.
$_SERVER['HTTP_REFERER'] = 'http://not.evil';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Filtered referer.
$_SERVER['HTTP_REFERER'] = 'http://example.com';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Filtered 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 CIDR address matching for IP filter (#7).
*
* @return void
*/
public function test_cidr_match() {
// IPv4 tests.
$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(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '127.0.0.1', '127.0.0.1/33' )
)
);
$this->assertFalse(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '127.0.0.1', '127.0.0.1/-1' )
)
);
$this->assertTrue(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.2.123', '192.0.2.0/24' )
)
);
$this->assertFalse(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.3.123', '192.0.2.0/24' )
)
);
$this->assertTrue(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.2.123', '192.0.2.120/29' )
)
);
$this->assertFalse(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '192.0.2.128', '192.0.2.120/29' )
)
);
$this->assertTrue( invoke_static( StatifyBlacklist::class, 'cidr_match', array( '10.11.12.13', '10.0.0.0/8' ) ) );
$this->assertFalse(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '10.11.12.345', '10.0.0.0/8' )
)
);
// IPv6 tests.
$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(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '2001:db8:a0b:12f0:1:2:3:4', '2001:db8:a0b:12f0::1/64 ' )
)
);
$this->assertTrue(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '2001:db8:a0b:12f0::123:456', '2001:db8:a0b:12f0::1/96 ' )
)
);
$this->assertFalse(
invoke_static(
StatifyBlacklist::class,
'cidr_match',
array( '2001:db8:a0b:12f0::1:132:465', '2001:db8:a0b:12f0::1/96 ' )
)
);
}
/**
* Test IP filter (#7).
*
* @return void
*/
public function test_ip_filter() {
// Prepare Options: 2 filtered IPs, disabled.
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'ip' => array(
'active' => 0,
'blacklist' => array(
'192.0.2.123',
'2001:db8:a0b:12f0::1',
),
),
'ua' => array(
'active' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// Set matching IP.
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter.
StatifyBlacklist::$options['ip']['active'] = 1;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Try matching v6 address.
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::1';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Non-matching addresses.
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::2';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Subnet matching.
StatifyBlacklist::$options['ip']['blacklist'] = array(
'192.0.2.0/25',
'2001:db8:a0b:12f0::/96',
);
$_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() );
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::5';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0:0:1111::1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Filter using proxy header.
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_X_FORWARDED_FOR'] = '192.0.2.123';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_X_REAL_IP'] = '2001:db8:a0b:12f0:0:1111::1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_X_REAL_IP'] = '2001:db8:a0b:12f0:0::1';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}
/**
* Test simple target filter.
*
* @return void
*/
public function test_target_filter() {
// Prepare Options: 2 filtered domains, disabled.
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(
'/excluded/page/' => 0,
'/?page_id=3' => 1,
),
),
'ip' => array(
'active' => 0,
'blacklist' => array(),
),
'ua' => array(
'active' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// Empty target.
unset( $_SERVER['REQUEST_URI'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Non-filtered 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() );
// Filtered 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['target']['active'] = 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() );
}
// TODO: Test target regex filter.
/**
* Test user agent filter (#20).
*
* @return void
*/
public function test_ua_filter() {
// Prepare Options: 2 filtered IPs, disabled.
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'ip' => array(
'active' => 0,
'blacklist' => array(),
),
'ua' => array(
'active' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(
'TestBot/1.23' => 0,
),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// Set matching user agent.
$_SERVER['HTTP_USER_AGENT'] = 'TestBot/1.23';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter.
StatifyBlacklist::$options['ua']['active'] = 1;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Non-matching addresses.
$_SERVER['HTTP_USER_AGENT'] = 'Another Browser 4.5.6 (Linux)';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_USER_AGENT'] = 'TestBot/2.34';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Keyword matching.
StatifyBlacklist::$options['ua']['blacklist'] = array( 'TestBot' => 0 );
StatifyBlacklist::$options['ua']['regexp'] = StatifyBlacklist::MODE_KEYWORD;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// RegEx.
StatifyBlacklist::$options['ua']['blacklist'] = array( 'T[a-z]+B[a-z]+' => 0 );
StatifyBlacklist::$options['ua']['regexp'] = StatifyBlacklist::MODE_REGEX;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
StatifyBlacklist::$options['ua']['blacklist'] = array( 't[a-z]+' => 0 );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
StatifyBlacklist::$options['ua']['regexp'] = StatifyBlacklist::MODE_REGEX_CI;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}
/**
* Test combined filters.
*
* @since 1.4.4
*
* @return void
*/
public function test_combined_filters() {
// Prepare Options: simple referer + simple target + ip.
StatifyBlacklist::$options = array(
'referer' => array(
'active' => 1,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(
'example.com' => 0,
),
),
'target' => array(
'active' => 1,
'cron' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(
'/excluded/page/' => 0,
),
),
'ip' => array(
'active' => 1,
'blacklist' => array(
'192.0.2.123',
),
),
'ua' => array(
'active' => 0,
'regexp' => StatifyBlacklist::MODE_NORMAL,
'blacklist' => array(),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// No match.
$_SERVER['HTTP_REFERER'] = 'https://example.net';
$_SERVER['REQUEST_URI'] = '/normal/page/';
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
unset( $_SERVER['HTTP_X_FORWARDED_FOR'] );
unset( $_SERVER['HTTP_X_REAL_IP'] );
// Matching Referer.
$_SERVER['HTTP_REFERER'] = 'https://example.com';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Matching target.
$_SERVER['HTTP_REFERER'] = 'https://example.net';
$_SERVER['REQUEST_URI'] = '/excluded/page/';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Matching IP.
$_SERVER['REQUEST_URI'] = '/normal/page/';
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
// Same for RegExp filters.
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/';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REQUEST_URI'] = '/normal/page/';
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
}
}

89
test/bootstrap.php Normal file
View File

@ -0,0 +1,89 @@
<?php
/**
* PHPUnit bootstrap file
*
* @package Statify_Blacklist
*/
/**
* Simulating the ABSPATH constant.
*
* @var boolean ABSPATH
*/
const ABSPATH = false;
/*
* Include class files.
*/
require_once __DIR__ . '/../inc/class-statifyblacklist.php';
require_once __DIR__ . '/../inc/class-statifyblacklist-admin.php';
require_once __DIR__ . '/../inc/class-statifyblacklist-system.php';
// Include Composer autoloader.
require_once __DIR__ . '/../vendor/autoload.php';
/** @ignore */
function invoke_static( $class, $method_name, $parameters = array() ) {
$reflection = new \ReflectionClass( $class );
$method = $reflection->getMethod( $method_name );
$method->setAccessible( true );
return $method->invokeArgs( null, $parameters );
}
// Some mocked WP functions.
$mock_options = array();
$mock_multisite = false;
/** @ignore */
function is_multisite() {
global $mock_multisite;
return $mock_multisite;
}
/** @ignore */
function wp_parse_args( $args, $defaults = '' ) {
if ( is_object( $args ) ) {
$r = get_object_vars( $args );
} elseif ( is_array( $args ) ) {
$r =& $args;
} else {
parse_str( $args, $r );
}
if ( is_array( $defaults ) ) {
return array_merge( $defaults, $r );
}
return $r;
}
/** @ignore */
function get_option( $option, $default = false ) {
global $mock_options;
return isset( $mock_options[ $option ] ) ? $mock_options[ $option ] : $default;
}
/** @ignore */
function update_option( $option, $value, $autoload = null ) {
global $mock_options;
$mock_options[ $option ] = $value;
}
/** @ignore */
function wp_get_raw_referer() {
return isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
}
function wp_parse_url( $value ) {
return parse_url( $value );
}
/** @ignore */
function wp_unslash( $value ) {
return is_string( $value ) ? stripslashes( $value ) : $value;
}

View File

@ -1,541 +0,0 @@
<?php
/**
* Statify Blacklist: Unit Test
*
* This is a PHPunit test class for the plugin's functionality
*
* @package Statify_Blacklist
* @subpackage Admin
* @since 1.3.0
*/
/**
* Simulating the ABSPATH constant.
*
* @since 1.3.0
* @var bool ABSPATH
*/
const ABSPATH = false;
/**
* The StatifyBlacklist base class.
*/
require_once( 'inc/class-statifyblacklist.php' );
/**
* The StatifyBlacklist system class.
*/
require_once( 'inc/class-statifyblacklist-system.php' );
/**
* The StatifyBlacklist admin class.
*/
require_once( 'inc/class-statifyblacklist-admin.php' );
/**
* Class StatifyBlacklistTest.
*
* PHPUnit test class for StatifyBlacklist.
*
* @since 1.3.0
*/
class StatifyBlacklist_Test extends PHPUnit\Framework\TestCase {
/**
* Test simple referer filter.
*/
public function test_referer_filter() {
// Prepare Options: 2 blacklisted domains, disabled.
StatifyBlacklist::$_options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(
'example.com' => 0,
'example.net' => 1,
),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'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://example.org';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Blacklisted referer.
$_SERVER['HTTP_REFERER'] = 'http://example.com';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Blacklisted referer with path.
$_SERVER['HTTP_REFERER'] = 'http://example.net/foo/bar.html';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter and run tests again.
StatifyBlacklist::$_options['referer']['active'] = 1;
unset( $_SERVER['HTTP_REFERER'] );
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'http://example.org';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'http://example.com';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_REFERER'] = 'http://example.net/foo/bar.html';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
}
/**
* Test referer filter using regular expressions.
*/
public function testRefererRegexFilter() {
// Prepare Options: 2 regular expressions.
StatifyBlacklist::$_options = array(
'referer' => array(
'active' => 1,
'cron' => 0,
'regexp' => 1,
'blacklist' => array(
'example.[a-z]+' => 0,
'test' => 1,
),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'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() );
// 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() );
}
/**
* Test the upgrade methodology for configuration options.
*/
public function testUpgrade() {
// Create configuration of version 1.3.
$options13 = array(
'active_referer' => 1,
'cron_referer' => 0,
'referer' => array(
'example.net' => 0,
'example.com' => 1,
),
'referer_regexp' => 0,
'version' => 1.3,
);
// Set options in mock.
update_option( 'statify-blacklist', $options13 );
// Execute upgrade.
StatifyBlacklist_System::upgrade();
// Retrieve updated options.
$optionsUpdated = 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'] ) );
// 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'] );
// 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'] );
// Verify that version number has changed to current release.
$this->assertEquals( StatifyBlacklist::VERSION_MAIN, $optionsUpdated['version'] );
}
/**
* Test CIDR address matching for IP filter (#7)
*/
public function testCidrMatch() {
// 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->assertFalse(
invokeStatic(
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',
)
)
);
$this->assertTrue(
invokeStatic(
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',
)
)
);
$this->assertTrue(
invokeStatic(
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',
)
)
);
$this->assertTrue( invokeStatic( 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',
)
)
);
// 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(
invokeStatic(
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 ',
)
)
);
$this->assertFalse(
invokeStatic(
StatifyBlacklist::class, 'cidr_match', array(
'2001:db8:a0b:12f0::1:132:465',
'2001:db8:a0b:12f0::1/96 ',
)
)
);
}
/**
* Test sanitization of IP addresses
*/
public function testSanitizeIPs() {
// 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 ) ) );
$this->assertNotFalse( $result );
$this->assertInternalType( 'array', $result );
$this->assertEquals( $valid, $result );
// IPv6 tests.
$valid = array(
'2001:db8:a0b:12f0::',
'2001:db8:a0b:12f0::1',
'2001:db8:a0b:12f0::1/128',
'2001:db8:a0b:12f0::/64',
);
$invalid = array(
'2001:db8:a0b:12f0::x',
'2001:db8:a0b:12f0:::',
'2001:fffff:a0b:12f0::1',
'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 ) ) );
$this->assertNotFalse( $result );
$this->assertInternalType( 'array', $result );
$this->assertEquals( $valid, $result );
}
/**
* Test IP filter (#7).
*/
public function testIPFilter() {
// Prepare Options: 2 blacklisted IPs, disabled.
StatifyBlacklist::$_options = array(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(),
),
'ip' => array(
'active' => 0,
'blacklist' => array(
'192.0.2.123',
'2001:db8:a0b:12f0::1',
),
),
'version' => StatifyBlacklist::VERSION_MAIN,
);
// No multisite.
StatifyBlacklist::$multisite = false;
// Set matching IP.
$_SERVER['REMOTE_ADDR'] = '192.0.2.123';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Activate filter.
StatifyBlacklist::$_options['ip']['active'] = 1;
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Try matching v6 address.
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::1';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
// Non-matching addresses.
$_SERVER['REMOTE_ADDR'] = '192.0.2.234';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::2';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Subnet matching.
StatifyBlacklist::$_options['ip']['blacklist'] = array(
'192.0.2.0/25',
'2001:db8:a0b:12f0::/96',
);
$_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() );
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0::5';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['REMOTE_ADDR'] = '2001:db8:a0b:12f0:0:1111::1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
// Filter using proxy header.
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_X_FORWARDED_FOR'] = '192.0.2.123';
$this->assertTrue( StatifyBlacklist::apply_blacklist_filter() );
$_SERVER['HTTP_X_REAL_IP'] = '2001:db8:a0b:12f0:0:1111::1';
$this->assertNull( StatifyBlacklist::apply_blacklist_filter() );
$_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(
'referer' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(),
),
'target' => array(
'active' => 0,
'cron' => 0,
'regexp' => 0,
'blacklist' => array(
'/excluded/page/' => 0,
'/?page_id=3' => 1,
),
),
'ip' => array(
'active' => 0,
'blacklist' => 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['target']['active'] = 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() );
}
// TODO: Test target regex filter.
}
/** @ignore */
function invokeStatic( $class, $methodName, $parameters = array() ) {
$reflection = new \ReflectionClass( $class );
$method = $reflection->getMethod( $methodName );
$method->setAccessible( true );
return $method->invokeArgs( null, $parameters );
}
// Some mocked WP functions.
$mock_options = array();
$mock_multisite = false;
/** @ignore */
function is_multisite() {
global $mock_multisite;
return $mock_multisite;
}
/** @ignore */
function wp_parse_args( $args, $defaults = '' ) {
if ( is_object( $args ) ) {
$r = get_object_vars( $args );
} elseif ( is_array( $args ) ) {
$r =& $args;
} else {
parse_str( $args, $r );
}
if ( is_array( $defaults ) ) {
return array_merge( $defaults, $r );
}
return $r;
}
/** @ignore */
function get_option( $option, $default = false ) {
global $mock_options;
return isset( $mock_options[ $option ] ) ? $mock_options[ $option ] : $default;
}
/** @ignore */
function update_option( $option, $value, $autoload = null ) {
global $mock_options;
$mock_options[ $option ] = $value;
}
/** @ignore */
function wp_get_raw_referer() {
return isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
}
function wp_parse_url( $value ) {
return parse_url( $value );
}
/** @ignore */
function wp_unslash( $value ) {
return is_string( $value ) ? stripslashes( $value ) : $value;
}

View File

@ -1,6 +1,6 @@
<?php
/**
* Statify Blacklist: Settings View
* Statify Filter: Settings View
*
* This file contains the dynamic HTML skeleton for the plugin's settings page.
*
@ -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?', 'statify-blacklist' ) );
}
if ( ! empty( $_POST['cleanUp'] ) ) {
@ -27,24 +29,83 @@ 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 );
}
);
}
// Extract user agent array.
if ( isset( $_POST['statifyblacklist']['ua']['blacklist'] ) ) {
$ua_string = sanitize_textarea_field( wp_unslash( $_POST['statifyblacklist']['ua']['blacklist'] ) );
}
if ( empty( trim( $ua_string ) ) ) {
$ua = array();
} else {
$ua = array_filter(
array_map(
function ( $a ) {
return trim( $a );
},
explode( "\r\n", str_replace( '\\\\', '\\', $ua_string ) )
),
function ( $a ) {
return ! empty( $a );
}
);
}
// Update options (data will be sanitized).
@ -73,27 +134,46 @@ if ( ! empty( $_POST['statifyblacklist'] ) ) {
? (int) $_POST['statifyblacklist']['ip']['active'] : 0,
'blacklist' => $ip,
),
'ua' => array(
'active' => isset( $_POST['statifyblacklist']['ua']['active'] )
? (int) $_POST['statifyblacklist']['ua']['active'] : 0,
'regexp' => isset( $_POST['statifyblacklist']['ua']['regexp'] )
? (int) $_POST['statifyblacklist']['ua']['regexp'] : 0,
'blacklist' => array_flip( $ua ),
),
'version' => StatifyBlacklist::VERSION_MAIN,
)
);
// 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 Filter', 'statify-blacklist' ); ?></h1>
<?php
if ( is_plugin_inactive( 'statify/statify.php' ) ) {
print '<div class="notice notice-warning"><p>';
@ -101,11 +181,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 +195,294 @@ 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 filter', '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 filter', '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 filter', '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_target_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 filter', '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 filter', '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 filter', '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>
<h2><?php esc_html_e( 'User agent filter', 'statify-blacklist' ); ?></h2>
<table class="form-table">
<tbody>
<tr>
<th scope="row">
<label for="statify-blacklist_active_ua">
<?php esc_html_e( 'Activate live filter', 'statify-blacklist' ); ?>
</label>
</th>
<td>
<input type="checkbox" name="statifyblacklist[ua][active]" id="statify-blacklist_active_ua"
value="1" <?php checked( StatifyBlacklist::$options['ua']['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 user agent filter, because the user agent is stored.', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_ua_regexp"><?php esc_html_e( 'Matching method', 'statify-blacklist' ); ?></label>
</th>
<td>
<select name="statifyblacklist[ua][regexp]" id="statify-blacklist_ua_regexp">
<option value="<?php print esc_attr( StatifyBlacklist::MODE_NORMAL ); ?>" <?php selected( StatifyBlacklist::$options['ua']['regexp'], StatifyBlacklist::MODE_NORMAL ); ?>>
<?php esc_html_e( 'Exact', 'statify-blacklist' ); ?>
</option>
<option value="<?php print esc_attr( StatifyBlacklist::MODE_KEYWORD ); ?>" <?php selected( StatifyBlacklist::$options['ua']['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['ua']['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['ua']['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 user agents', '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 user agent by regular expression', 'statify-blacklist' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="statify-blacklist_ua"><?php esc_html_e( 'User agent filter', 'statify-blacklist' ); ?></label>:
</th>
<td>
<textarea cols="40" rows="5" name="statifyblacklist[ua][blacklist]" id="statify-blacklist_ua"><?php
if ( empty( $statifyblacklist_update_result['ua'] ) ) {
print esc_html( implode( "\r\n", array_keys( StatifyBlacklist::$options['ua']['blacklist'] ) ) );
} else {
print esc_html( implode( "\r\n", array_keys( $statifyblacklist_update_result['ua']['sanitized'] ) ) );
}
?></textarea>
<p class="description">
<?php esc_html_e( 'Add one user agent string per line, e.g.', 'statify-blacklist' ); ?>
MyBot/1.23
</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', 'statify-blacklist' ); ?>">
<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>