From da6cde00cf2d7530fcaff0f8eaa4e137dc0a6457 Mon Sep 17 00:00:00 2001 From: Stefan Kalscheuer Date: Mon, 24 May 2021 18:36:28 +0200 Subject: [PATCH] migrate settings to WP settings API --- .github/workflows/test.yml | 2 +- inc/class-statifyblacklist-admin.php | 178 +----- inc/class-statifyblacklist-settings.php | 727 ++++++++++++++++++++++++ phpcs.xml | 1 - statify-blacklist.php | 1 + test/StatifyBlacklist_Admin_Test.php | 67 --- test/StatifyBlacklist_Settings_Test.php | 329 +++++++++++ test/bootstrap.php | 45 ++ views/settings-page.php | 488 ---------------- 9 files changed, 1117 insertions(+), 721 deletions(-) create mode 100644 inc/class-statifyblacklist-settings.php delete mode 100644 test/StatifyBlacklist_Admin_Test.php create mode 100644 test/StatifyBlacklist_Settings_Test.php delete mode 100755 views/settings-page.php diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ed72b0..0f8436c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: args: > -Dsonar.organization=stklcode-github -Dsonar.projectKey=stklcode:statify-blacklist - -Dsonar.sources=inc,views,statify-blacklist.php + -Dsonar.sources=inc,statify-blacklist.php -Dsonar.tests=test -Dsonar.php.tests.reportPath=tests-junit.xml -Dsonar.php.coverage.reportPaths=tests-clover.xml diff --git a/inc/class-statifyblacklist-admin.php b/inc/class-statifyblacklist-admin.php index 7151940..023321b 100644 --- a/inc/class-statifyblacklist-admin.php +++ b/inc/class-statifyblacklist-admin.php @@ -16,17 +16,15 @@ if ( ! defined( 'ABSPATH' ) ) { /** * 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 + * + * @since 1.5.0 */ public static function init() { // Add actions. @@ -46,91 +44,12 @@ class StatifyBlacklist_Admin extends StatifyBlacklist { 2 ); } else { + add_action( 'admin_init', array( 'StatifyBlacklist_Settings', 'register_settings' ) ); 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. - */ - public static function update_options( $options = null ) { - if ( isset( $options ) && current_user_can( 'manage_options' ) ) { - - // 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 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::sanitize_ips( $given_ip ); - - // Abort on errors. - $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. - if ( self::$multisite ) { - update_site_option( 'statify-blacklist', $options ); - } else { - update_option( 'statify-blacklist', $options ); - } - } - - // Refresh options. - parent::update_options( $options ); - - return false; - } - /** * Add configuration page to admin menu. * @@ -139,50 +58,33 @@ class StatifyBlacklist_Admin extends StatifyBlacklist { public static function add_menu_page() { $title = __( 'Statify Filter', 'statify-blacklist' ); if ( self::$multisite ) { - add_submenu_page( - 'settings.php', + add_options_page( $title, $title, 'manage_network_plugins', - 'statify-blacklist-settings', - array( - 'StatifyBlacklist_Admin', - 'settings_page', - ) + 'statify-blacklist', + array( 'StatifyBlacklist_Settings', 'create_settings_page' ) ); } else { - add_submenu_page( - 'options-general.php', + add_options_page( $title, $title, 'manage_options', 'statify-blacklist', - array( - 'StatifyBlacklist_Admin', - 'settings_page', - ) + array( 'StatifyBlacklist_Settings', 'create_settings_page' ) ); } } - /** - * Include the Statify-Blacklist settings page. - * - * @since 1.0.0 - */ - public static function settings_page() { - include STATIFYBLACKLIST_DIR . '/views/settings-page.php'; - } - /** * Add plugin meta links * - * @since 1.0.0 - * * @param array $links Registered links. * @param string $file The filename. * * @return array Merged links. + * + * @since 1.0.0 */ public static function plugin_meta_link( $links, $file ) { if ( STATIFYBLACKLIST_BASE === $file ) { @@ -195,12 +97,12 @@ class StatifyBlacklist_Admin extends StatifyBlacklist { /** * Add plugin action links. * - * @since 1.0.0 - * * @param array $links Registered links. * @param string $file The filename. * * @return array Merged links. + * + * @since 1.0.0 */ public static function plugin_actions_links( $links, $file ) { $base = self::$multisite ? network_admin_url( 'settings.php' ) : admin_url( 'options-general.php' ); @@ -298,11 +200,11 @@ class StatifyBlacklist_Admin extends StatifyBlacklist { /** * Sanitize URLs and remove empty results. * - * @since 1.1.1 - * * @param array $urls given array of URLs. * * @return array sanitized array. + * + * @since 1.1.1 */ private static function sanitize_urls( $urls ) { return array_flip( @@ -316,56 +218,4 @@ class StatifyBlacklist_Admin extends StatifyBlacklist { ) ); } - - /** - * Sanitize IP addresses with optional CIDR notation and remove empty results. - * - * @since 1.4.0 - * - * @param array $ips given array of URLs. - * - * @return array sanitized array. - */ - private static function sanitize_ips( $ips ) { - return array_filter( - array_map( 'strtolower', $ips ), - function ( $ip ) { - return preg_match( - '/^((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])(\/([0-9]|[1-2][0-9]|3[0-2]))?$/', - $ip - ) || - preg_match( - '/^(([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,7}:|([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}' . - '|([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}' . - '|([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}' . - '|[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|:((:[0-9a-f]{1,4}){1,7}|:)' . - '|fe80:(:[0-9a-f]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]' . - '|1?[0-9])?[0-9])|([0-9a-f]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))' . - '(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/i', - $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 ); - } - ); - } } diff --git a/inc/class-statifyblacklist-settings.php b/inc/class-statifyblacklist-settings.php new file mode 100644 index 0000000..2d5ff26 --- /dev/null +++ b/inc/class-statifyblacklist-settings.php @@ -0,0 +1,727 @@ + + array( __CLASS__, 'sanitize_options' ), + ) + ); + + // Referer filter. + add_settings_section( + 'statifyblacklist-referer', + __( 'Referer filter', 'statify-blacklist' ), + null, + 'statify-blacklist' + ); + add_settings_field( + 'statifyblacklist-referer-active', + __( 'Activate live filter', 'statify-blacklist' ), + array( __CLASS__, 'option_referer_active' ), + 'statify-blacklist', + 'statifyblacklist-referer' + ); + add_settings_field( + 'statifyblacklist-referer-cron', + __( 'CronJob execution', 'statify-blacklist' ), + array( __CLASS__, 'option_referer_cron' ), + 'statify-blacklist', + 'statifyblacklist-referer' + ); + add_settings_field( + 'statifyblacklist-referer-regexp', + __( 'Matching method', 'statify-blacklist' ), + array( __CLASS__, 'option_referer_regexp' ), + 'statify-blacklist', + 'statifyblacklist-referer', + array( 'label_for' => 'statifyblacklist-referer-regexp' ) + ); + add_settings_field( + 'statifyblacklist-referer-blacklist', + __( 'Referer filter', 'statify-blacklist' ), + array( __CLASS__, 'option_referer_blacklist' ), + 'statify-blacklist', + 'statifyblacklist-referer', + array( 'label_for' => 'statifyblacklist-referer-blacklist' ) + ); + + // Target filter. + add_settings_section( + 'statifyblacklist-target', + __( 'Target filter', 'statify-blacklist' ), + null, + 'statify-blacklist' + ); + add_settings_field( + 'statifyblacklist-target-active', + __( 'Activate live filter', 'statify-blacklist' ), + array( __CLASS__, 'option_target_active' ), + 'statify-blacklist', + 'statifyblacklist-target' + ); + add_settings_field( + 'statifyblacklist-target-cron', + __( 'CronJob execution', 'statify-blacklist' ), + array( __CLASS__, 'option_target_cron' ), + 'statify-blacklist', + 'statifyblacklist-target' + ); + add_settings_field( + 'statifyblacklist-target-regexp', + __( 'Matching method', 'statify-blacklist' ), + array( __CLASS__, 'option_target_regexp' ), + 'statify-blacklist', + 'statifyblacklist-target', + array( 'label_for' => 'statifyblacklist-target-regexp' ) + ); + add_settings_field( + 'statifyblacklist-target-blacklist', + __( 'Target filter', 'statify-blacklist' ), + array( __CLASS__, 'option_target_blacklist' ), + 'statify-blacklist', + 'statifyblacklist-target', + array( 'label_for' => 'statifyblacklist-target-blacklist' ) + ); + + // IP filter. + add_settings_section( + 'statifyblacklist-ip', + __( 'IP filter', 'statify-blacklist' ), + null, + 'statify-blacklist' + ); + add_settings_field( + 'statifyblacklist-ip-active', + __( 'Activate live filter', 'statify-blacklist' ), + array( __CLASS__, 'option_ip_active' ), + 'statify-blacklist', + 'statifyblacklist-ip' + ); + add_settings_field( + 'statifyblacklist-ip-blacklist', + __( 'IP filter', 'statify-blacklist' ), + array( __CLASS__, 'option_ip_blacklist' ), + 'statify-blacklist', + 'statifyblacklist-ip', + array( 'label_for' => 'statifyblacklist-ip-blacklist' ) + ); + + // User agent filter. + add_settings_section( + 'statifyblacklist-ua', + __( 'User agent filter', 'statify-blacklist' ), + null, + 'statify-blacklist' + ); + add_settings_field( + 'statifyblacklist-ua-active', + __( 'Activate live filter', 'statify-blacklist' ), + array( __CLASS__, 'option_ua_active' ), + 'statify-blacklist', + 'statifyblacklist-ua' + ); + add_settings_field( + 'statifyblacklist-ua-regexp', + __( 'Matching method', 'statify-blacklist' ), + array( __CLASS__, 'option_ua_regexp' ), + 'statify-blacklist', + 'statifyblacklist-ua', + array( 'label_for' => 'statifyblacklist-ua-regexp' ) + ); + add_settings_field( + 'statifyblacklist-ua-blacklist', + __( 'User agent filter', 'statify-blacklist' ), + array( __CLASS__, 'option_ua_blacklist' ), + 'statify-blacklist', + 'statifyblacklist-ua', + array( 'label_for' => 'statifyblacklist-ua-blacklist' ) + ); + } + + /** + * Creates the settings pages. + * + * @return void + */ + public static function create_settings_page() { + ?> +
+

+ +
+ +
+ +

+ + +

+
+
+ + +
+ + +

+ +

+
+ +
+ + +

+ +

+
+ + +

+ - +
+ - +
+ - +

+ + +

+ +

+ +
+ + +

+ +

+
+ +
+ + +

+ +

+
+ + +

+ - +
+ - +

+ + +

+ /, /test/page/, /?page_id=123 +

+ +
+ + +

+ +
+ +

+
+ + +

+ + 127.0.0.1, 192.168.123.0/24, 2001:db8:a0b:12f0::1/64 +

+ + + +

+ +
+ +

+ + +

+ - +
+ - +
+ - +

+ + +

+ + MyBot/1.23 +

+ array( + 'active' => isset( $options['referer']['active'] ) ? (int) $options['referer']['active'] : 0, + 'cron' => isset( $options['referer']['cron'] ) ? (int) $options['referer']['cron'] : 0, + 'regexp' => isset( $options['referer']['regexp'] ) ? (int) $options['referer']['regexp'] : 0, + 'blacklist' => array_flip( $referer ), + ), + 'target' => array( + 'active' => isset( $options['target']['active'] ) ? (int) $options['target']['active'] : 0, + 'cron' => isset( $options['target']['cron'] ) ? (int) $options['target']['cron'] : 0, + 'regexp' => isset( $options['target']['regexp'] ) ? (int) $options['target']['regexp'] : 0, + 'blacklist' => array_flip( $target ), + ), + 'ip' => array( + 'active' => isset( $options['ip']['active'] ) ? (int) $options['ip']['active'] : 0, + 'blacklist' => $ip, + ), + 'ua' => array( + 'active' => isset( $options['ua']['active'] ) ? (int) $options['ua']['active'] : 0, + 'regexp' => isset( $options['ua']['regexp'] ) ? (int) $options['ua']['regexp'] : 0, + 'blacklist' => array_flip( $ua ), + ), + 'version' => StatifyBlacklist::VERSION_MAIN, + ); + + // Apply sanitizations. + self::sanitize_referer_options( $res['referer'] ); + self::sanitize_target_options( $res['target'] ); + self::sanitize_ip_options( $res['ip'] ); + + return $res; + } + + /** + * Sanitize referer options. + * + * @param array $options Original referer options. + * + * @return void + * + * @since 1.7.0 + */ + private static function sanitize_referer_options( &$options ) { + $referer_given = $options['blacklist']; + $referer_invalid = array(); + if ( StatifyBlacklist::MODE_NORMAL === $options['regexp'] ) { + // Sanitize URLs and remove empty inputs. + $referer_sanitized = self::sanitize_urls( $referer_given ); + } elseif ( StatifyBlacklist::MODE_REGEX === $options['regexp'] || StatifyBlacklist::MODE_REGEX_CI === $options['regexp'] ) { + $referer_sanitized = $referer_given; + // Check regular expressions. + $referer_invalid = self::sanitize_regex( $referer_given ); + } else { + $referer_sanitized = $referer_given; + } + $referer_diff = array_diff_key( $referer_given, $referer_sanitized ); + $options['blacklist'] = $referer_sanitized; + + // Generate messages. + if ( ! empty( $referer_diff ) ) { + add_settings_error( + 'statify-blacklist', + 'referer-diff', + __( 'Some URLs are invalid and have been sanitized.', 'statify-blacklist' ), + 'warning' + ); + } + if ( ! empty( $referer_invalid ) ) { + add_settings_error( + 'statify-blacklist', + 'referer-invalid', + __( 'Some regular expressions for referrers are invalid:', 'statify-blacklist' ) . '
' . implode( '
', $referer_invalid ) + ); + } + } + + /** + * Sanitize target options. + * + * @param array $options Original target options. + * + * @return void + * + * @since 1.7.0 + */ + private static function sanitize_target_options( &$options ) { + $target_given = $options['blacklist']; + $target_invalid = array(); + if ( StatifyBlacklist::MODE_REGEX === $options['regexp'] || StatifyBlacklist::MODE_REGEX_CI === $options['regexp'] ) { + $target_sanitized = $target_given; + // Check regular expressions. + $target_invalid = self::sanitize_regex( $target_given ); + } else { + $target_sanitized = $target_given; + } + $options['blacklist'] = $target_sanitized; + + // Generate messages. + if ( ! empty( $target_invalid ) ) { + add_settings_error( + 'statify-blacklist', + 'target-invalid', + __( 'Some regular expressions for targets are invalid:', 'statify-blacklist' ) . '
' . implode( '
', $target_invalid ) + ); + } + } + + /** + * Sanitize IPs and subnets and remove empty inputs. + * + * @param array $options Original IP options. + * + * @return void + * + * @since 1.7.0 + */ + private static function sanitize_ip_options( &$options ) { + $given_ip = $options['blacklist']; + $sanitized_ip = self::sanitize_ips( $given_ip ); + $ip_diff = array_diff( $given_ip, $sanitized_ip ); + $options['blacklist'] = $sanitized_ip; + + // Generate messages. + if ( ! empty( $ip_diff ) ) { + add_settings_error( + 'statify-blacklist', + 'ip-diff', + // translators: List of invalid IP addresses (comma separated). + sprintf( __( 'Some IPs are invalid: %s', 'statify-blacklist' ), implode( ', ', $ip_diff ) ), + 'warning' + ); + } + } + + /** + * Sanitize URLs and remove empty results. + * + * @param array $urls given array of URLs. + * + * @return array sanitized array. + * + * @since 1.1.1 + * @since 1.7.0 moved from StatifyBlacklist_Admin to StatifyBlacklist_Settings. + */ + private static function sanitize_urls( $urls ) { + return array_flip( + array_filter( + array_map( + function ( $r ) { + return preg_replace( '/[^\da-z\.-]/i', '', filter_var( $r, FILTER_SANITIZE_URL ) ); + }, + array_flip( $urls ) + ) + ) + ); + } + + /** + * Sanitize IP addresses with optional CIDR notation and remove empty results. + * + * @param array $ips given array of URLs. + * + * @return array sanitized array. + * + * @since 1.4.0 + * @since 1.7.0 moved from StatifyBlacklist_Admin to StatifyBlacklist_Settings. + */ + private static function sanitize_ips( $ips ) { + return array_filter( + array_map( 'strtolower', $ips ), + function ( $ip ) { + return preg_match( + '/^((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])(\/([0-9]|[1-2][0-9]|3[0-2]))?$/', + $ip + ) || + preg_match( + '/^(([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,7}:|([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}' . + '|([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}' . + '|([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}' . + '|[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|:((:[0-9a-f]{1,4}){1,7}|:)' . + '|fe80:(:[0-9a-f]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]' . + '|1?[0-9])?[0-9])|([0-9a-f]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[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. + * + * @param array $expressions Given pre-sanitized array of regular expressions. + * + * @return array Array of invalid expressions. + * + * @since 1.5.0 #13 + * @since 1.7.0 moved from StatifyBlacklist_Admin to StatifyBlacklist_Settings. + */ + 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 ); + } + ); + } + + /** + * Parse multi-line option string. + * + * @param string $raw Input string. + * + * @return array Parsed options. + */ + private static function parse_multiline_option( $raw ) { + if ( empty( trim( $raw ) ) ) { + return array(); + } else { + return array_filter( + array_map( + function ( $a ) { + return trim( $a ); + }, + explode( "\r\n", str_replace( '\\\\', '\\', $raw ) ) + ), + function ( $a ) { + return ! empty( $a ); + } + ); + } + } +} diff --git a/phpcs.xml b/phpcs.xml index a4ff01e..1e0c607 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -8,7 +8,6 @@ statify-blacklist.php inc - views diff --git a/statify-blacklist.php b/statify-blacklist.php index 240d86b..1f25d37 100644 --- a/statify-blacklist.php +++ b/statify-blacklist.php @@ -70,6 +70,7 @@ function statify_blacklist_autoload( $class ) { $plugin_classes = array( 'StatifyBlacklist', 'StatifyBlacklist_Admin', + 'StatifyBlacklist_Settings', 'StatifyBlacklist_System', ); diff --git a/test/StatifyBlacklist_Admin_Test.php b/test/StatifyBlacklist_Admin_Test.php deleted file mode 100644 index 3ad9f20..0000000 --- a/test/StatifyBlacklist_Admin_Test.php +++ /dev/null @@ -1,67 +0,0 @@ -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', - 'fe80::7645:6de2:ff:1', - '::ffff:192.0.2.123', - ); - $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', - '::ffff:12.34.56.789', - ); - $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( array_map( 'strtolower', $valid ), $result ); - } -} diff --git a/test/StatifyBlacklist_Settings_Test.php b/test/StatifyBlacklist_Settings_Test.php new file mode 100644 index 0000000..16e7620 --- /dev/null +++ b/test/StatifyBlacklist_Settings_Test.php @@ -0,0 +1,329 @@ + array( + 'blacklist' => '', + 'regexp' => '0', + ), + 'target' => array( + 'blacklist' => '', + 'regexp' => '0', + ), + 'ip' => array( 'blacklist' => '' ), + 'ua' => array( + 'blacklist' => '', + 'regexp' => '0', + ), + ); + + $sanitized = StatifyBlacklist_Settings::sanitize_options( $raw ); + + self::assertEmpty( $settings_error ); + self::assertEquals( + array( + 'referer' => array( + 'active' => 0, + 'cron' => 0, + 'blacklist' => array(), + 'regexp' => StatifyBlacklist::MODE_NORMAL, + ), + 'target' => array( + 'active' => 0, + 'cron' => 0, + 'blacklist' => array(), + 'regexp' => StatifyBlacklist::MODE_NORMAL, + ), + 'ip' => array( + 'active' => 0, + 'blacklist' => array(), + ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_NORMAL, + 'blacklist' => array(), + ), + 'version' => StatifyBlacklist::VERSION_MAIN, + ), + $sanitized + ); + + // Some checked options and some valid entries. + $raw = array( + 'referer' => array( + 'cron' => '1', + 'blacklist' => "example.com\r\nexample.net\r\nexample.org", + 'regexp' => '0', + ), + 'target' => array( + 'active' => '1', + 'blacklist' => "foo\r\nbar\r\ntest", + 'regexp' => '3', + ), + 'ip' => array( + 'active' => '1', + 'blacklist' => "127.0.0.1/8\r\n::1", + ), + 'ua' => array( + 'blacklist' => 'MyBot/1.23', + 'regexp' => '1', + ), + ); + + $sanitized = StatifyBlacklist_Settings::sanitize_options( $raw ); + + self::assertEmpty( $settings_error ); + self::assertEquals( + array( + 'referer' => array( + 'active' => 0, + 'cron' => 1, + 'blacklist' => array( + 'example.com' => 0, + 'example.net' => 1, + 'example.org' => 2, + ), + 'regexp' => StatifyBlacklist::MODE_NORMAL, + ), + 'target' => array( + 'active' => 1, + 'cron' => 0, + 'blacklist' => array( + 'foo' => 0, + 'bar' => 1, + 'test' => 2, + ), + 'regexp' => StatifyBlacklist::MODE_KEYWORD, + ), + 'ip' => array( + 'active' => 1, + 'blacklist' => array( + '127.0.0.1/8', + '::1', + ), + ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_REGEX, + 'blacklist' => array( + 'MyBot/1.23' => 0, + ), + ), + 'version' => StatifyBlacklist::VERSION_MAIN, + ), + $sanitized + ); + + // Now we have some additional nonsense fields and invalid entries. + $raw = array( + 'testme ' => 'whatever', + 'referer' => array( + 'cron' => '1', + 'blacklist' => " example\\.com \r\nexample(\\.net\r\nexample\\.com", + 'regexp' => '1', + ), + 'target' => array( + 'active' => '1', + 'blacklist' => "fo.\r\n[bar\r\n*test", + 'regexp' => '2', + ), + 'ip' => array( + 'active' => '1', + 'blacklist' => "127.0.0.1/8\r\nthisisnotanip", + ), + 'ua' => array( + 'blacklist' => 'MyBot/1.23', + 'regexp' => '1', + ), + ); + + $sanitized = StatifyBlacklist_Settings::sanitize_options( $raw ); + + self::assertEquals( + array( + 'referer' => array( + 'active' => 0, + 'cron' => 1, + 'blacklist' => array( + 'example\.com' => 2, + 'example(\.net' => 1, + ), + 'regexp' => StatifyBlacklist::MODE_REGEX, + ), + 'target' => array( + 'active' => 1, + 'cron' => 0, + 'blacklist' => array( + 'fo.' => 0, + '[bar' => 1, + '*test' => 2, + ), + 'regexp' => StatifyBlacklist::MODE_REGEX_CI, + ), + 'ip' => array( + 'active' => 1, + 'blacklist' => array( + '127.0.0.1/8', + ), + ), + 'ua' => array( + 'active' => 0, + 'regexp' => StatifyBlacklist::MODE_REGEX, + 'blacklist' => array( + 'MyBot/1.23' => 0, + ), + ), + 'version' => StatifyBlacklist::VERSION_MAIN, + ), + $sanitized + ); + + self::assertEquals( + array( + array( 'statify-blacklist', 'referer-invalid', 'Some regular expressions for referrers are invalid:
example(\.net', 'error' ), + array( 'statify-blacklist', 'target-invalid', 'Some regular expressions for targets are invalid:
[bar
*test', 'error' ), + array( 'statify-blacklist', 'ip-diff', 'Some IPs are invalid: thisisnotanip', 'warning' ), + ), + $settings_error + ); + } + + /** + * 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_Settings::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', + 'fe80::7645:6de2:ff:1', + '::ffff:192.0.2.123', + ); + $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', + '::ffff:12.34.56.789', + ); + $result = invoke_static( StatifyBlacklist_Settings::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( + array( + '2001:db8:a0b:12f0::', + '2001:db8:a0b:12f0::1', + '2001:db8:a0b:12f0::1/128', + '2001:db8:a0b:12f0::/64', + 'fe80::7645:6de2:ff:1', + '::ffff:192.0.2.123', + ), + array_values( $result ) + ); + } + + /** + * Test settings registration. + * + * @return void + */ + public function test_register_settings() { + global $settings; + $settings = array(); + + StatifyBlacklist_Settings::register_settings(); + $this->assertEquals( array( 'statify-blacklist' ), array_keys( $settings ), 'unexpected settings pages' ); + $this->assertEquals( + array( + 'statifyblacklist-referer', + 'statifyblacklist-target', + 'statifyblacklist-ip', + 'statifyblacklist-ua', + ), + array_keys( $settings['statify-blacklist']['sections'] ), + 'unexpected settings sections' + ); + $this->assertEquals( + array( + 'statifyblacklist-referer-active', + 'statifyblacklist-referer-cron', + 'statifyblacklist-referer-regexp', + 'statifyblacklist-referer-blacklist', + ), + array_keys( $settings['statify-blacklist']['sections']['statifyblacklist-referer']['fields'] ), + 'unexpected fields in referrer section' + ); + $this->assertEquals( + array( + 'statifyblacklist-target-active', + 'statifyblacklist-target-cron', + 'statifyblacklist-target-regexp', + 'statifyblacklist-target-blacklist', + ), + array_keys( $settings['statify-blacklist']['sections']['statifyblacklist-target']['fields'] ), + 'unexpected fields in target section' + ); + $this->assertEquals( + array( 'statifyblacklist-ip-active', 'statifyblacklist-ip-blacklist' ), + array_keys( $settings['statify-blacklist']['sections']['statifyblacklist-ip']['fields'] ), + 'unexpected fields in ip section' + ); + $this->assertEquals( + array( + 'statifyblacklist-ua-active', + 'statifyblacklist-ua-regexp', + 'statifyblacklist-ua-blacklist', + ), + array_keys( $settings['statify-blacklist']['sections']['statifyblacklist-ua']['fields'] ), + 'unexpected fields in user agent section' + ); + } +} diff --git a/test/bootstrap.php b/test/bootstrap.php index 6a3796e..97228e7 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -17,6 +17,7 @@ const ABSPATH = false; */ require_once __DIR__ . '/../inc/class-statifyblacklist.php'; require_once __DIR__ . '/../inc/class-statifyblacklist-admin.php'; +require_once __DIR__ . '/../inc/class-statifyblacklist-settings.php'; require_once __DIR__ . '/../inc/class-statifyblacklist-system.php'; // Include Composer autoloader. @@ -36,6 +37,8 @@ function invoke_static( $class, $method_name, $parameters = array() ) { // Some mocked WP functions. $mock_options = array(); $mock_multisite = false; +$settings_error = array(); +$settings = array(); /** @ignore */ function is_multisite() { @@ -87,3 +90,45 @@ function wp_parse_url( $value ) { function wp_unslash( $value ) { return is_string( $value ) ? stripslashes( $value ) : $value; } + +/** @ignore */ +function __( $text, $domain = 'default' ) { + return $text; +} + +/** @ignore */ +function add_settings_error( $setting, $code, $message, $type = 'error' ) { + global $settings_error; + $settings_error[] = array( $setting, $code, $message, $type ); +} + +/** @ignore */ +function register_setting( $option_group, $option_name, $args = array() ) { + global $settings; + $settings[ $option_name ] = array( + 'group' => $option_group, + 'args' => $args, + 'sections' => array(), + ); +} + +/** @ignore */ +function add_settings_section( $id, $title, $callback, $page, $args = array() ) { + global $settings; + $settings[ $page ]['sections'][ $id ] = array( + 'title' => $title, + 'callback' => $callback, + 'args' => $args, + 'fields' => array(), + ); +} + +/** @ignore */ +function add_settings_field( $id, $title, $callback, $page, $section = 'default', $args = array() ) { + global $settings; + $settings[ $page ]['sections'][ $section ]['fields'][ $id ] = array( + 'title' => $title, + 'callback' => $callback, + 'args' => $args, + ); +} diff --git a/views/settings-page.php b/views/settings-page.php deleted file mode 100755 index 3c082cd..0000000 --- a/views/settings-page.php +++ /dev/null @@ -1,488 +0,0 @@ - array( - 'active' => isset( $_POST['statifyblacklist']['referer']['active'] ) - ? (int) $_POST['statifyblacklist']['referer']['active'] : 0, - 'cron' => isset( $_POST['statifyblacklist']['referer']['cron'] ) - ? (int) $_POST['statifyblacklist']['referer']['cron'] : 0, - 'regexp' => isset( $_POST['statifyblacklist']['referer']['regexp'] ) - ? (int) $_POST['statifyblacklist']['referer']['regexp'] : 0, - 'blacklist' => array_flip( $referer ), - ), - 'target' => array( - 'active' => isset( $_POST['statifyblacklist']['target']['active'] ) - ? (int) $_POST['statifyblacklist']['target']['active'] : 0, - 'cron' => isset( $_POST['statifyblacklist']['target']['cron'] ) - ? (int) $_POST['statifyblacklist']['target']['cron'] : 0, - 'regexp' => isset( $_POST['statifyblacklist']['target']['regexp'] ) - ? (int) $_POST['statifyblacklist']['target']['regexp'] : 0, - 'blacklist' => array_flip( $target ), - ), - 'ip' => array( - 'active' => isset( $_POST['statifyblacklist']['ip']['active'] ) - ? (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 ) { - $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' ) . '
' . implode( '
', $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']['diff'] ) ); - } - } else { - $statifyblacklist_post_success = __( 'Settings updated successfully.', 'statify-blacklist' ); - } - } -} - -/* - * Disable some code style rules that are impractical for textarea content: - * - * phpcs:disable Squiz.PHP.EmbeddedPhp.ContentBeforeOpen - * phpcs:disable Squiz.PHP.EmbeddedPhp.ContentAfterEnd - */ -?> - -
-

-

'; - esc_html_e( 'Statify plugin is not active.', 'statify-blacklist' ); - print '

'; - } - if ( isset( $statifyblacklist_post_warning ) ) { - foreach ( $statifyblacklist_post_warning as $w ) { - print '

' . - wp_kses( $w, array( 'br' => array() ) ) . - '

'; - } - print '

' . esc_html__( 'Settings have not been saved yet.', 'statify-blacklist' ) . '

'; - } - if ( isset( $statifyblacklist_post_success ) ) { - print '

' . - esc_html( $statifyblacklist_post_success ) . - '

'; - } - ?> -
- - -

- - - - - - - - - - - - - - - - - - - - -
- - - > -

- -

-
- - - > -

-
- - - - -

- - -
- - -
- - -

-
- - - -

- -

-
- -

- - - - - - - - - - - - - - - - - - - - -
- - - > -

- -

-
- - - > -

- -

-
- - - - -

- - -
- - -

-
- - - - -

- /, /test/page/, /?page_id=123 -

-
- -

- - - - - - - - - - - - -
- - - > -

- -
- -

-
- : - - - -

- - 127.0.0.1, 192.168.123.0/24, 2001:db8:a0b:12f0::1/64 -

-
- -

- - - - - - - - - - - - - - - - -
- - - > -

- -
- -

-
- - - - -

- - -
- - -
- - -

-
- : - - - -

- - MyBot/1.23 -

-
- -

- -


- -
-

- - -

-

-
-