Skip to content

Commit

Permalink
Add a dedicated automatic-rules.php file for storing firewall rules f…
Browse files Browse the repository at this point in the history
…etched from WPCOM
  • Loading branch information
nateweller committed Dec 20, 2022
1 parent 3969cd5 commit 3e0a2d2
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 55 deletions.
1 change: 1 addition & 0 deletions projects/packages/waf/src/class-rest-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions projects/packages/waf/src/class-waf-cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public function teardown() {
*/
public function generate_rules() {
try {
Waf_Runner::generate_automatic_rules();
Waf_Runner::generate_rules();
} catch ( \Exception $e ) {

Expand Down
104 changes: 49 additions & 55 deletions projects/packages/waf/src/class-waf-runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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() );
Expand All @@ -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();
}
Expand Down Expand Up @@ -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 );
}
Expand All @@ -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.
Expand Down Expand Up @@ -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";
Expand Down

0 comments on commit 3e0a2d2

Please sign in to comment.