oont-contents/plugins/jetpack/jetpack_vendor/automattic/jetpack-waf/src/class-compatibility.php
2025-02-08 15:10:23 +01:00

328 lines
11 KiB
PHP

<?php
/**
* Class used to manage backwards-compatibility of the package.
*
* @since 0.8.0
*
* @package automattic/jetpack-waf
*/
namespace Automattic\Jetpack\Waf;
use Jetpack_Options;
/**
* Defines methods for ensuring backwards compatibility.
*/
class Waf_Compatibility {
/**
* Returns the name for the IP allow list enabled/disabled option.
*
* @since 0.22.0
*
* @return string
*/
private static function get_ip_allow_list_enabled_option_name() {
/**
* Patch: bootstrap script generated prior to 0.17.0 may have autoloaded Waf_Rules_Manager class during standalone mode execution.
*
* @see peb6dq-2HL-p2
*/
if ( ! defined( 'Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME' ) ) {
return 'jetpack_waf_ip_allow_list_enabled';
}
return Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME;
}
/**
* Returns the name for the IP block list enabled/disabled option.
*
* @since 0.22.0
*
* @return string
*/
private static function get_ip_block_list_enabled_option_name() {
/**
* Patch: bootstrap script generated prior to 0.17.0 may have autoloaded Waf_Rules_Manager class during standalone mode execution.
*
* @see peb6dq-2HL-p2
*/
if ( ! defined( 'Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME' ) ) {
return 'jetpack_waf_ip_block_list_enabled';
}
return Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME;
}
/**
* Add compatibilty hooks
*
* @since 0.8.0
*
* @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 );
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_' . self::get_ip_allow_list_enabled_option_name(), __CLASS__ . '::default_option_waf_ip_allow_list_enabled', 10, 3 );
add_filter( 'default_option_' . self::get_ip_block_list_enabled_option_name(), __CLASS__ . '::default_option_waf_ip_block_list_enabled', 10, 3 );
}
/**
* Run compatibility migrations.
*
* Note that this method should be compatible with sites where
* the request firewall is not active or not supported.
*
* @see Waf_Runner::is_supported_environment().
*
* @since 0.11.0
*
* @return void
*/
public static function run_compatibility_migrations() {
self::migrate_brute_force_protection_ip_allow_list();
}
/**
* 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.
*
* @since 0.8.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_needs_update( $default, $option, $passed_default ) {
// Allow get_option() to override this default value
if ( $passed_default ) {
return $default;
}
// If the option hasn't been added yet, the WAF needs to be updated.
return true;
}
/**
* Merge the WAF and Brute Force Protection IP allow lists.
*
* @since 0.11.0
*
* @param string $waf_allow_list The WAF IP allow list.
* @param array $brute_force_allow_list The Brute Force Protection IP allow list. Array of IP objects.
*
* @return string The merged IP allow list.
*/
public static function merge_ip_allow_lists( $waf_allow_list, $brute_force_allow_list ) {
if ( empty( $brute_force_allow_list ) ) {
return $waf_allow_list;
}
// Convert the IP objects to strings.
$brute_force_allow_list = array_map(
function ( $ip_object ) {
if ( ! empty( $ip_object->range ) ) {
return $ip_object->range_low . '-' . $ip_object->range_high;
}
return $ip_object->ip_address;
},
$brute_force_allow_list
);
$brute_force_allow_list_string = implode( "\n", $brute_force_allow_list );
if ( empty( $waf_allow_list ) ) {
return $brute_force_allow_list_string;
}
// Return the lists merged into a single string.
return "$waf_allow_list\n$brute_force_allow_list_string";
}
/**
* Migrate the brute force protection IP allow list option to the WAF option.
*
* @since 0.11.0
*
* @return void
*/
public static function migrate_brute_force_protection_ip_allow_list() {
// Get the allow list values directly from the database to avoid filters.
$brute_force_allow_list = Jetpack_Options::get_raw_option( 'jetpack_protect_whitelist' );
$waf_allow_list = Jetpack_Options::get_raw_option( 'jetpack_waf_ip_allow_list' );
if ( ! empty( $brute_force_allow_list ) ) {
if ( empty( $waf_allow_list ) ) {
$waf_allow_list = '';
}
// Merge the two allow lists.
$merged_allow_list = self::merge_ip_allow_lists( $waf_allow_list, $brute_force_allow_list );
// Update the WAF IP allow list with the merged list.
Jetpack_Options::update_raw_option( 'jetpack_waf_ip_allow_list', $merged_allow_list );
// Delete the old option if the update was successful.
// Check the values directly as `update_raw_option()` returns false if the value hasn't changed.
if ( Jetpack_Options::get_raw_option( 'jetpack_waf_ip_allow_list' ) === $merged_allow_list ) {
delete_option( 'jetpack_protect_whitelist' );
}
}
}
/**
* Filter for Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME's option value.
* Merges the deprecated IP allow list from the brute force protection module
* with the existing option value, and flags that the WAF needs to be updated.
*
* @since 0.11.0
*
* @param array $waf_allow_list The current value of the option.
*
* @return array The merged IP allow list.
*/
public static function filter_option_waf_ip_allow_list( $waf_allow_list ) {
$brute_force_allow_list = Jetpack_Options::get_raw_option( 'jetpack_protect_whitelist', false );
if ( false !== $brute_force_allow_list ) {
$waf_allow_list = self::merge_ip_allow_lists( $waf_allow_list, $brute_force_allow_list );
update_option( Waf_Initializer::NEEDS_UPDATE_OPTION_NAME, true );
}
return $waf_allow_list;
}
/**
* Default option for when the Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME option is not set.
*
* @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( $default, $option, $passed_default ) {
// Allow get_option() to override this default value
if ( $passed_default ) {
return $default;
}
$waf_allow_list = '';
// If the brute force option exists, use that and flag that the WAF needs to be updated.
$brute_force_allow_list = Jetpack_Options::get_raw_option( 'jetpack_protect_whitelist', false );
if ( false !== $brute_force_allow_list ) {
$waf_allow_list = self::merge_ip_allow_lists( $waf_allow_list, $brute_force_allow_list );
update_option( Waf_Initializer::NEEDS_UPDATE_OPTION_NAME, true );
}
return $waf_allow_list;
}
/**
* Check if the brute force protection code is being run by an older version of Jetpack (< 12.0).
*
* @since 0.11.1
*
* @return bool
*/
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 0.17.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_ip_allow_list_enabled( $default, $option, $passed_default ) {
// Allow get_option() to override this default value
if ( $passed_default ) {
return $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 = Jetpack_Options::get_raw_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 ( ! Jetpack_Options::get_raw_option( Waf_Rules_Manager::IP_ALLOW_LIST_OPTION_NAME ) ) {
return false;
}
// 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 0.17.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_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 Jetpack_Options::get_raw_option( Waf_Rules_Manager::IP_LISTS_ENABLED_OPTION_NAME, false );
}
}