diff --git a/projects/packages/waf/changelog/add-waf-ip-list-toggles b/projects/packages/waf/changelog/add-waf-ip-list-toggles new file mode 100644 index 0000000000000..a2863e6e289b5 --- /dev/null +++ b/projects/packages/waf/changelog/add-waf-ip-list-toggles @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Added the ability to toggle IP block and allow lists individually. diff --git a/projects/packages/waf/composer.json b/projects/packages/waf/composer.json index 783d62d4b277e..53b1049cb7c42 100644 --- a/projects/packages/waf/composer.json +++ b/projects/packages/waf/composer.json @@ -61,7 +61,7 @@ "link-template": "https://github.com/Automattic/jetpack-waf/compare/v${old}...v${new}" }, "branch-alias": { - "dev-trunk": "0.16.x-dev" + "dev-trunk": "0.17.x-dev" } }, "config": { diff --git a/projects/packages/waf/src/class-brute-force-protection.php b/projects/packages/waf/src/class-brute-force-protection.php index 08a0b12d8e255..f88c76a4b68e9 100644 --- a/projects/packages/waf/src/class-brute-force-protection.php +++ b/projects/packages/waf/src/class-brute-force-protection.php @@ -14,6 +14,7 @@ use Automattic\Jetpack\Modules; use Automattic\Jetpack\Waf\Waf_Compatibility; use Automattic\Jetpack\Waf\Waf_Constants; +use Automattic\Jetpack\Waf\Waf_Rules_Manager; use Jetpack_IXR_Client; use Jetpack_Options; use WP_Error; @@ -633,6 +634,15 @@ public function get_headers() { return $output; } + /** + * Whether or not the IP allow list is enabled. + * + * @return bool + */ + public static function ip_allow_list_enabled() { + return get_option( Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME, true ); + } + /** * Checks if the IP address is in the allow list. * @@ -658,6 +668,11 @@ public function ip_is_allowed( $ip ) { return true; } + // Allow list must be enabled. + if ( ! $this->ip_allow_list_enabled() ) { + return false; + } + $allow_list = Brute_Force_Protection_Shared_Functions::get_local_allow_list(); if ( is_multisite() ) { diff --git a/projects/packages/waf/src/class-compatibility.php b/projects/packages/waf/src/class-compatibility.php index 1adc7e94ae760..8e70fa2ec8ebd 100644 --- a/projects/packages/waf/src/class-compatibility.php +++ b/projects/packages/waf/src/class-compatibility.php @@ -28,6 +28,8 @@ public static function add_compatibility_hooks() { add_filter( 'default_option_' . Waf_Initializer::NEEDS_UPDATE_OPTION_NAME, __CLASS__ . '::default_option_waf_needs_update', 10, 3 ); add_filter( 'default_option_' . Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME, __CLASS__ . '::default_option_waf_ip_allow_list', 10, 3 ); add_filter( 'option_' . Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME, __CLASS__ . '::filter_option_waf_ip_allow_list', 10, 1 ); + add_filter( 'default_option_' . Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME, __CLASS__ . '::default_option_waf_ip_allow_list_enabled', 10, 3 ); + add_filter( 'default_option_' . Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME, __CLASS__ . '::default_option_waf_ip_block_list_enabled', 10, 3 ); } /** @@ -229,4 +231,58 @@ public static function default_option_waf_ip_allow_list( $default, $option, $pas public static function is_brute_force_running_in_jetpack() { return defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '12', '<' ); } + + /** + * Default the allow list enabled option to the value of the generic IP lists enabled option it replaced. + * + * @since $next-version$ + * + * @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_ip_allow_list_enabled( $default, $option, $passed_default ) { + // If the deprecated IP lists option was set to false, disable the allow list. + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. + $deprecated_option = get_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, true ); + if ( ! $deprecated_option ) { + return false; + } + + // If the allow list is empty, disable the allow list. + if ( ! get_option( Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME ) ) { + return false; + } + + // Allow get_option() to override this default value + if ( $passed_default ) { + return $default; + } + + // Default to enabling the allow list. + return true; + } + + /** + * Default the block list enabled option to the value of the generic IP lists enabled option it replaced. + * + * @since $next-version$ + * + * @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_ip_block_list_enabled( $default, $option, $passed_default ) { + // Allow get_option() to override this default value + if ( $passed_default ) { + return $default; + } + + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. + return get_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, false ); + } } diff --git a/projects/packages/waf/src/class-rest-controller.php b/projects/packages/waf/src/class-rest-controller.php index 30759fc2478ce..d0a873c86e7fe 100644 --- a/projects/packages/waf/src/class-rest-controller.php +++ b/projects/packages/waf/src/class-rest-controller.php @@ -107,7 +107,9 @@ public static function update_waf( $request ) { } // IP Lists Enabled + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. if ( isset( $request[ Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME ] ) ) { + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. update_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, (bool) $request->get_param( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME ) ); } @@ -115,11 +117,17 @@ public static function update_waf( $request ) { 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 ] ); } + if ( isset( $request[ Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME ] ) ) { + update_option( Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME, $request[ Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME ] ); + } // IP Allow List 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 ] ); } + if ( isset( $request[ Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME ] ) ) { + update_option( Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME, $request[ Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME ] ); + } // Share Data if ( isset( $request[ Waf_Runner::SHARE_DATA_OPTION_NAME ] ) ) { diff --git a/projects/packages/waf/src/class-waf-rules-manager.php b/projects/packages/waf/src/class-waf-rules-manager.php index 2c9f890179bf8..236d714325784 100644 --- a/projects/packages/waf/src/class-waf-rules-manager.php +++ b/projects/packages/waf/src/class-waf-rules-manager.php @@ -24,12 +24,20 @@ class Waf_Rules_Manager { // 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_ALLOW_LIST_ENABLED_OPTION_NAME = 'jetpack_waf_ip_allow_list_enabled'; const IP_BLOCK_LIST_OPTION_NAME = 'jetpack_waf_ip_block_list'; + const IP_BLOCK_LIST_ENABLED_OPTION_NAME = 'jetpack_waf_ip_block_list_enabled'; 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'; + /** + * IP Lists Enabled Option Name + * + * @deprecated $next-version$ Use Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME and Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME instead. + */ + const IP_LISTS_ENABLED_OPTION_NAME = 'jetpack_waf_ip_list'; + // Rule Files const RULES_ENTRYPOINT_FILE = '/rules/rules.php'; const AUTOMATIC_RULES_FILE = '/rules/automatic-rules.php'; @@ -45,11 +53,15 @@ 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 ); + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. add_action( 'add_option_' . self::IP_LISTS_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 ); + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. 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( 'add_option_' . self::IP_ALLOW_LIST_ENABLED_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( 'add_option_' . self::IP_BLOCK_LIST_ENABLED_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' ) ); @@ -213,9 +225,13 @@ public static function generate_rules() { } } - // Add manual rules - if ( get_option( self::IP_LISTS_ENABLED_OPTION_NAME ) ) { + // Add IP allow list + if ( get_option( self::IP_ALLOW_LIST_ENABLED_OPTION_NAME ) ) { $rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::IP_ALLOW_RULES_FILE ) ) . "\n"; + } + + // Add IP block list + if ( get_option( self::IP_BLOCK_LIST_ENABLED_OPTION_NAME ) ) { $rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::IP_BLOCK_RULES_FILE ), "return \$waf->block( 'block', -1, 'ip block list' );" ) . "\n"; } diff --git a/projects/packages/waf/src/class-waf-runner.php b/projects/packages/waf/src/class-waf-runner.php index 5f32cef0ac231..6e13203d6f7d8 100644 --- a/projects/packages/waf/src/class-waf-runner.php +++ b/projects/packages/waf/src/class-waf-runner.php @@ -161,15 +161,25 @@ public static function disable() { public static function get_config() { return array( 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_ALLOW_LIST_ENABLED_OPTION_NAME => get_option( Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME ), Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME => get_option( Waf_Rules_Manager::IP_BLOCK_LIST_OPTION_NAME ), + Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME => get_option( Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME ), self::SHARE_DATA_OPTION_NAME => get_option( self::SHARE_DATA_OPTION_NAME ), self::SHARE_DEBUG_DATA_OPTION_NAME => get_option( self::SHARE_DEBUG_DATA_OPTION_NAME ), 'bootstrap_path' => self::get_bootstrap_file_path(), 'standalone_mode' => self::get_standalone_mode_status(), 'automatic_rules_available' => (bool) self::automatic_rules_available(), 'brute_force_protection' => (bool) Brute_Force_Protection::is_enabled(), + + /** + * Provide the deprecated IP lists options for backwards compatibility with older versions of the Jetpack and Protect plugins. + * i.e. If one plugin is updated and the other is not, the latest version of this package will be used by both plugins. + * + * @deprecated $next-version$ + */ + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. + Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME => get_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME ), ); } diff --git a/projects/packages/waf/tests/php/integration/test-waf-compatibility.php b/projects/packages/waf/tests/php/integration/test-waf-compatibility.php index 0a1b7db054ac6..a8670eade18ab 100644 --- a/projects/packages/waf/tests/php/integration/test-waf-compatibility.php +++ b/projects/packages/waf/tests/php/integration/test-waf-compatibility.php @@ -189,4 +189,40 @@ public function testAutomaticRulesOptionInheritsFromModuleStatus() { // Check that the automatic rules option is enabled by default. $this->assertTrue( Waf_Runner::is_enabled() ); } + + /** + * Test the default options for the IP allow and block lists. + */ + public function testIpListsDefaultOptions() { + // Enable the WAF module. + Waf_Runner::enable(); + + // Validate default options on fresh installs. + $this->assertFalse( get_option( Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME ) ); + $this->assertFalse( get_option( Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME ) ); + + // Add content to the allow list. + update_option( Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME, '1.2.3.4' ); + + $this->assertTrue( get_option( Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME ) ); + $this->assertFalse( get_option( Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME ) ); + + // Toggle the old generic option from true to false. + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. + update_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, true ); + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. + update_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, false ); + + // Options default to false when the old generic option is set to false. + $this->assertFalse( get_option( Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME ) ); + $this->assertFalse( get_option( Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME ) ); + + // Set the old generic option to true. + // @phan-suppress-next-line PhanDeprecatedClassConstant -- Needed for backwards compatibility. + update_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, true ); + + // Options default to true when the old generic option is set to true. + $this->assertTrue( get_option( Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME ) ); + $this->assertTrue( get_option( Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME ) ); + } } diff --git a/projects/plugins/jetpack/changelog/add-waf-ip-list-toggles b/projects/plugins/jetpack/changelog/add-waf-ip-list-toggles new file mode 100644 index 0000000000000..a1c1831fa1ef7 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-waf-ip-list-toggles @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: Updated composer.lock. + + diff --git a/projects/plugins/jetpack/composer.lock b/projects/plugins/jetpack/composer.lock index d73c8ba62b5a4..6f0608bc4f15a 100644 --- a/projects/plugins/jetpack/composer.lock +++ b/projects/plugins/jetpack/composer.lock @@ -2753,7 +2753,7 @@ "dist": { "type": "path", "url": "../../packages/waf", - "reference": "7c30728a366806da71f9eb62fb015980ae79ebb2" + "reference": "8877a29b9be7e4ab5b3a13a9afdde2b65870cfe8" }, "require": { "automattic/jetpack-connection": "@dev", @@ -2780,7 +2780,7 @@ "link-template": "https://github.com/Automattic/jetpack-waf/compare/v${old}...v${new}" }, "branch-alias": { - "dev-trunk": "0.16.x-dev" + "dev-trunk": "0.17.x-dev" } }, "autoload": { diff --git a/projects/plugins/protect/changelog/add-waf-ip-list-toggles b/projects/plugins/protect/changelog/add-waf-ip-list-toggles new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/protect/changelog/add-waf-ip-list-toggles @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/protect/composer.lock b/projects/plugins/protect/composer.lock index 473917adbd2e5..0de9a01c191a9 100644 --- a/projects/plugins/protect/composer.lock +++ b/projects/plugins/protect/composer.lock @@ -1725,7 +1725,7 @@ "dist": { "type": "path", "url": "../../packages/waf", - "reference": "7c30728a366806da71f9eb62fb015980ae79ebb2" + "reference": "8877a29b9be7e4ab5b3a13a9afdde2b65870cfe8" }, "require": { "automattic/jetpack-connection": "@dev", @@ -1752,7 +1752,7 @@ "link-template": "https://github.com/Automattic/jetpack-waf/compare/v${old}...v${new}" }, "branch-alias": { - "dev-trunk": "0.16.x-dev" + "dev-trunk": "0.17.x-dev" } }, "autoload": {