updated plugin Jetpack Protect
version 1.3.0
This commit is contained in:
@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.10.1] - 2023-03-07
|
||||
- Updated package dependencies.
|
||||
|
||||
## [0.10.0] - 2023-02-28
|
||||
### Added
|
||||
- Added support for IP ranges in allow and block lists. [#29131]
|
||||
|
||||
## [0.9.3] - 2023-02-20
|
||||
### Changed
|
||||
- Minor internal updates.
|
||||
|
||||
## [0.9.2] - 2023-02-15
|
||||
### Changed
|
||||
- Minor internal updates.
|
||||
|
||||
## [0.9.1] - 2023-02-13
|
||||
### Fixed
|
||||
- Fix an update error that impacted sites using the WAF in standalone mode. [#28844]
|
||||
|
||||
## [0.9.0] - 2023-01-25
|
||||
### Changed
|
||||
- Change the web application firewall to run automatic and manual rules independently. [#27726]
|
||||
|
||||
## [0.8.3] - 2023-01-11
|
||||
### Fixed
|
||||
- Fixed the WAF package's PHP tests and Composer requirements [#28185]
|
||||
|
||||
## [0.8.2] - 2023-01-09
|
||||
### Fixed
|
||||
- Fix firewall activation hooks on first option updates. [#28234]
|
||||
|
||||
## [0.8.1] - 2023-01-07
|
||||
### Changed
|
||||
- Change directory location that stores firewall rules. [#28049]
|
||||
@ -131,6 +162,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
- Core: do not ship .phpcs.dir.xml in production builds.
|
||||
|
||||
[0.10.1]: https://github.com/Automattic/jetpack-waf/compare/v0.10.0...v0.10.1
|
||||
[0.10.0]: https://github.com/Automattic/jetpack-waf/compare/v0.9.3...v0.10.0
|
||||
[0.9.3]: https://github.com/Automattic/jetpack-waf/compare/v0.9.2...v0.9.3
|
||||
[0.9.2]: https://github.com/Automattic/jetpack-waf/compare/v0.9.1...v0.9.2
|
||||
[0.9.1]: https://github.com/Automattic/jetpack-waf/compare/v0.9.0...v0.9.1
|
||||
[0.9.0]: https://github.com/Automattic/jetpack-waf/compare/v0.8.3...v0.9.0
|
||||
[0.8.3]: https://github.com/Automattic/jetpack-waf/compare/v0.8.2...v0.8.3
|
||||
[0.8.2]: https://github.com/Automattic/jetpack-waf/compare/v0.8.1...v0.8.2
|
||||
[0.8.1]: https://github.com/Automattic/jetpack-waf/compare/v0.8.0...v0.8.1
|
||||
[0.8.0]: https://github.com/Automattic/jetpack-waf/compare/v0.7.2...v0.8.0
|
||||
[0.7.2]: https://github.com/Automattic/jetpack-waf/compare/v0.7.1...v0.7.2
|
||||
|
@ -4,11 +4,16 @@
|
||||
"type": "jetpack-library",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"automattic/jetpack-connection": "^1.51.1",
|
||||
"automattic/jetpack-constants": "^1.6.21",
|
||||
"automattic/jetpack-ip": "^0.1.0",
|
||||
"automattic/jetpack-status": "^1.16.2",
|
||||
"wikimedia/aho-corasick": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"yoast/phpunit-polyfills": "1.0.4",
|
||||
"automattic/jetpack-changelogger": "^3.3.1-alpha"
|
||||
"automattic/jetpack-changelogger": "^3.3.2",
|
||||
"automattic/wordbless": "@dev"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
@ -20,13 +25,14 @@
|
||||
},
|
||||
"scripts": {
|
||||
"phpunit": [
|
||||
"./vendor/phpunit/phpunit/phpunit --colors=always"
|
||||
],
|
||||
"test-coverage": [
|
||||
"php -dpcov.directory=. ./vendor/bin/phpunit --coverage-clover \"$COVERAGE_DIR/clover.xml\""
|
||||
"./vendor/phpunit/phpunit/phpunit --configuration tests/php/integration/phpunit.xml.dist --colors=always",
|
||||
"./vendor/phpunit/phpunit/phpunit --configuration tests/php/unit/phpunit.xml.dist --colors=always"
|
||||
],
|
||||
"post-install-cmd": "WorDBless\\Composer\\InstallDropin::copy",
|
||||
"post-update-cmd": "WorDBless\\Composer\\InstallDropin::copy",
|
||||
"test-coverage-html": [
|
||||
"php -dpcov.directory=. ./vendor/bin/phpunit --coverage-html ./coverage"
|
||||
"php -dpcov.directory=. ./vendor/bin/phpunit --coverage-html ./coverage --configuration tests/php/integration/phpunit.xml.dist",
|
||||
"php -dpcov.directory=. ./vendor/bin/phpunit --coverage-html ./coverage --configuration tests/php/unit/phpunit.xml.dist"
|
||||
],
|
||||
"test-php": [
|
||||
"@composer phpunit"
|
||||
@ -42,7 +48,12 @@
|
||||
"link-template": "https://github.com/Automattic/jetpack-waf/compare/v${old}...v${new}"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-trunk": "0.8.x-dev"
|
||||
"dev-trunk": "0.10.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"roots/wordpress-core-installer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,43 @@ class Waf_Compatibility {
|
||||
* @return void
|
||||
*/
|
||||
public static function add_compatibility_hooks() {
|
||||
add_filter( 'default_option_' . Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME, __CLASS__ . '::default_option_waf_automatic_rules', 10, 3 );
|
||||
add_filter( 'default_option_' . Waf_Initializer::NEEDS_UPDATE_OPTION_NAME, __CLASS__ . '::default_option_waf_needs_update', 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a default value for sites that installed the WAF
|
||||
* before the automatic rules option was introduced.
|
||||
*
|
||||
* @since 0.9.0
|
||||
*
|
||||
* @param mixed $default The default value to return if the option does not exist in the database.
|
||||
* @param string $option Option name.
|
||||
* @param bool $passed_default Was get_option() passed a default value.
|
||||
*
|
||||
* @return mixed The default value to return if the option does not exist in the database.
|
||||
*/
|
||||
public static function default_option_waf_automatic_rules( $default, $option, $passed_default ) {
|
||||
// Allow get_option() to override this default value
|
||||
if ( $passed_default ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return self::get_default_automatic_rules_option();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the option is not available, use the WAF module status
|
||||
* to determine whether or not to run automatic rules.
|
||||
*
|
||||
* @since 0.9.0
|
||||
*
|
||||
* @return bool The default value for automatic rules.
|
||||
*/
|
||||
public static function get_default_automatic_rules_option() {
|
||||
return Waf_Runner::is_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a default value for sites that installed the WAF
|
||||
* before the NEEDS_UPDATE_OPTION_NAME option was added.
|
||||
|
@ -17,6 +17,8 @@ use WP_REST_Server;
|
||||
class REST_Controller {
|
||||
/**
|
||||
* Register REST API endpoints.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register_rest_routes() {
|
||||
register_rest_route(
|
||||
@ -52,28 +54,29 @@ class REST_Controller {
|
||||
|
||||
/**
|
||||
* Update rules endpoint
|
||||
*
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public static function update_rules() {
|
||||
$success = true;
|
||||
$message = 'Rules updated succesfully';
|
||||
|
||||
try {
|
||||
Waf_Runner::generate_rules();
|
||||
} catch ( \Exception $e ) {
|
||||
$success = false;
|
||||
$message = $e->getMessage();
|
||||
Waf_Rules_Manager::generate_automatic_rules();
|
||||
Waf_Rules_Manager::generate_rules();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
|
||||
return rest_ensure_response(
|
||||
array(
|
||||
'success' => $success,
|
||||
'message' => $message,
|
||||
'success' => true,
|
||||
'message' => __( 'Rules updated succesfully', 'jetpack-waf' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* WAF Endpoint
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public static function waf() {
|
||||
return rest_ensure_response( Waf_Runner::get_config() );
|
||||
@ -83,22 +86,28 @@ class REST_Controller {
|
||||
* Update WAF Endpoint
|
||||
*
|
||||
* @param WP_REST_Request $request The API request.
|
||||
* @return WP_REST_Response
|
||||
*
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public static function update_waf( $request ) {
|
||||
// Automatic Rules Enabled
|
||||
if ( isset( $request[ Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME ] ) ) {
|
||||
update_option( Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME, (bool) $request->get_param( Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME ) );
|
||||
}
|
||||
|
||||
// IP Lists Enabled
|
||||
if ( isset( $request[ Waf_Runner::IP_LISTS_ENABLED_OPTION_NAME ] ) ) {
|
||||
update_option( Waf_Runner::IP_LISTS_ENABLED_OPTION_NAME, (bool) $request->get_param( Waf_Runner::IP_LISTS_ENABLED_OPTION_NAME ) );
|
||||
if ( isset( $request[ Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME ] ) ) {
|
||||
update_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, (bool) $request->get_param( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME ) );
|
||||
}
|
||||
|
||||
// IP Block List
|
||||
if ( isset( $request[ Waf_Runner::IP_BLOCK_LIST_OPTION_NAME ] ) ) {
|
||||
update_option( Waf_Runner::IP_BLOCK_LIST_OPTION_NAME, $request[ Waf_Runner::IP_BLOCK_LIST_OPTION_NAME ] );
|
||||
if ( isset( $request[ Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME ] ) ) {
|
||||
update_option( Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME, $request[ Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME ] );
|
||||
}
|
||||
|
||||
// IP Allow List
|
||||
if ( isset( $request[ Waf_Runner::IP_ALLOW_LIST_OPTION_NAME ] ) ) {
|
||||
update_option( Waf_Runner::IP_ALLOW_LIST_OPTION_NAME, $request[ Waf_Runner::IP_ALLOW_LIST_OPTION_NAME ] );
|
||||
if ( isset( $request[ Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME ] ) ) {
|
||||
update_option( Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME, $request[ Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME ] );
|
||||
}
|
||||
|
||||
// Share Data
|
||||
@ -106,7 +115,11 @@ class REST_Controller {
|
||||
update_option( Waf_Runner::SHARE_DATA_OPTION_NAME, (bool) $request[ Waf_Runner::SHARE_DATA_OPTION_NAME ] );
|
||||
}
|
||||
|
||||
Waf_Runner::update_waf();
|
||||
try {
|
||||
Waf_Runner::update_waf();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
|
||||
return self::waf();
|
||||
}
|
||||
|
@ -142,7 +142,8 @@ class CLI extends WP_CLI_Command {
|
||||
*/
|
||||
public function generate_rules() {
|
||||
try {
|
||||
Waf_Runner::generate_rules();
|
||||
Waf_Rules_Manager::generate_automatic_rules();
|
||||
Waf_Rules_Manager::generate_rules();
|
||||
} catch ( \Exception $e ) {
|
||||
|
||||
return WP_CLI::error(
|
||||
@ -158,7 +159,7 @@ class CLI extends WP_CLI_Command {
|
||||
sprintf(
|
||||
/* translators: %1$s is the name of the mode that was just switched to. */
|
||||
__( 'Jetpack WAF rules successfully created to: "%1$s".', 'jetpack-waf' ),
|
||||
Waf_Runner::get_waf_file_path( Waf_Runner::RULES_FILE )
|
||||
Waf_Runner::get_waf_file_path( Waf_Rules_Manager::RULES_ENTRYPOINT_FILE )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -18,17 +18,76 @@ class Waf_Constants {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function initialize_bootstrap_constants() {
|
||||
self::define_waf_directory();
|
||||
self::define_wpconfig_path();
|
||||
self::define_killswitch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatiblity patch for cases where an outdated Waf_Constants class has been autoloaded by
|
||||
* the standalone bootstrap execution at the beginning of the current request.
|
||||
*/
|
||||
public static function initialize_constants() {
|
||||
self::initialize_bootstrap_constants();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path to the WAF directory if it has not been set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_waf_directory() {
|
||||
if ( ! defined( 'JETPACK_WAF_DIR' ) ) {
|
||||
define( 'JETPACK_WAF_DIR', trailingslashit( WP_CONTENT_DIR ) . 'jetpack-waf' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path to the wp-config.php file if it has not been set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_wpconfig_path() {
|
||||
if ( ! defined( 'JETPACK_WAF_WPCONFIG' ) ) {
|
||||
define( 'JETPACK_WAF_WPCONFIG', trailingslashit( WP_CONTENT_DIR ) . '../wp-config.php' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the killswitch definition if it has not been set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_killswitch() {
|
||||
if ( ! defined( 'DISABLE_JETPACK_WAF' ) ) {
|
||||
$is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;
|
||||
$is_atomic = ( new Host() )->is_atomic_platform();
|
||||
define( 'DISABLE_JETPACK_WAF', $is_wpcom || $is_atomic );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode definition if it has not been set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_mode() {
|
||||
if ( ! defined( 'JETPACK_WAF_MODE' ) ) {
|
||||
$mode_option = get_option( Waf_Runner::MODE_OPTION_NAME );
|
||||
define( 'JETPACK_WAF_MODE', $mode_option );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the share data definition if it has not been set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_share_data() {
|
||||
if ( ! defined( 'JETPACK_WAF_SHARE_DATA' ) ) {
|
||||
$share_data_option = get_option( Waf_Runner::SHARE_DATA_OPTION_NAME, false );
|
||||
define( 'JETPACK_WAF_SHARE_DATA', $share_data_option );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Initializes the module
|
||||
*/
|
||||
@ -47,19 +49,37 @@ class Waf_Initializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* On module activation set up waf mode
|
||||
* Activate the WAF on module activation.
|
||||
*
|
||||
* @return bool|WP_Error True if the WAF activation is successful, WP_Error otherwise.
|
||||
*/
|
||||
public static function on_activation() {
|
||||
update_option( Waf_Runner::MODE_OPTION_NAME, 'normal' );
|
||||
Waf_Runner::activate();
|
||||
( new Waf_Standalone_Bootstrap() )->generate();
|
||||
add_option( Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME, false );
|
||||
|
||||
try {
|
||||
Waf_Runner::activate();
|
||||
( new Waf_Standalone_Bootstrap() )->generate();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* On module deactivation, unset waf mode
|
||||
* Deactivate the WAF on module deactivation.
|
||||
*
|
||||
* @return bool|WP_Error True if the WAF deactivation is successful, WP_Error otherwise.
|
||||
*/
|
||||
public static function on_deactivation() {
|
||||
Waf_Runner::deactivate();
|
||||
try {
|
||||
Waf_Runner::deactivate();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,21 +126,36 @@ class Waf_Initializer {
|
||||
*
|
||||
* Updates the WAF when the "needs update" option is enabled.
|
||||
*
|
||||
* @return void
|
||||
* @return bool|WP_Error True if the WAF is up-to-date or was sucessfully updated, WP_Error if the update failed.
|
||||
*/
|
||||
public static function check_for_waf_update() {
|
||||
if ( get_option( self::NEEDS_UPDATE_OPTION_NAME ) ) {
|
||||
Waf_Runner::define_mode();
|
||||
if ( ! Waf_Runner::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
return;
|
||||
// Compatiblity patch for cases where an outdated WAF_Constants class has been
|
||||
// autoloaded by the standalone bootstrap execution at the beginning of the current request.
|
||||
if ( ! method_exists( Waf_Constants::class, 'define_mode' ) ) {
|
||||
try {
|
||||
( new Waf_Standalone_Bootstrap() )->generate();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
}
|
||||
|
||||
Waf_Runner::generate_ip_rules();
|
||||
Waf_Runner::generate_rules();
|
||||
( new Waf_Standalone_Bootstrap() )->generate();
|
||||
Waf_Constants::define_mode();
|
||||
if ( ! Waf_Runner::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
return new WP_Error( 'waf_mode_invalid', 'Invalid firewall mode.' );
|
||||
}
|
||||
|
||||
update_option( self::NEEDS_UPDATE_OPTION_NAME, 0 );
|
||||
try {
|
||||
Waf_Rules_Manager::generate_ip_rules();
|
||||
Waf_Rules_Manager::generate_rules();
|
||||
( new Waf_Standalone_Bootstrap() )->generate();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
}
|
||||
|
||||
update_option( self::NEEDS_UPDATE_OPTION_NAME, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,323 @@
|
||||
<?php
|
||||
/**
|
||||
* Class for generating and working with firewall rule files.
|
||||
*
|
||||
* @since 0.9.0
|
||||
*
|
||||
* @package automattic/jetpack-waf
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\IP\Utils as IP_Utils;
|
||||
use Jetpack_Options;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class for generating and working with firewall rule files.
|
||||
*/
|
||||
class Waf_Rules_Manager {
|
||||
|
||||
const RULES_VERSION = '1.0.0';
|
||||
|
||||
// WAF Options
|
||||
const VERSION_OPTION_NAME = 'jetpack_waf_rules_version';
|
||||
const AUTOMATIC_RULES_ENABLED_OPTION_NAME = 'jetpack_waf_automatic_rules';
|
||||
const IP_LISTS_ENABLED_OPTION_NAME = 'jetpack_waf_ip_list';
|
||||
const IP_ALLOW_LIST_OPTION_NAME = 'jetpack_waf_ip_allow_list';
|
||||
const IP_BLOCK_LIST_OPTION_NAME = 'jetpack_waf_ip_block_list';
|
||||
const RULE_LAST_UPDATED_OPTION_NAME = 'jetpack_waf_last_updated_timestamp';
|
||||
const AUTOMATIC_RULES_LAST_UPDATED_OPTION_NAME = 'jetpack_waf_automatic_rules_last_updated_timestamp';
|
||||
|
||||
// Rule Files
|
||||
const RULES_ENTRYPOINT_FILE = '/rules/rules.php';
|
||||
const AUTOMATIC_RULES_FILE = '/rules/automatic-rules.php';
|
||||
const IP_ALLOW_RULES_FILE = '/rules/allow-ip.php';
|
||||
const IP_BLOCK_RULES_FILE = '/rules/block-ip.php';
|
||||
|
||||
/**
|
||||
* Register WordPress hooks for the WAF rules.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add_hooks() {
|
||||
// Re-activate the WAF any time an option is added or updated.
|
||||
add_action( 'add_option_' . self::AUTOMATIC_RULES_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
add_action( 'update_option_' . self::AUTOMATIC_RULES_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
add_action( 'add_option_' . self::IP_LISTS_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
add_action( 'update_option_' . self::IP_LISTS_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
add_action( 'add_option_' . self::IP_ALLOW_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
add_action( 'update_option_' . self::IP_ALLOW_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
add_action( 'add_option_' . self::IP_BLOCK_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
add_action( 'update_option_' . self::IP_BLOCK_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
|
||||
// Register the cron job.
|
||||
add_action( 'jetpack_waf_rules_update_cron', array( static::class, 'update_rules_cron' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the cron job to update the WAF rules.
|
||||
*
|
||||
* @return bool|WP_Error True if the event is scheduled, WP_Error on failure.
|
||||
*/
|
||||
public static function schedule_rules_cron() {
|
||||
if ( ! wp_next_scheduled( 'jetpack_waf_rules_update_cron' ) ) {
|
||||
return wp_schedule_event( time(), 'twicedaily', 'jetpack_waf_rules_update_cron', array(), true );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries periodically to update the rules using our API.
|
||||
*
|
||||
* @return bool|WP_Error True if rules update is successful, WP_Error on failure.
|
||||
*/
|
||||
public static function update_rules_cron() {
|
||||
Waf_Constants::define_mode();
|
||||
if ( ! Waf_Runner::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
return new WP_Error( 'waf_invalid_mode', 'Invalid firewall mode.' );
|
||||
}
|
||||
|
||||
try {
|
||||
self::generate_automatic_rules();
|
||||
self::generate_ip_rules();
|
||||
self::generate_rules();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
|
||||
update_option( self::RULE_LAST_UPDATED_OPTION_NAME, time() );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-activate the WAF any time an option is added or updated.
|
||||
*
|
||||
* @return bool|WP_Error True if re-activation is successful, WP_Error on failure.
|
||||
*/
|
||||
public static function reactivate_on_rules_option_change() {
|
||||
try {
|
||||
Waf_Runner::activate();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return $e->get_wp_error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the rule set if rules version has changed
|
||||
*
|
||||
* @throws Waf_Exception If the firewall mode is invalid.
|
||||
* @throws Waf_Exception If the rules update fails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_rules_if_changed() {
|
||||
Waf_Constants::define_mode();
|
||||
if ( ! Waf_Runner::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
throw new Waf_Exception( 'Invalid firewall mode.' );
|
||||
}
|
||||
$version = get_option( self::VERSION_OPTION_NAME );
|
||||
if ( self::RULES_VERSION !== $version ) {
|
||||
self::generate_automatic_rules();
|
||||
self::generate_ip_rules();
|
||||
self::generate_rules();
|
||||
|
||||
update_option( self::VERSION_OPTION_NAME, self::RULES_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve rules from the API
|
||||
*
|
||||
* @throws Waf_Exception If site is not registered.
|
||||
* @throws Rules_API_Exception If API did not respond 200.
|
||||
* @throws Rules_API_Exception If data is missing from response.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_rules_from_api() {
|
||||
$blog_id = Jetpack_Options::get_option( 'id' );
|
||||
if ( ! $blog_id ) {
|
||||
throw new Waf_Exception( 'Site is not registered' );
|
||||
}
|
||||
|
||||
$response = Client::wpcom_json_api_request_as_blog(
|
||||
sprintf( '/sites/%s/waf-rules', $blog_id ),
|
||||
'2',
|
||||
array(),
|
||||
null,
|
||||
'wpcom'
|
||||
);
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
throw new Rules_API_Exception( 'API connection failed.', (int) $response_code );
|
||||
}
|
||||
|
||||
$rules_json = wp_remote_retrieve_body( $response );
|
||||
$rules = json_decode( $rules_json, true );
|
||||
|
||||
if ( empty( $rules['data'] ) ) {
|
||||
throw new Rules_API_Exception( 'Data missing from response.' );
|
||||
}
|
||||
|
||||
return $rules['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a require statement in a file_exists check.
|
||||
*
|
||||
* @param string $required_file The file to check if exists and require.
|
||||
* @param string $return_code The PHP code to execute if the file require returns true. Defaults to 'return;'.
|
||||
*
|
||||
* @return string The wrapped require statement.
|
||||
*/
|
||||
private static function wrap_require( $required_file, $return_code = 'return;' ) {
|
||||
return "if ( file_exists( '$required_file' ) ) { if ( require( '$required_file' ) ) { $return_code } }";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the rules.php script
|
||||
*
|
||||
* @global \WP_Filesystem_Base $wp_filesystem WordPress filesystem abstraction.
|
||||
*
|
||||
* @throws File_System_Exception If file writing fails initializing rule files.
|
||||
* @throws File_System_Exception If file writing fails writing to the rules entrypoint file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function generate_rules() {
|
||||
global $wp_filesystem;
|
||||
Waf_Runner::initialize_filesystem();
|
||||
|
||||
$rules = "<?php\n";
|
||||
$entrypoint_file_path = Waf_Runner::get_waf_file_path( self::RULES_ENTRYPOINT_FILE );
|
||||
|
||||
// Ensure that the folder exists
|
||||
if ( ! $wp_filesystem->is_dir( dirname( $entrypoint_file_path ) ) ) {
|
||||
$wp_filesystem->mkdir( dirname( $entrypoint_file_path ) );
|
||||
}
|
||||
|
||||
// Ensure all potentially required rule files exist
|
||||
$rule_files = array( self::RULES_ENTRYPOINT_FILE, self::AUTOMATIC_RULES_FILE, self::IP_ALLOW_RULES_FILE, self::IP_BLOCK_RULES_FILE );
|
||||
foreach ( $rule_files as $rule_file ) {
|
||||
$rule_file = Waf_Runner::get_waf_file_path( $rule_file );
|
||||
if ( ! $wp_filesystem->is_file( $rule_file ) ) {
|
||||
if ( ! $wp_filesystem->put_contents( $rule_file, "<?php\n" ) ) {
|
||||
throw new File_System_Exception( 'Failed writing rules file to: ' . $rule_file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add manual rules
|
||||
if ( get_option( self::IP_LISTS_ENABLED_OPTION_NAME ) ) {
|
||||
$rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::IP_ALLOW_RULES_FILE ) ) . "\n";
|
||||
$rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::IP_BLOCK_RULES_FILE ), "return \$waf->block( 'block', -1, 'ip block list' );" ) . "\n";
|
||||
}
|
||||
|
||||
// Add automatic rules
|
||||
if ( get_option( self::AUTOMATIC_RULES_ENABLED_OPTION_NAME ) ) {
|
||||
$rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::AUTOMATIC_RULES_FILE ) ) . "\n";
|
||||
}
|
||||
|
||||
// Update the rules file
|
||||
if ( ! $wp_filesystem->put_contents( $entrypoint_file_path, $rules ) ) {
|
||||
throw new File_System_Exception( 'Failed writing rules file to: ' . $entrypoint_file_path );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the automatic-rules.php script
|
||||
*
|
||||
* @global \WP_Filesystem_Base $wp_filesystem WordPress filesystem abstraction.
|
||||
*
|
||||
* @throws Waf_Exception If rules cannot be fetched from the API.
|
||||
* @throws File_System_Exception If file writing fails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function generate_automatic_rules() {
|
||||
global $wp_filesystem;
|
||||
Waf_Runner::initialize_filesystem();
|
||||
|
||||
$automatic_rules_file_path = Waf_Runner::get_waf_file_path( self::AUTOMATIC_RULES_FILE );
|
||||
|
||||
// Ensure that the folder exists.
|
||||
if ( ! $wp_filesystem->is_dir( dirname( $automatic_rules_file_path ) ) ) {
|
||||
$wp_filesystem->mkdir( dirname( $automatic_rules_file_path ) );
|
||||
}
|
||||
|
||||
try {
|
||||
$rules = self::get_rules_from_api();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
// Do not throw API exceptions for users who do not have access
|
||||
if ( 401 !== $e->getCode() ) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no rules available, don't overwrite the existing file.
|
||||
if ( empty( $rules ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $automatic_rules_file_path, $rules ) ) {
|
||||
throw new File_System_Exception( 'Failed writing automatic rules file to: ' . $automatic_rules_file_path );
|
||||
}
|
||||
|
||||
update_option( self::AUTOMATIC_RULES_LAST_UPDATED_OPTION_NAME, time() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the rules.php script
|
||||
*
|
||||
* @global \WP_Filesystem_Base $wp_filesystem WordPress filesystem abstraction.
|
||||
*
|
||||
* @throws File_System_Exception If writing to IP allow list file fails.
|
||||
* @throws File_System_Exception If writing to IP block list file fails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function generate_ip_rules() {
|
||||
global $wp_filesystem;
|
||||
Waf_Runner::initialize_filesystem();
|
||||
|
||||
$allow_ip_file_path = Waf_Runner::get_waf_file_path( self::IP_ALLOW_RULES_FILE );
|
||||
$block_ip_file_path = Waf_Runner::get_waf_file_path( self::IP_BLOCK_RULES_FILE );
|
||||
|
||||
// Ensure that the folders exists.
|
||||
if ( ! $wp_filesystem->is_dir( dirname( $allow_ip_file_path ) ) ) {
|
||||
$wp_filesystem->mkdir( dirname( $allow_ip_file_path ) );
|
||||
}
|
||||
if ( ! $wp_filesystem->is_dir( dirname( $block_ip_file_path ) ) ) {
|
||||
$wp_filesystem->mkdir( dirname( $block_ip_file_path ) );
|
||||
}
|
||||
|
||||
$allow_list = IP_Utils::get_ip_addresses_from_string( get_option( self::IP_ALLOW_LIST_OPTION_NAME ) );
|
||||
$block_list = IP_Utils::get_ip_addresses_from_string( get_option( self::IP_BLOCK_LIST_OPTION_NAME ) );
|
||||
|
||||
$allow_rules_content = '';
|
||||
// phpcs:disable WordPress.PHP.DevelopmentFunctions
|
||||
$allow_rules_content .= '$waf_allow_list = ' . var_export( $allow_list, true ) . ";\n";
|
||||
// phpcs:enable
|
||||
$allow_rules_content .= 'return $waf->is_ip_in_array( $waf_allow_list );' . "\n";
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $allow_ip_file_path, "<?php\n$allow_rules_content" ) ) {
|
||||
throw new File_System_Exception( 'Failed writing allow list file to: ' . $allow_ip_file_path );
|
||||
}
|
||||
|
||||
$block_rules_content = '';
|
||||
// phpcs:disable WordPress.PHP.DevelopmentFunctions
|
||||
$block_rules_content .= '$waf_block_list = ' . var_export( $block_list, true ) . ";\n";
|
||||
// phpcs:enable
|
||||
$block_rules_content .= 'return $waf->is_ip_in_array( $waf_block_list );' . "\n";
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $block_ip_file_path, "<?php\n$block_rules_content" ) ) {
|
||||
throw new File_System_Exception( 'Failed writing block list file to: ' . $block_ip_file_path );
|
||||
}
|
||||
}
|
||||
}
|
@ -7,44 +7,37 @@
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Modules;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
use Jetpack_Options;
|
||||
|
||||
/**
|
||||
* Executes the WAF.
|
||||
*/
|
||||
class Waf_Runner {
|
||||
|
||||
const WAF_MODULE_NAME = 'waf';
|
||||
const WAF_RULES_VERSION = '1.0.0';
|
||||
const MODE_OPTION_NAME = 'jetpack_waf_mode';
|
||||
const IP_LISTS_ENABLED_OPTION_NAME = 'jetpack_waf_ip_list';
|
||||
const IP_ALLOW_LIST_OPTION_NAME = 'jetpack_waf_ip_allow_list';
|
||||
const IP_BLOCK_LIST_OPTION_NAME = 'jetpack_waf_ip_block_list';
|
||||
const RULES_FILE = '/rules/rules.php';
|
||||
const ALLOW_IP_FILE = '/rules/allow-ip.php';
|
||||
const BLOCK_IP_FILE = '/rules/block-ip.php';
|
||||
const VERSION_OPTION_NAME = 'jetpack_waf_rules_version';
|
||||
const RULE_LAST_UPDATED_OPTION_NAME = 'jetpack_waf_last_updated_timestamp';
|
||||
const SHARE_DATA_OPTION_NAME = 'jetpack_waf_share_data';
|
||||
const WAF_MODULE_NAME = 'waf';
|
||||
const MODE_OPTION_NAME = 'jetpack_waf_mode';
|
||||
const SHARE_DATA_OPTION_NAME = 'jetpack_waf_share_data';
|
||||
|
||||
/**
|
||||
* Run the WAF
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function initialize() {
|
||||
if ( ! self::is_enabled() ) {
|
||||
return;
|
||||
}
|
||||
self::define_mode();
|
||||
self::define_share_data();
|
||||
Waf_Constants::define_mode();
|
||||
Waf_Constants::define_share_data();
|
||||
if ( ! self::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
return;
|
||||
}
|
||||
// Don't run if in standalone mode
|
||||
if ( function_exists( 'add_action' ) ) {
|
||||
self::add_hooks();
|
||||
Waf_Rules_Manager::add_hooks();
|
||||
Waf_Rules_Manager::schedule_rules_cron();
|
||||
}
|
||||
if ( ! self::did_run() ) {
|
||||
self::run();
|
||||
@ -57,42 +50,10 @@ class Waf_Runner {
|
||||
* @return void
|
||||
*/
|
||||
public static function add_hooks() {
|
||||
add_action( 'update_option_' . self::IP_ALLOW_LIST_OPTION_NAME, array( static::class, 'activate' ), 10, 0 );
|
||||
add_action( 'update_option_' . self::IP_BLOCK_LIST_OPTION_NAME, array( static::class, 'activate' ), 10, 0 );
|
||||
add_action( 'update_option_' . self::IP_LISTS_ENABLED_OPTION_NAME, array( static::class, 'activate' ), 10, 0 );
|
||||
add_action( 'jetpack_waf_rules_update_cron', array( static::class, 'update_rules_cron' ) );
|
||||
// TODO: This doesn't exactly fit here - may need to find another home
|
||||
if ( ! wp_next_scheduled( 'jetpack_waf_rules_update_cron' ) ) {
|
||||
wp_schedule_event( time(), 'twicedaily', 'jetpack_waf_rules_update_cron' );
|
||||
}
|
||||
// Register REST routes.
|
||||
add_action( 'rest_api_init', array( new REST_Controller(), 'register_rest_routes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode definition if it has not been set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_mode() {
|
||||
if ( ! defined( 'JETPACK_WAF_MODE' ) ) {
|
||||
$mode_option = get_option( self::MODE_OPTION_NAME );
|
||||
define( 'JETPACK_WAF_MODE', $mode_option );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode definition if it has not been set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_share_data() {
|
||||
if ( ! defined( 'JETPACK_WAF_SHARE_DATA' ) ) {
|
||||
$share_data_option = get_option( self::SHARE_DATA_OPTION_NAME, false );
|
||||
define( 'JETPACK_WAF_SHARE_DATA', $share_data_option );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Did the WAF run yet or not?
|
||||
*
|
||||
@ -144,6 +105,11 @@ class Waf_Runner {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do not run on the VIP platform
|
||||
if ( ( new Host() )->is_vip_site() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -164,6 +130,8 @@ class Waf_Runner {
|
||||
|
||||
/**
|
||||
* Enables the WAF module on the site.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function enable() {
|
||||
return ( new Modules() )->activate( self::WAF_MODULE_NAME, false, false );
|
||||
@ -171,6 +139,8 @@ class Waf_Runner {
|
||||
|
||||
/**
|
||||
* Disabled the WAF module on the site.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function disable() {
|
||||
return ( new Modules() )->deactivate( self::WAF_MODULE_NAME );
|
||||
@ -183,11 +153,13 @@ class Waf_Runner {
|
||||
*/
|
||||
public static function get_config() {
|
||||
return array(
|
||||
self::IP_LISTS_ENABLED_OPTION_NAME => get_option( self::IP_LISTS_ENABLED_OPTION_NAME ),
|
||||
self::IP_ALLOW_LIST_OPTION_NAME => get_option( self::IP_ALLOW_LIST_OPTION_NAME ),
|
||||
self::IP_BLOCK_LIST_OPTION_NAME => get_option( self::IP_BLOCK_LIST_OPTION_NAME ),
|
||||
self::SHARE_DATA_OPTION_NAME => get_option( self::SHARE_DATA_OPTION_NAME ),
|
||||
'bootstrap_path' => self::get_bootstrap_file_path(),
|
||||
Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME => get_option( Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME ),
|
||||
Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME => get_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME ),
|
||||
Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME => get_option( Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME ),
|
||||
Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME => get_option( Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME ),
|
||||
self::SHARE_DATA_OPTION_NAME => get_option( self::SHARE_DATA_OPTION_NAME ),
|
||||
'bootstrap_path' => self::get_bootstrap_file_path(),
|
||||
'automatic_rules_available' => (bool) self::automatic_rules_available(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -208,7 +180,7 @@ class Waf_Runner {
|
||||
* @return string The full file path to the provided file in the WAF directory.
|
||||
*/
|
||||
public static function get_waf_file_path( $file ) {
|
||||
Waf_Constants::initialize_constants();
|
||||
Waf_Constants::define_waf_directory();
|
||||
|
||||
// Ensure the file path starts with a slash.
|
||||
if ( '/' !== substr( $file, 0, 1 ) ) {
|
||||
@ -255,12 +227,12 @@ class Waf_Runner {
|
||||
$waf = new Waf_Runtime( new Waf_Transforms(), new Waf_Operators() );
|
||||
|
||||
// execute waf rules.
|
||||
$rules_file_path = self::get_waf_file_path( self::RULES_FILE );
|
||||
$rules_file_path = self::get_waf_file_path( Waf_Rules_Manager::RULES_ENTRYPOINT_FILE );
|
||||
if ( file_exists( $rules_file_path ) ) {
|
||||
// phpcs:ignore
|
||||
include $rules_file_path;
|
||||
}
|
||||
} catch ( \Exception $err ) { // phpcs:ignore
|
||||
} catch ( \Exception $err ) { // phpcs:ignore
|
||||
// Intentionally doing nothing.
|
||||
}
|
||||
|
||||
@ -284,10 +256,11 @@ class Waf_Runner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the WP filesystem.
|
||||
* Initializes the WP filesystem and WAF directory structure.
|
||||
*
|
||||
* @throws File_System_Exception If filesystem is unavailable.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception If filesystem is unavailable.
|
||||
*/
|
||||
public static function initialize_filesystem() {
|
||||
if ( ! function_exists( '\\WP_Filesystem' ) ) {
|
||||
@ -295,52 +268,62 @@ class Waf_Runner {
|
||||
}
|
||||
|
||||
if ( ! \WP_Filesystem() ) {
|
||||
throw new \Exception( 'No filesystem available.' );
|
||||
throw new File_System_Exception( 'No filesystem available.' );
|
||||
}
|
||||
|
||||
self::initialize_waf_directory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the WAF by generating the rules script and setting the version
|
||||
*
|
||||
* @throws Waf_Exception If the firewall mode is invalid.
|
||||
* @throws Waf_Exception If the activation fails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function activate() {
|
||||
self::define_mode();
|
||||
Waf_Constants::define_mode();
|
||||
if ( ! self::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
return;
|
||||
throw new Waf_Exception( 'Invalid firewall mode.' );
|
||||
}
|
||||
$version = get_option( self::VERSION_OPTION_NAME );
|
||||
|
||||
$version = get_option( Waf_Rules_Manager::VERSION_OPTION_NAME );
|
||||
if ( ! $version ) {
|
||||
add_option( self::VERSION_OPTION_NAME, self::WAF_RULES_VERSION );
|
||||
add_option( Waf_Rules_Manager::VERSION_OPTION_NAME, Waf_Rules_Manager::RULES_VERSION );
|
||||
}
|
||||
|
||||
add_option( self::SHARE_DATA_OPTION_NAME, true );
|
||||
|
||||
self::initialize_filesystem();
|
||||
self::create_waf_directory();
|
||||
self::generate_ip_rules();
|
||||
|
||||
Waf_Rules_Manager::generate_automatic_rules();
|
||||
Waf_Rules_Manager::generate_ip_rules();
|
||||
Waf_Rules_Manager::generate_rules();
|
||||
|
||||
self::create_blocklog_table();
|
||||
self::generate_rules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Created the waf directory on activation.
|
||||
* Ensures that the waf directory is created.
|
||||
*
|
||||
* @throws File_System_Exception If filesystem is unavailable.
|
||||
* @throws File_System_Exception If creating the directory fails.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception In case there's a problem when creating the directory.
|
||||
*/
|
||||
public static function create_waf_directory() {
|
||||
public static function initialize_waf_directory() {
|
||||
WP_Filesystem();
|
||||
Waf_Constants::initialize_constants();
|
||||
Waf_Constants::define_waf_directory();
|
||||
|
||||
global $wp_filesystem;
|
||||
if ( ! $wp_filesystem ) {
|
||||
throw new \Exception( 'Can not work without the file system being initialized.' );
|
||||
throw new File_System_Exception( 'Can not work without the file system being initialized.' );
|
||||
}
|
||||
|
||||
if ( ! $wp_filesystem->is_dir( JETPACK_WAF_DIR ) ) {
|
||||
if ( ! $wp_filesystem->mkdir( JETPACK_WAF_DIR ) ) {
|
||||
throw new \Exception( 'Failed creating WAF standalone bootstrap file directory: ' . JETPACK_WAF_DIR );
|
||||
throw new File_System_Exception( 'Failed creating WAF file directory: ' . JETPACK_WAF_DIR );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -372,241 +355,81 @@ class Waf_Runner {
|
||||
/**
|
||||
* Deactivates the WAF by deleting the relevant options and emptying rules file.
|
||||
*
|
||||
* @throws File_System_Exception If file writing fails.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception If file writing fails.
|
||||
*/
|
||||
public static function deactivate() {
|
||||
delete_option( self::MODE_OPTION_NAME );
|
||||
delete_option( self::VERSION_OPTION_NAME );
|
||||
delete_option( Waf_Rules_Manager::VERSION_OPTION_NAME );
|
||||
|
||||
global $wp_filesystem;
|
||||
|
||||
self::initialize_filesystem();
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( self::get_waf_file_path( self::RULES_FILE ), "<?php\n" ) ) {
|
||||
throw new \Exception( 'Failed to empty rules.php file.' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries periodically to update the rules using our API.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_rules_cron() {
|
||||
self::define_mode();
|
||||
if ( ! self::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
// If the rules file doesn't exist, there's nothing else to do.
|
||||
if ( ! $wp_filesystem->exists( self::get_waf_file_path( Waf_Rules_Manager::RULES_ENTRYPOINT_FILE ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::generate_ip_rules();
|
||||
self::generate_rules();
|
||||
update_option( self::RULE_LAST_UPDATED_OPTION_NAME, time() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the rule set if rules version has changed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_rules_if_changed() {
|
||||
self::define_mode();
|
||||
if ( ! self::is_allowed_mode( JETPACK_WAF_MODE ) ) {
|
||||
return;
|
||||
}
|
||||
$version = get_option( self::VERSION_OPTION_NAME );
|
||||
if ( self::WAF_RULES_VERSION !== $version ) {
|
||||
update_option( self::VERSION_OPTION_NAME, self::WAF_RULES_VERSION );
|
||||
self::generate_ip_rules();
|
||||
self::generate_rules();
|
||||
// Empty the rules entrypoint file.
|
||||
if ( ! $wp_filesystem->put_contents( self::get_waf_file_path( Waf_Rules_Manager::RULES_ENTRYPOINT_FILE ), "<?php\n" ) ) {
|
||||
throw new File_System_Exception( 'Failed to empty rules.php file.' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle updates to the WAF
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_waf() {
|
||||
self::update_rules_if_changed();
|
||||
Waf_Rules_Manager::update_rules_if_changed();
|
||||
|
||||
// Re-generate the standalone bootstrap file on every update
|
||||
// TODO: We may consider only doing this when the WAF version changes
|
||||
( new Waf_Standalone_Bootstrap() )->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve rules from the API
|
||||
* Check if an automatic rules file is available
|
||||
*
|
||||
* @throws \Exception If site is not registered.
|
||||
* @throws \Exception If API did not respond 200.
|
||||
* @throws \Exception If data is missing from response.
|
||||
* @return array
|
||||
* @return bool False if an automatic rules file is not available, true otherwise
|
||||
*/
|
||||
public static function get_rules_from_api() {
|
||||
$blog_id = Jetpack_Options::get_option( 'id' );
|
||||
if ( ! $blog_id ) {
|
||||
throw new \Exception( 'Site is not registered' );
|
||||
public static function automatic_rules_available() {
|
||||
$automatic_rules_last_updated = get_option( Waf_Rules_Manager::AUTOMATIC_RULES_LAST_UPDATED_OPTION_NAME );
|
||||
|
||||
// If we do not have a automatic rules last updated timestamp cached, return false.
|
||||
if ( ! $automatic_rules_last_updated ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response = Client::wpcom_json_api_request_as_blog(
|
||||
sprintf( '/sites/%s/waf-rules', $blog_id ),
|
||||
'2',
|
||||
array(),
|
||||
null,
|
||||
'wpcom'
|
||||
);
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
throw new \Exception( 'API connection failed.', $response_code );
|
||||
}
|
||||
|
||||
$rules_json = wp_remote_retrieve_body( $response );
|
||||
$rules = json_decode( $rules_json, true );
|
||||
|
||||
if ( empty( $rules['data'] ) ) {
|
||||
throw new \Exception( 'Data missing from response.' );
|
||||
}
|
||||
|
||||
return $rules['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the rules.php script
|
||||
*
|
||||
* @throws \Exception If file writing fails.
|
||||
* @return void
|
||||
*/
|
||||
public static function generate_rules() {
|
||||
/**
|
||||
* WordPress filesystem abstraction.
|
||||
*
|
||||
* @var \WP_Filesystem_Base $wp_filesystem
|
||||
*/
|
||||
// Validate that the automatic rules file exists and is not empty.
|
||||
global $wp_filesystem;
|
||||
|
||||
self::initialize_filesystem();
|
||||
|
||||
$rules_file_path = self::get_waf_file_path( self::RULES_FILE );
|
||||
|
||||
$api_exception = null;
|
||||
$throw_api_exception = true;
|
||||
try {
|
||||
$rules = self::get_rules_from_api();
|
||||
} catch ( \Exception $e ) {
|
||||
if ( 401 === $e->getCode() ) {
|
||||
// do not throw API exceptions for users who do not have access
|
||||
$throw_api_exception = false;
|
||||
self::initialize_filesystem();
|
||||
} catch ( Waf_Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$automatic_rules_file_contents = $wp_filesystem->get_contents( self::get_waf_file_path( Waf_Rules_Manager::AUTOMATIC_RULES_FILE ) );
|
||||
|
||||
// If the automatic rules file was removed or is now empty, return false.
|
||||
if ( ! $automatic_rules_file_contents || "<?php\n" === $automatic_rules_file_contents ) {
|
||||
|
||||
// Delete the automatic rules last updated option.
|
||||
delete_option( Waf_Rules_Manager::AUTOMATIC_RULES_LAST_UPDATED_OPTION_NAME );
|
||||
|
||||
$automatic_rules_enabled = get_option( Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME );
|
||||
|
||||
// If automatic rules setting is enabled, disable it.
|
||||
if ( $automatic_rules_enabled ) {
|
||||
update_option( Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME, false );
|
||||
}
|
||||
|
||||
if ( $wp_filesystem->exists( $rules_file_path ) && $throw_api_exception ) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$rules = "<?php\n";
|
||||
$api_exception = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that the folder exists.
|
||||
if ( ! $wp_filesystem->is_dir( dirname( $rules_file_path ) ) ) {
|
||||
$wp_filesystem->mkdir( dirname( $rules_file_path ) );
|
||||
}
|
||||
|
||||
$ip_allow_rules = self::get_waf_file_path( self::ALLOW_IP_FILE );
|
||||
$ip_block_rules = self::get_waf_file_path( self::BLOCK_IP_FILE );
|
||||
|
||||
$ip_list_code = "if ( file_exists( '$ip_allow_rules' ) ) { if ( require( '$ip_allow_rules' ) ) { return; } }\n" .
|
||||
"if ( file_exists( '$ip_block_rules' ) ) { if ( require( '$ip_block_rules' ) ) { return \$waf->block('block', -1, 'ip block list'); } }\n";
|
||||
|
||||
$rules_divided_by_line = explode( "\n", $rules );
|
||||
array_splice( $rules_divided_by_line, 1, 0, $ip_list_code );
|
||||
|
||||
$rules = implode( "\n", $rules_divided_by_line );
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $rules_file_path, $rules ) ) {
|
||||
throw new \Exception( 'Failed writing rules file to: ' . $rules_file_path );
|
||||
}
|
||||
|
||||
if ( null !== $api_exception && $throw_api_exception ) {
|
||||
throw $api_exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We allow for both, one IP per line or comma-; semicolon; or whitespace-separated lists. This also validates the IP addresses
|
||||
* and only returns the ones that look valid.
|
||||
*
|
||||
* @param string $ips List of ips - example: "8.8.8.8\n4.4.4.4,2.2.2.2;1.1.1.1 9.9.9.9,5555.5555.5555.5555".
|
||||
* @return array List of valid IP addresses. - example based on input example: array('8.8.8.8', '4.4.4.4', '2.2.2.2', '1.1.1.1', '9.9.9.9')
|
||||
*/
|
||||
private static function ip_option_to_array( $ips ) {
|
||||
$ips = (string) $ips;
|
||||
$ips = preg_split( '/[\s,;]/', $ips );
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ( $ips as $ip ) {
|
||||
if ( filter_var( $ip, FILTER_VALIDATE_IP ) !== false ) {
|
||||
$result[] = $ip;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the rules.php script
|
||||
*
|
||||
* @throws \Exception If filesystem is not available.
|
||||
* @throws \Exception If file writing fails.
|
||||
* @return void
|
||||
*/
|
||||
public static function generate_ip_rules() {
|
||||
/**
|
||||
* WordPress filesystem abstraction.
|
||||
*
|
||||
* @var \WP_Filesystem_Base $wp_filesystem
|
||||
*/
|
||||
global $wp_filesystem;
|
||||
|
||||
self::initialize_filesystem();
|
||||
|
||||
$rules_file_path = self::get_waf_file_path( self::RULES_FILE );
|
||||
$allow_ip_file_path = self::get_waf_file_path( self::ALLOW_IP_FILE );
|
||||
$block_ip_file_path = self::get_waf_file_path( self::BLOCK_IP_FILE );
|
||||
|
||||
// Ensure that the folder exists.
|
||||
if ( ! $wp_filesystem->is_dir( dirname( $rules_file_path ) ) ) {
|
||||
$wp_filesystem->mkdir( dirname( $rules_file_path ) );
|
||||
}
|
||||
|
||||
$allow_list = self::ip_option_to_array( get_option( self::IP_ALLOW_LIST_OPTION_NAME ) );
|
||||
$block_list = self::ip_option_to_array( get_option( self::IP_BLOCK_LIST_OPTION_NAME ) );
|
||||
|
||||
$lists_enabled = (bool) get_option( self::IP_LISTS_ENABLED_OPTION_NAME );
|
||||
if ( false === $lists_enabled ) {
|
||||
// Making the lists empty effectively disabled the feature while still keeping the other WAF rules evaluation active.
|
||||
$allow_list = array();
|
||||
$block_list = array();
|
||||
}
|
||||
|
||||
$allow_rules_content = '';
|
||||
// phpcs:disable WordPress.PHP.DevelopmentFunctions
|
||||
$allow_rules_content .= '$waf_allow_list = ' . var_export( $allow_list, true ) . ";\n";
|
||||
// phpcs:enable
|
||||
$allow_rules_content .= 'return $waf->is_ip_in_array( $waf_allow_list );' . "\n";
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $allow_ip_file_path, "<?php\n$allow_rules_content" ) ) {
|
||||
throw new \Exception( 'Failed writing allow list file to: ' . $allow_ip_file_path );
|
||||
}
|
||||
|
||||
$block_rules_content = '';
|
||||
// phpcs:disable WordPress.PHP.DevelopmentFunctions
|
||||
$block_rules_content .= '$waf_block_list = ' . var_export( $block_list, true ) . ";\n";
|
||||
// phpcs:enable
|
||||
$block_rules_content .= 'return $waf->is_ip_in_array( $waf_block_list );' . "\n";
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $block_ip_file_path, "<?php\n$block_rules_content" ) ) {
|
||||
throw new \Exception( 'Failed writing block list file to: ' . $block_ip_file_path );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
use Automattic\Jetpack\IP\Utils as IP_Utils;
|
||||
|
||||
require_once __DIR__ . '/functions.php';
|
||||
|
||||
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This class is all about sanitizing input.
|
||||
@ -663,14 +665,32 @@ class Waf_Runtime {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies is ip from request is in an array.
|
||||
* Verifies if the IP from the current request is in an array.
|
||||
*
|
||||
* @param array $array Array to verify ip against.
|
||||
* @param array $array Array of IP addresses to verify the request IP against.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ip_in_array( $array ) {
|
||||
$real_ip = $this->request->get_real_user_ip_address();
|
||||
$real_ip = $this->request->get_real_user_ip_address();
|
||||
$array_length = count( $array );
|
||||
|
||||
return in_array( $real_ip, $array, true );
|
||||
for ( $i = 0; $i < $array_length; $i++ ) {
|
||||
// Check if the IP matches a provided range.
|
||||
$range = explode( '-', $array[ $i ] );
|
||||
if ( count( $range ) === 2 ) {
|
||||
if ( IP_Utils::ip_address_is_in_range( $real_ip, $range[0], $range[1] ) ) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the IP is an exact match.
|
||||
if ( $real_ip === $array[ $i ] ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,6 @@
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Handles the bootstrap.
|
||||
@ -17,6 +16,8 @@ class Waf_Standalone_Bootstrap {
|
||||
|
||||
/**
|
||||
* Ensures that constants are initialized if this class is used.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->guard_against_missing_abspath();
|
||||
@ -26,13 +27,14 @@ class Waf_Standalone_Bootstrap {
|
||||
/**
|
||||
* Ensures that this class is not used unless we are in the right context.
|
||||
*
|
||||
* @throws Waf_Exception If we are outside of WordPress.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception If we are outside of WordPress.
|
||||
*/
|
||||
private function guard_against_missing_abspath() {
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
throw new Exception( 'Cannot generate the WAF bootstrap if we are not running in WordPress context.' );
|
||||
throw new Waf_Exception( 'Cannot generate the WAF bootstrap if we are not running in WordPress context.' );
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,8 +67,9 @@ class Waf_Standalone_Bootstrap {
|
||||
/**
|
||||
* Finds the path to the autoloader, which can then be used to require the autoloader in the generated boostrap file.
|
||||
*
|
||||
* @throws Waf_Exception In case the autoloader file can not be found.
|
||||
*
|
||||
* @return string|null
|
||||
* @throws Exception In case the autoloader file can not be found.
|
||||
*/
|
||||
private function locate_autoloader_file() {
|
||||
global $jetpack_autoloader_loader;
|
||||
@ -102,7 +105,7 @@ class Waf_Standalone_Bootstrap {
|
||||
|
||||
// Check that the determined file actually exists.
|
||||
if ( ! file_exists( $autoload_file ) ) {
|
||||
throw new Exception( 'Can not find autoloader, and the WAF standalone boostrap will not work without it.' );
|
||||
throw new Waf_Exception( 'Can not find autoloader, and the WAF standalone boostrap will not work without it.' );
|
||||
}
|
||||
|
||||
return $autoload_file;
|
||||
@ -120,8 +123,11 @@ class Waf_Standalone_Bootstrap {
|
||||
/**
|
||||
* Generates the bootstrap file.
|
||||
*
|
||||
* @throws File_System_Exception If the filesystem is not available.
|
||||
* @throws File_System_Exception If the WAF directory can not be created.
|
||||
* @throws File_System_Exception If the bootstrap file can not be created.
|
||||
*
|
||||
* @return string Absolute path to the bootstrap file.
|
||||
* @throws Exception In case the file can not be written.
|
||||
*/
|
||||
public function generate() {
|
||||
|
||||
@ -129,9 +135,11 @@ class Waf_Standalone_Bootstrap {
|
||||
|
||||
global $wp_filesystem;
|
||||
if ( ! $wp_filesystem ) {
|
||||
throw new Exception( 'Can not work without the file system being initialized.' );
|
||||
throw new File_System_Exception( 'Can not work without the file system being initialized.' );
|
||||
}
|
||||
|
||||
$autoloader_file = $this->locate_autoloader_file();
|
||||
|
||||
$bootstrap_file = $this->get_bootstrap_file_path();
|
||||
$mode_option = get_option( Waf_Runner::MODE_OPTION_NAME, false );
|
||||
$share_data_option = get_option( Waf_Runner::SHARE_DATA_OPTION_NAME, false );
|
||||
@ -144,18 +152,18 @@ class Waf_Standalone_Bootstrap {
|
||||
. sprintf( "define( 'JETPACK_WAF_SHARE_DATA', %s );\n", var_export( $share_data_option, true ) )
|
||||
. sprintf( "define( 'JETPACK_WAF_DIR', %s );\n", var_export( JETPACK_WAF_DIR, true ) )
|
||||
. sprintf( "define( 'JETPACK_WAF_WPCONFIG', %s );\n", var_export( JETPACK_WAF_WPCONFIG, true ) )
|
||||
. 'require_once ' . var_export( $this->locate_autoloader_file(), true ) . ";\n"
|
||||
. 'require_once ' . var_export( $autoloader_file, true ) . ";\n"
|
||||
. "Automattic\Jetpack\Waf\Waf_Runner::initialize();\n";
|
||||
// phpcs:enable
|
||||
|
||||
if ( ! $wp_filesystem->is_dir( JETPACK_WAF_DIR ) ) {
|
||||
if ( ! $wp_filesystem->mkdir( JETPACK_WAF_DIR ) ) {
|
||||
throw new Exception( 'Failed creating WAF standalone bootstrap file directory: ' . JETPACK_WAF_DIR );
|
||||
throw new File_System_Exception( 'Failed creating WAF standalone bootstrap file directory: ' . JETPACK_WAF_DIR );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $bootstrap_file, $code ) ) {
|
||||
throw new Exception( 'Failed writing WAF standalone bootstrap file to: ' . $bootstrap_file );
|
||||
throw new File_System_Exception( 'Failed writing WAF standalone bootstrap file to: ' . $bootstrap_file );
|
||||
}
|
||||
|
||||
return $bootstrap_file;
|
||||
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* Class used to retrieve WAF stats
|
||||
*
|
||||
* @package automattic/jetpack-waf
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
use Automattic\Jetpack\IP\Utils as IP_Utils;
|
||||
|
||||
/**
|
||||
* Retrieves WAF stats.
|
||||
*/
|
||||
class Waf_Stats {
|
||||
|
||||
/**
|
||||
* Get IP allow list count
|
||||
*
|
||||
* @return int The number of valid IP addresses in the allow list
|
||||
*/
|
||||
public static function get_ip_allow_list_count() {
|
||||
$ip_allow_list = get_option( Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME );
|
||||
|
||||
if ( ! $ip_allow_list ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$results = IP_Utils::get_ip_addresses_from_string( $ip_allow_list );
|
||||
|
||||
return count( $results );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IP block list count
|
||||
*
|
||||
* @return int The number of valid IP addresses in the block list
|
||||
*/
|
||||
public static function get_ip_block_list_count() {
|
||||
$ip_block_list = get_option( Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME );
|
||||
|
||||
if ( ! $ip_block_list ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$results = IP_Utils::get_ip_addresses_from_string( $ip_block_list );
|
||||
|
||||
return count( $results );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Rules version
|
||||
*
|
||||
* @return bool|string False if value is not found. The current stored rules version if cache is found.
|
||||
*/
|
||||
public static function get_rules_version() {
|
||||
return get_option( Waf_Rules_Manager::VERSION_OPTION_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Automatic Rules last updated timestamp
|
||||
*
|
||||
* @return bool|string False if value is not found. The timestamp the current stored rules was last updated if cache is found.
|
||||
*/
|
||||
public static function get_automatic_rules_last_updated() {
|
||||
return get_option( Waf_Rules_Manager::AUTOMATIC_RULES_LAST_UPDATED_OPTION_NAME );
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* File system exception.
|
||||
*
|
||||
* @since 0.10.1
|
||||
*
|
||||
* @package automattic/jetpack-waf
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
/**
|
||||
* Custom exception for WAF file system errors.
|
||||
*/
|
||||
class File_System_Exception extends Waf_Exception {
|
||||
|
||||
/**
|
||||
* Error slug which maps to WP_Error::$code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'file_system_error';
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Rules API exception.
|
||||
*
|
||||
* @since 0.10.1
|
||||
*
|
||||
* @package automattic/jetpack-waf
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
/**
|
||||
* Custom exception for WAF rules API errors.
|
||||
*/
|
||||
class Rules_API_Exception extends Waf_Exception {
|
||||
|
||||
/**
|
||||
* Error slug which maps to WP_Error::$code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'rules_api_error';
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Base custom exception for the WAF package.
|
||||
*
|
||||
* @since 0.10.1
|
||||
*
|
||||
* @package automattic/jetpack-waf
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Waf;
|
||||
|
||||
use Exception;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* WAF exception.
|
||||
*/
|
||||
class Waf_Exception extends Exception {
|
||||
|
||||
/**
|
||||
* Error slug which maps to WP_Error::$code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'waf_error';
|
||||
|
||||
/**
|
||||
* Convert the exception into a WP_Error object.
|
||||
*
|
||||
* @return WP_Error
|
||||
*/
|
||||
public function get_wp_error() {
|
||||
return new WP_Error( static::SLUG, $this->getMessage() );
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user