Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protect: Turn on waf module by default and add UI control for automatic rules #27663

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
9ed7778
Initialize project branch
nateweller Nov 21, 2022
dee3025
Protect: Add WAF package (#27530)
nateweller Nov 21, 2022
396b859
Protect: Clean up components (#27256)
nateweller Nov 21, 2022
10b9e1d
Protect: Add routing and blank Firewall page (#27245)
nateweller Nov 21, 2022
80262c3
Protect: Add Tabbed Navigation (#27259)
nateweller Nov 22, 2022
ef0325b
Protect: Add "new" badge for users who have never viewed the Firewall…
nateweller Nov 22, 2022
09d271c
Protect: Add useWafData hook (#27307)
nateweller Nov 22, 2022
3a5daad
Add: Protect WAF header (#27279)
dkmyta Nov 22, 2022
cb30a93
Protect: Add WAF footer (#27280)
dkmyta Nov 22, 2022
e29fcc8
Merge branch 'trunk' into add/protect-waf-mvp
nateweller Nov 22, 2022
6673112
Fix composer.json
nateweller Nov 23, 2022
e7e3f5c
Changelog
nateweller Nov 23, 2022
1e57499
Protect: Add upgrade prompt while WAF enabled with no rules access (#…
dkmyta Nov 24, 2022
32fa7bf
WAF: Remove has_rules_access in favour of external checks (#27479)
dkmyta Nov 24, 2022
e6cb421
Protect: Add Basic WAF Controls (#27291)
nateweller Nov 26, 2022
76128df
Update Protect to version 1.2.0-alpha
nateweller Nov 26, 2022
668fa15
Protect: WAF share data toggle (#27577)
dkmyta Nov 26, 2022
1bfc1d6
Changelogs
nateweller Nov 28, 2022
8834156
Merge branch 'trunk' into add/protect-waf-mvp
nateweller Nov 28, 2022
52c2c99
Remove outdated TODO comment
nateweller Nov 28, 2022
1de4ed7
Protect: Add notices to the Firewall screen (#27621)
nateweller Nov 28, 2022
e7d549e
Fix invalid HTML - pre cannot be a child of p
nateweller Nov 29, 2022
a0cef45
Merge branch 'trunk' into add/protect-waf-mvp
nateweller Nov 29, 2022
27c26ae
Fix notice duration timeout
nateweller Nov 29, 2022
cbdedea
Remove manual WAF initialization
nateweller Nov 29, 2022
abd1199
Register REST routes on WAF init
nateweller Nov 29, 2022
0666b4f
Create foundation for automatic and manual rules usage separation
dkmyta Nov 29, 2022
fe322db
Update naming
dkmyta Nov 29, 2022
25b7bfd
Update WAF after settings have changed
nateweller Nov 29, 2022
8bd07e4
Add action for tracking initial WAF setup and use for loading header …
dkmyta Nov 30, 2022
0d429f7
Remove comments and testing code
dkmyta Nov 30, 2022
e7851e4
Merge branch 'add/protect-waf-mvp' into add/protect-waf-manual-vs-aut…
dkmyta Nov 30, 2022
b752490
Minor updates to pending conditional checks
dkmyta Nov 30, 2022
8532959
Add Now Available badge to automatic rules section and an action to c…
dkmyta Nov 30, 2022
5d519b4
Add popover for upgrade notice regarding automatic rules, assign same…
dkmyta Nov 30, 2022
0cc703c
Add option to temporarily dismiss the popover only, rather than perma…
dkmyta Nov 30, 2022
15bb2d7
Improve variable naming
dkmyta Nov 30, 2022
744e2ae
Revert changes to useWafData hook usage of setWafIsUpdating (previous…
dkmyta Dec 1, 2022
23dab3a
Add/remove styling comments
dkmyta Dec 1, 2022
9dcf1ef
Update tracks events in Jetpack Protect (#27659)
nateweller Dec 1, 2022
e872ef2
Merge branch 'trunk' into add/protect-waf-mvp
nateweller Dec 1, 2022
a7725ab
Initialize project branch
nateweller Dec 1, 2022
91ccf4a
Initialize project branch
nateweller Dec 1, 2022
1364d47
Merge new base, fix conflicts
dkmyta Dec 1, 2022
b94364e
Update mock data to include WAF properties
dkmyta Dec 2, 2022
d06b800
Update FirewallHeader component to export sub component and import fo…
dkmyta Dec 2, 2022
26cb154
[not verified] Update the WAF to support running the automatic and ma…
nateweller Dec 1, 2022
3c2ebab
[not verified] Check if rules enabled after defining constants and ad…
nateweller Dec 1, 2022
f978158
[not verified] Use IP rules enabled constant in IP list generation fo…
nateweller Dec 1, 2022
b49c21e
[not verified] Regenerate standalone bootstrap when rule options are …
nateweller Dec 1, 2022
1e68467
[not verified] Add support for automatic rules and ip lists enabled c…
nateweller Dec 1, 2022
346acb7
[not verified] Remove definition of constants for enabled rule types …
nateweller Dec 6, 2022
e41f281
Improve upgrade popover and badge logic and handling
dkmyta Dec 6, 2022
7a7d45a
Remove regeneration of bootstrap file on option updates
nateweller Dec 6, 2022
bf722e4
Improve upgrade popover and badge logic and handling
dkmyta Dec 6, 2022
a05dd4c
Merge base, fix conflicts
dkmyta Dec 6, 2022
dd0df41
Remove auto vs manual toggle separation and upgrade messaging
dkmyta Dec 7, 2022
e82e914
Delete old changelog entries
dkmyta Dec 7, 2022
388a217
Remove endpoints for upgrade messaging
dkmyta Dec 7, 2022
16759bc
Remove upgrade messaging endpoints, handling, and content
dkmyta Dec 7, 2022
5f7d4bd
Remove state handlers for seen upgrade
dkmyta Dec 7, 2022
ad0577d
Merge base, fix conflicts
dkmyta Dec 7, 2022
25352d2
Remove unneeded comments
dkmyta Dec 7, 2022
3468a7d
Fix footer styling issues in mobile when WAF module not enabled
dkmyta Dec 7, 2022
4bcad26
Merge branch 'add/protect-waf-dedicated-manual-rules-section' into ad…
dkmyta Dec 7, 2022
95a0e06
Extract JETPACK_SCAN variable to constants file
nateweller Dec 8, 2022
01d0f8c
Update automatic rules toggle to connect to correlated waf setting
nateweller Dec 8, 2022
c809615
Ensure module is enabled before attempting to update settings
nateweller Dec 8, 2022
3f9c5b2
Merge branch 'add/protect-waf-phase-2' into add/waf-automatic-rule-to…
nateweller Dec 10, 2022
9518e1f
Merge branch 'add/waf-automatic-rule-toggle' into add/protect-waf-man…
nateweller Dec 10, 2022
92ff825
Show a notice when the waf module is disabled
nateweller Dec 10, 2022
288e0e4
[not verified] Activate WAF module on plugin activation
nateweller Dec 10, 2022
afbcc9f
Disable WAF controls when the module is disabled
nateweller Dec 10, 2022
e5d0319
Document and organize FirewallPage
nateweller Dec 10, 2022
23c7114
Include automatic rules in default formState object
nateweller Dec 11, 2022
2269bd0
Update styles
nateweller Dec 12, 2022
7b823b1
Remove CurrentlyEnabledFeatures
nateweller Dec 12, 2022
0c26c95
Fix saving of automatic rules option in endpoint
nateweller Dec 12, 2022
8f25a12
[not verified] Fix tests
nateweller Dec 15, 2022
576d5e1
changelog
nateweller Dec 15, 2022
374fbb2
Rename fix-waf-tests to update-waf-tests
nateweller Dec 15, 2022
d4d07a8
Merge branch 'add/protect-waf-phase-2' into add/waf-automatic-rule-to…
nateweller Dec 15, 2022
0586360
Merge branch 'add/waf-automatic-rule-toggle' into add/protect-waf-man…
nateweller Dec 16, 2022
c296c3f
Remove unused redux code left over from other work
nateweller Dec 16, 2022
dfc6038
Merge branch 'add/protect-waf-phase-2' into add/waf-automatic-rule-to…
nateweller Dec 16, 2022
f3e965c
Merge branch 'add/waf-automatic-rule-toggle' into add/protect-waf-man…
nateweller Dec 16, 2022
c43d1ca
Fix styling issue
nateweller Dec 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions projects/packages/waf/changelog/add-waf-automatic-rule-toggle
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: changed

Change the web activation firewall to run automatic and manual rules independantly.
5 changes: 5 additions & 0 deletions projects/packages/waf/src/class-rest-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ public static function waf() {
* @return WP_REST_Response
*/
public static function update_waf( $request ) {
// Automatic Rules Enabled
if ( isset( $request[ Waf_Runner::AUTOMATIC_RULES_ENABLED_OPTION_NAME ] ) ) {
update_option( Waf_Runner::AUTOMATIC_RULES_ENABLED_OPTION_NAME, (bool) $request->get_param( Waf_Runner::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 ) );
Expand Down
100 changes: 64 additions & 36 deletions projects/packages/waf/src/class-waf-runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@
*/
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 = __DIR__ . '/../rules/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';
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 WAF_RULES_VERSION = '1.0.0';
const MODE_OPTION_NAME = 'jetpack_waf_mode';
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 RULES_FILE = __DIR__ . '/../rules/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';
const RULE_LAST_UPDATED_OPTION_NAME = 'jetpack_waf_last_updated_timestamp';
const SHARE_DATA_OPTION_NAME = 'jetpack_waf_share_data';

/**
* Run the WAF
Expand Down Expand Up @@ -56,9 +57,10 @@ public static function initialize() {
* @return void
*/
public static function add_hooks() {
add_action( 'update_option_' . self::AUTOMATIC_RULES_ENABLED_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( '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' ) ) {
Expand All @@ -81,7 +83,7 @@ public static function define_mode() {
}

/**
* Set the mode definition if it has not been set.
* Set the share data definition if it has not been set.
*
* @return void
*/
Expand Down Expand Up @@ -136,6 +138,24 @@ 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 All @@ -157,11 +177,12 @@ public static function disable() {
*/
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(),
self::AUTOMATIC_RULES_ENABLED_OPTION_NAME => get_option( self::AUTOMATIC_RULES_ENABLED_OPTION_NAME ),
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(),
);
}

Expand Down Expand Up @@ -268,6 +289,8 @@ 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();
Expand Down Expand Up @@ -440,29 +463,29 @@ public static function generate_rules() {

self::initialize_filesystem();

$rules = "<?php\n";
$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;
}

if ( $wp_filesystem->exists( self::RULES_FILE ) && $throw_api_exception ) {
throw $e;
// 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;
}

$rules = "<?php\n";
$api_exception = $e;
}

// Ensure that the folder exists.
if ( ! $wp_filesystem->is_dir( dirname( self::RULES_FILE ) ) ) {
$wp_filesystem->mkdir( dirname( self::RULES_FILE ) );
}

// Add manual rules
$ip_allow_rules = self::ALLOW_IP_FILE;
$ip_block_rules = self::BLOCK_IP_FILE;

Expand All @@ -474,6 +497,11 @@ public static function generate_rules() {

$rules = implode( "\n", $rules_divided_by_line );

// Ensure that the folder exists.
if ( ! $wp_filesystem->is_dir( dirname( self::RULES_FILE ) ) ) {
$wp_filesystem->mkdir( dirname( self::RULES_FILE ) );
}

if ( ! $wp_filesystem->put_contents( self::RULES_FILE, $rules ) ) {
throw new \Exception( 'Failed writing rules file to: ' . self::RULES_FILE );
}
Expand Down
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/update-waf-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: compat

Update tests for compat with new firewall settings
7 changes: 7 additions & 0 deletions projects/plugins/jetpack/tests/e2e/helpers/waf-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { execWpCommand } from 'jetpack-e2e-commons/helpers/utils-helper.cjs';
import logger from 'jetpack-e2e-commons/logger.cjs';

export async function enableAutomaticRules() {
logger.sync( 'Enabling automatic firewall rules' );
return execWpCommand( 'option update jetpack_waf_automatic_rules 1' );
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { WpPage } from 'jetpack-e2e-commons/pages/index.js';
import playwrightConfig from '../../playwright.config.cjs';
import { Plans, prerequisitesBuilder } from 'jetpack-e2e-commons/env/index.js';
import { resolveSiteUrl } from 'jetpack-e2e-commons/helpers/utils-helper.cjs';
import { enableAutomaticRules } from '../../helpers/waf-helper.js';

test.describe.parallel( 'WAF Blocking', () => {
test.beforeAll( async ( { browser } ) => {
Expand All @@ -18,6 +19,7 @@ test.describe.parallel( 'WAF Blocking', () => {
.withPlan( Plans.Complete )
.withActiveModules( [ 'waf' ] )
.build();
await enableAutomaticRules();
await page.close();
} );

Expand Down
1 change: 1 addition & 0 deletions projects/plugins/protect/jetpack-protect.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ function ( $actions ) {
}
);

register_activation_hook( __FILE__, array( 'Jetpack_Protect', 'plugin_activation' ) );
register_deactivation_hook( __FILE__, array( 'Jetpack_Protect', 'plugin_deactivation' ) );

// Main plugin class.
Expand Down
45 changes: 44 additions & 1 deletion projects/plugins/protect/src/class-jetpack-protect.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
use Automattic\Jetpack\JITMS\JITM as JITM;
use Automattic\Jetpack\Modules;
use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
use Automattic\Jetpack\My_Jetpack\Products as My_Jetpack_Products;
use Automattic\Jetpack\Plugins_Installer;
Expand All @@ -34,7 +35,8 @@
*/
class Jetpack_Protect {

const JETPACK_WAF_MODULE_SLUG = 'waf';
const JETPACK_WAF_MODULE_SLUG = 'waf';
const JETPACK_PROTECT_ACTIVATION_OPTION = JETPACK_PROTECT_SLUG . '_activated';

/**
* Constructor.
Expand All @@ -43,6 +45,9 @@ public function __construct() {
add_action( 'init', array( $this, 'init' ) );
add_action( '_admin_menu', array( $this, 'admin_page_init' ) );

// Activate the module as the plugin is activated
add_action( 'admin_init', array( $this, 'do_plugin_activation_activities' ) );

// Init Jetpack packages
add_action(
'plugins_loaded',
Expand Down Expand Up @@ -218,6 +223,44 @@ public function plugin_settings_page() {
<?php
}

/**
* Activate the WAF module on plugin activation.
*
* @static
*/
public static function plugin_activation() {
add_option( self::JETPACK_PROTECT_ACTIVATION_OPTION, true );
}

/**
* Helper to check that we have a Jetpack connection.
*/
private static function is_connected() {
$manager = new Connection_Manager();
return $manager->is_connected() && $manager->has_connected_user();
}

/**
* Runs on admin_init, and does actions required on plugin activation, based on
* the activation option.
*
* This needs to be run after the activation hook, as that results in a redirect,
* and we need the sync module's actions and filters to be registered.
*/
public static function do_plugin_activation_activities() {
if ( get_option( self::JETPACK_PROTECT_ACTIVATION_OPTION ) && self::is_connected() ) {
self::activate_module();
}
}

/**
* Activates the Publicize module and disables the activation option
*/
public static function activate_module() {
delete_option( self::JETPACK_PROTECT_ACTIVATION_OPTION );
( new Modules() )->activate( self::JETPACK_WAF_MODULE_SLUG, false, false );
}

/**
* Removes plugin from the connection manager
* If it's the last plugin using the connection, the site will be disconnected.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { addQueryArgs, getQueryArg } from '@wordpress/url';
import React, { useEffect } from 'react';
import { JETPACK_SCAN_SLUG } from '../../constants';
import useWafData from '../../hooks/use-waf-data';
import { STORE_ID } from '../../state/store';
import InterstitialPage from '../interstitial-page';
Expand All @@ -13,16 +14,14 @@ import Tabs, { Tab } from '../tabs';
import styles from './styles.module.scss';
import useRegistrationWatcher from './use-registration-watcher';

export const JETPACK_SCAN = 'jetpack_scan';

const AdminPage = ( { children } ) => {
useRegistrationWatcher();

const { isSeen: wafSeen } = useWafData();
const { refreshPlan, startScanOptimistically, refreshStatus } = useDispatch( STORE_ID );
const { adminUrl } = window.jetpackProtectInitialState || {};
const { run, isRegistered, hasCheckoutStarted } = useProductCheckoutWorkflow( {
productSlug: JETPACK_SCAN,
productSlug: JETPACK_SCAN_SLUG,
redirectUrl: addQueryArgs( adminUrl, { checkPlan: true } ),
siteProductAvailabilityHandler: async () =>
apiFetch( {
Expand Down
Loading