diff --git a/projects/packages/waf/src/class-rest-controller.php b/projects/packages/waf/src/class-rest-controller.php index 4dc328ec64cca..0c7adb7d79f43 100644 --- a/projects/packages/waf/src/class-rest-controller.php +++ b/projects/packages/waf/src/class-rest-controller.php @@ -58,6 +58,7 @@ public static function update_rules() { $message = 'Rules updated succesfully'; try { + Waf_Runner::generate_automatic_rules(); Waf_Runner::generate_rules(); } catch ( \Exception $e ) { $success = false; diff --git a/projects/packages/waf/src/class-waf-cli.php b/projects/packages/waf/src/class-waf-cli.php index 5ce887dcdf868..57c8e0571b7f3 100644 --- a/projects/packages/waf/src/class-waf-cli.php +++ b/projects/packages/waf/src/class-waf-cli.php @@ -142,6 +142,7 @@ public function teardown() { */ public function generate_rules() { try { + Waf_Runner::generate_automatic_rules(); Waf_Runner::generate_rules(); } catch ( \Exception $e ) { diff --git a/projects/packages/waf/src/class-waf-runner.php b/projects/packages/waf/src/class-waf-runner.php index 18d1a1d4d197d..b6ffd3621ae10 100644 --- a/projects/packages/waf/src/class-waf-runner.php +++ b/projects/packages/waf/src/class-waf-runner.php @@ -24,6 +24,7 @@ class Waf_Runner { 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 = __DIR__ . '/../rules/rules.php'; + const AUTOMATIC_RULES_FILE = __DIR__ . '/../rules/automatic-rules.php'; const ALLOW_IP_FILE = __DIR__ . '/../rules/allow-ip.php'; const BLOCK_IP_FILE = __DIR__ . '/../rules/block-ip.php'; const VERSION_OPTION_NAME = 'jetpack_waf_rules_version'; @@ -138,24 +139,6 @@ public static function is_enabled() { return true; } - /** - * Determines if automatic rules are enabled. - * - * @return bool - */ - public static function automatic_rules_enabled() { - // for backwards compatibility, if the automatic rules option does not exist and the - // module is active, consider automatic rules enabled - $option_exists = get_option( self::AUTOMATIC_RULES_ENABLED_OPTION_NAME ) === false; - if ( ! $option_exists && self::is_enabled() ) { - $is_enabled = true; - } else { - $is_enabled = (bool) get_option( self::AUTOMATIC_RULES_ENABLED_OPTION_NAME ); - } - - return $is_enabled; - } - /** * Enables the WAF module on the site. */ @@ -289,12 +272,11 @@ public static function activate() { add_option( self::VERSION_OPTION_NAME, self::WAF_RULES_VERSION ); } - add_option( self::AUTOMATIC_RULES_ENABLED_OPTION_NAME, false ); - add_option( self::IP_LISTS_ENABLED_OPTION_NAME, false ); add_option( self::SHARE_DATA_OPTION_NAME, true ); self::initialize_filesystem(); self::create_waf_directory(); + self::generate_automatic_rules(); self::generate_ip_rules(); self::create_blocklog_table(); self::generate_rules(); @@ -376,6 +358,7 @@ public static function update_rules_cron() { return; } + self::generate_automatic_rules(); self::generate_ip_rules(); self::generate_rules(); update_option( self::RULE_LAST_UPDATED_OPTION_NAME, time() ); @@ -394,6 +377,7 @@ public static function update_rules_if_changed() { $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_automatic_rules(); self::generate_ip_rules(); self::generate_rules(); } @@ -467,41 +451,23 @@ public static function generate_rules() { $api_exception = null; $throw_api_exception = true; - // Add automatic rules - if ( self::automatic_rules_enabled() ) { - 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; - } - - if ( $wp_filesystem->exists( self::RULES_FILE ) && $throw_api_exception ) { - throw $e; - } - - $api_exception = $e; - } - } - // Add manual rules - $ip_allow_rules = self::ALLOW_IP_FILE; - $ip_block_rules = self::BLOCK_IP_FILE; - - $ip_list_code = "if ( require('$ip_allow_rules') ) { return; }\n" . - "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 ); + if ( get_option( self::IP_LISTS_ENABLED_OPTION_NAME ) ) { + $rules .= "if ( require('" . self::ALLOW_IP_FILE . "') ) { return; }\n"; + $rules .= "if ( require('" . self::BLOCK_IP_FILE . "') ) { return \$waf->block('block', -1, 'ip block list'); }\n"; + } - $rules = implode( "\n", $rules_divided_by_line ); + // Add automatic rules + if ( get_option( self::AUTOMATIC_RULES_ENABLED_OPTION_NAME ) ) { + $rules .= "require('" . self::AUTOMATIC_RULES_FILE . "');\n"; + } - // Ensure that the folder exists. + // Ensure that the folder exists if ( ! $wp_filesystem->is_dir( dirname( self::RULES_FILE ) ) ) { $wp_filesystem->mkdir( dirname( self::RULES_FILE ) ); } + // Update the rules file if ( ! $wp_filesystem->put_contents( self::RULES_FILE, $rules ) ) { throw new \Exception( 'Failed writing rules file to: ' . self::RULES_FILE ); } @@ -511,6 +477,41 @@ public static function generate_rules() { } } + /** + * Generates the automatic-rules.php script + * + * @throws \Exception If rules cannot be generated and saved. + * @return void + */ + public static function generate_automatic_rules() { + /** + * WordPress filesystem abstraction. + * + * @var \WP_Filesystem_Base $wp_filesystem + */ + global $wp_filesystem; + + self::initialize_filesystem(); + + // Ensure that the folder exists. + if ( ! $wp_filesystem->is_dir( dirname( self::AUTOMATIC_RULES_FILE ) ) ) { + $wp_filesystem->mkdir( dirname( self::AUTOMATIC_RULES_FILE ) ); + } + + try { + $rules = self::get_rules_from_api(); + } catch ( \Exception $exception ) { + // Do not throw API exceptions for users who do not have access + if ( 401 !== $exception->getCode() ) { + throw $exception; + } + } + + if ( ! $wp_filesystem->put_contents( self::AUTOMATIC_RULES_FILE, $rules ) ) { + throw new \Exception( 'Failed writing automatic rules file to: ' . self::AUTOMATIC_RULES_FILE ); + } + } + /** * 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. @@ -558,13 +559,6 @@ public static function generate_ip_rules() { $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";