Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
52.70% covered (warning)
52.70%
39 / 74
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Waf_Initializer
52.70% covered (warning)
52.70%
39 / 74
44.44% covered (danger)
44.44%
4 / 9
110.95
0.00% covered (danger)
0.00%
0 / 1
 init
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 on_waf_activation
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 on_brute_force_protection_activation
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 on_waf_deactivation
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 on_brute_force_protection_deactivation
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 update_waf_after_plugin_upgrade
38.89% covered (danger)
38.89%
7 / 18
0.00% covered (danger)
0.00%
0 / 1
32.82
 check_for_updates
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 remove_module_on_unsupported_environments
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 remove_standalone_module_on_unsupported_environments
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Class use to initialize the WAF module.
4 *
5 * @package automattic/jetpack-waf
6 */
7
8namespace Automattic\Jetpack\Waf;
9
10use Automattic\Jetpack\Waf\Brute_Force_Protection\Brute_Force_Protection;
11use WP_Error;
12use WP_Upgrader;
13
14/**
15 * Initializes the module
16 */
17class Waf_Initializer {
18
19    /**
20     * Option for storing whether the WAF files are potentially out of date.
21     *
22     * @var string NEEDS_UPDATE_OPTION_NAME
23     */
24    const NEEDS_UPDATE_OPTION_NAME = 'jetpack_waf_needs_update';
25
26    /**
27     * Initializes the configurations needed for the waf module.
28     *
29     * @return void
30     */
31    public static function init() {
32        // Do not run in unsupported environments
33        add_action( 'jetpack_get_available_modules', __CLASS__ . '::remove_module_on_unsupported_environments' );
34        add_action( 'jetpack_get_available_standalone_modules', __CLASS__ . '::remove_standalone_module_on_unsupported_environments' );
35
36        // Ensure backwards compatibility
37        Waf_Compatibility::add_compatibility_hooks();
38
39        // Register REST routes. Use a static callable so the controller class is not
40        // loaded into memory/opcache on requests that never reach `rest_api_init`.
41        add_action( 'rest_api_init', array( REST_Controller::class, 'register_rest_routes' ) );
42
43        // Update the WAF after installing or upgrading a relevant Jetpack plugin
44        add_action( 'upgrader_process_complete', __CLASS__ . '::update_waf_after_plugin_upgrade', 10, 2 );
45
46        // Check for compatibility updates
47        add_action( 'admin_init', __CLASS__ . '::check_for_updates' );
48
49        // WAF activation/deactivation hooks
50        add_action( 'jetpack_activate_module_waf', __CLASS__ . '::on_waf_activation' );
51        add_action( 'jetpack_deactivate_module_waf', __CLASS__ . '::on_waf_deactivation' );
52
53        // Brute force protection activation/deactivation hooks
54        add_action( 'jetpack_activate_module_protect', __CLASS__ . '::on_brute_force_protection_activation' );
55        add_action( 'jetpack_deactivate_module_protect', __CLASS__ . '::on_brute_force_protection_deactivation' );
56
57        // Run brute force protection
58        Brute_Force_Protection::initialize();
59
60        // Run the WAF
61        if ( Waf_Runner::is_supported_environment() ) {
62            Waf_Runner::initialize();
63        }
64    }
65
66    /**
67     * Activate the WAF on module activation.
68     *
69     * @return bool|WP_Error True if the WAF activation is successful, WP_Error otherwise.
70     */
71    public static function on_waf_activation() {
72        update_option( Waf_Runner::MODE_OPTION_NAME, 'normal' );
73        add_option( Waf_Rules_Manager::AUTOMATIC_RULES_ENABLED_OPTION_NAME, false );
74
75        try {
76            Waf_Runner::activate();
77            ( new Waf_Standalone_Bootstrap() )->generate();
78        } catch ( Waf_Exception $e ) {
79            return $e->get_wp_error();
80        }
81
82        return true;
83    }
84
85    /**
86     * Activate the Brute force protection on module activation.
87     *
88     * @return bool True if the Brute force protection activation is successful
89     */
90    public static function on_brute_force_protection_activation() {
91        $brute_force_protection = Brute_Force_Protection::instance();
92        $brute_force_protection->on_activation();
93
94        return true;
95    }
96
97    /**
98     * Deactivate the WAF on module deactivation.
99     *
100     * @return bool|WP_Error True if the WAF deactivation is successful, WP_Error otherwise.
101     */
102    public static function on_waf_deactivation() {
103        try {
104            Waf_Runner::deactivate();
105        } catch ( Waf_Exception $e ) {
106            return $e->get_wp_error();
107        }
108
109        return true;
110    }
111
112    /**
113     * Deactivate the Brute force protection on module deactivation.
114     *
115     * @return bool True if the Brute force protection deactivation is successful.
116     */
117    public static function on_brute_force_protection_deactivation() {
118        $brute_force_protection = Brute_Force_Protection::instance();
119        $brute_force_protection->on_deactivation();
120
121        return true;
122    }
123
124    /**
125     * Updates the WAF after upgrader process is complete.
126     *
127     * @param WP_Upgrader $upgrader    WP_Upgrader instance. In other contexts this might be a Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance.
128     * @param array       $hook_extra  Array of bulk item update data.
129     *
130     * @return void
131     */
132    public static function update_waf_after_plugin_upgrade( $upgrader, $hook_extra ) {
133        $jetpack_text_domains_with_waf = array( 'jetpack', 'jetpack-protect' );
134        $jetpack_plugins_with_waf      = array( 'jetpack/jetpack.php', 'jetpack-protect/jetpack-protect.php' );
135
136        $hook_extra['type']    = $hook_extra['type'] ?? null;
137        $hook_extra['action']  = $hook_extra['action'] ?? null;
138        $hook_extra['plugins'] = $hook_extra['plugins'] ?? array();
139
140        // Only run on upgrades affecting plugins
141        if ( 'plugin' !== $hook_extra['type'] ) {
142            return;
143        }
144
145        // Only run on updates and installations
146        if ( 'update' !== $hook_extra['action'] && 'install' !== $hook_extra['action'] ) {
147            return;
148        }
149
150        // Only run when Jetpack plugins were affected
151        if ( 'update' === $hook_extra['action'] &&
152            ! empty( $hook_extra['plugins'] ) &&
153            empty( array_intersect( $jetpack_plugins_with_waf, $hook_extra['plugins'] ) )
154        ) {
155            return;
156        }
157        if ( 'install' === $hook_extra['action'] &&
158            ! empty( $upgrader->new_plugin_data['TextDomain'] ) &&
159            empty( in_array( $upgrader->new_plugin_data['TextDomain'] ?? null, $jetpack_text_domains_with_waf, true ) )
160        ) {
161            return;
162        }
163
164        update_option( self::NEEDS_UPDATE_OPTION_NAME, true );
165    }
166
167    /**
168     * Check for WAF update
169     *
170     * Updates the WAF when the "needs update" option is enabled.
171     *
172     * @return bool|WP_Error True if the WAF is up-to-date or was sucessfully updated, WP_Error if the update failed.
173     */
174    public static function check_for_updates() {
175        if ( get_option( self::NEEDS_UPDATE_OPTION_NAME ) ) {
176            if ( Waf_Runner::is_supported_environment() ) {
177                // Compatiblity patch for cases where an outdated WAF_Constants class has been
178                // autoloaded by the standalone bootstrap execution at the beginning of the current request.
179                if ( ! method_exists( Waf_Constants::class, 'define_mode' ) ) {
180                    try {
181                        ( new Waf_Standalone_Bootstrap() )->generate();
182                    } catch ( Waf_Exception $e ) {
183                        return $e->get_wp_error();
184                    }
185                }
186
187                Waf_Compatibility::run_compatibility_migrations();
188
189                try {
190                    Waf_Rules_Manager::generate_ip_rules();
191                    Waf_Rules_Manager::generate_rules();
192                    ( new Waf_Standalone_Bootstrap() )->generate();
193                } catch ( Waf_Exception $e ) {
194                    return $e->get_wp_error();
195                }
196            } else {
197                // If the site doesn't support the request firewall,
198                // just migrate the IP allow list used by brute force protection.
199                Waf_Compatibility::migrate_brute_force_protection_ip_allow_list();
200            }
201
202            update_option( self::NEEDS_UPDATE_OPTION_NAME, false );
203        }
204
205        return true;
206    }
207
208    /**
209     * Disables the WAF module when on an unsupported platform in Jetpack.
210     *
211     * @param array $modules Filterable value for `jetpack_get_available_modules`.
212     *
213     * @return array Array of module slugs.
214     */
215    public static function remove_module_on_unsupported_environments( $modules ) {
216        if ( ! Waf_Runner::is_supported_environment() ) {
217            // WAF should never be available on unsupported platforms.
218            unset( $modules['waf'] );
219        }
220
221        return $modules;
222    }
223
224    /**
225     * Disables the WAF module when on an unsupported platform in a standalone plugin.
226     *
227     * @param array $modules Filterable value for `jetpack_get_available_standalone_modules`.
228     *
229     * @return array Array of module slugs.
230     */
231    public static function remove_standalone_module_on_unsupported_environments( $modules ) {
232        if ( ! Waf_Runner::is_supported_environment() ) {
233            // WAF should never be available on unsupported platforms.
234            $modules = array_filter(
235                $modules,
236                function ( $module ) {
237                    return $module !== 'waf';
238                }
239            );
240
241        }
242
243        return $modules;
244    }
245}