Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.68% covered (danger)
48.68%
37 / 76
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CLI
48.65% covered (danger)
48.65%
36 / 74
25.00% covered (danger)
25.00%
1 / 4
61.87
0.00% covered (danger)
0.00%
0 / 1
 mode
35.48% covered (danger)
35.48%
11 / 31
0.00% covered (danger)
0.00%
0 / 1
11.71
 setup
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
8
 teardown
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 generate_rules
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * CLI handler for Jetpack Waf.
4 *
5 * @package automattic/jetpack-waf
6 */
7
8namespace Automattic\Jetpack\Waf;
9
10use WP_CLI;
11use WP_CLI_Command;
12
13if ( ! defined( 'ABSPATH' ) ) {
14    exit( 0 );
15}
16
17/**
18 * Set up the WAF, change its mode, or generate its rules.
19 */
20class CLI extends WP_CLI_Command {
21    /**
22     * View or set the current mode of the WAF.
23     * ## OPTIONS
24     *
25     * [<mode>]
26     * : The new mode to be set.
27     * ---
28     * options:
29     *   - silent
30     *   - normal
31     * ---
32     *
33     * @param array $args Arguments passed to CLI.
34     * @return void|null
35     * @throws WP_CLI\ExitException If there is an error switching the mode.
36     */
37    public function mode( $args ) {
38        if ( count( $args ) > 1 ) {
39
40            return WP_CLI::error( __( 'Only one mode may be specified.', 'jetpack-waf' ) );
41        }
42        if ( count( $args ) === 1 ) {
43            if ( ! Waf_Runner::is_allowed_mode( $args[0] ) ) {
44
45                return WP_CLI::error(
46                    sprintf(
47                        /* translators: %1$s is the mode that was actually found. Also note that the expected "silent" and "normal" are hard-coded strings and must therefore stay the same in any translation. */
48                        __( 'Invalid mode: %1$s. Expected "silent" or "normal".', 'jetpack-waf' ),
49                        $args[0]
50                    )
51                );
52            }
53
54            update_option( Waf_Runner::MODE_OPTION_NAME, $args[0] );
55
56            try {
57                ( new Waf_Standalone_Bootstrap() )->generate();
58            } catch ( \Exception $e ) {
59                WP_CLI::warning(
60                    sprintf(
61                        /* translators: %1$s is the unexpected error message. */
62                        __( 'Unable to generate waf bootstrap - standalone mode may not work properly: %1$s', 'jetpack-waf' ),
63                        $e->getMessage()
64                    )
65                );
66            }
67
68            return WP_CLI::success(
69                sprintf(
70                    /* translators: %1$s is the name of the mode that was just switched to. */
71                    __( 'Jetpack WAF mode switched to "%1$s".', 'jetpack-waf' ),
72                    get_option( Waf_Runner::MODE_OPTION_NAME )
73                )
74            );
75        }
76        WP_CLI::line(
77            sprintf(
78                /* translators: %1$s is the name of the mode that the waf is currently running in. */
79                __( 'Jetpack WAF is running in "%1$s" mode.', 'jetpack-waf' ),
80                get_option( Waf_Runner::MODE_OPTION_NAME )
81            )
82        );
83    }
84
85    /**
86     * Turn on the firewall module and set up the WAF to run.
87     * ## OPTIONS
88     *
89     * <mode>
90     * : The new mode to be set.
91     * ---
92     * options:
93     *   - silent
94     *   - normal
95     * ---
96     *
97     * @param array $args Arguments passed to CLI.
98     * @return void|null
99     * @throws WP_CLI\ExitException If there is an error switching the mode.
100     */
101    public function setup( $args ) {
102        // Check the mode before anything below turns the firewall on.
103        if ( empty( $args[0] ) || ! Waf_Runner::is_allowed_mode( $args[0] ) ) {
104            return WP_CLI::error( __( 'Specify a mode for the firewall: "silent" or "normal".', 'jetpack-waf' ) );
105        }
106
107        // Let is_allowed_mode know we are running from the CLI
108        define( 'WAF_CLI_MODE', $args[0] );
109
110        // Turn the module on first: its activation hook resets the mode to "normal".
111        $was_set_up = Waf_Runner::is_enabled() || get_option( Waf_Runner::MODE_OPTION_NAME );
112        Waf_Runner::enable();
113        // Turning the module off runs its teardown, so only undo a first-time setup.
114        $roll_back = ! $was_set_up && Waf_Runner::is_enabled();
115
116        // Set the mode and generate the bootstrap
117        $this->mode( array( $args[0] ) );
118
119        try {
120            // Add relevant options and generate the rules.php file
121            Waf_Runner::activate();
122        } catch ( \Exception $e ) {
123            if ( $roll_back ) {
124                Waf_Runner::disable();
125            }
126
127            return WP_CLI::error(
128                sprintf(
129                    /* translators: %1$s is the unexpected error message. */
130                    __( 'Jetpack WAF rules file failed to generate: %1$s', 'jetpack-waf' ),
131                    $e->getMessage()
132                )
133            );
134        }
135
136        if ( ! Waf_Runner::is_enabled() ) {
137            WP_CLI::warning( __( 'The firewall module could not be turned on, so the firewall will not run inside WordPress. Check that the site is connected and its plan includes the firewall.', 'jetpack-waf' ) );
138        }
139
140        return WP_CLI::success( __( 'Jetpack WAF has successfully been set up.', 'jetpack-waf' ) );
141    }
142
143    /**
144     * Turn off the firewall module, delete the WAF options, and empty the rules file.
145     *
146     * @return void|null
147     * @throws WP_CLI\ExitException If deactivating has failures.
148     */
149    public function teardown() {
150        // Turn the module off too, or the Protect page keeps reporting the firewall as on.
151        Waf_Runner::disable();
152
153        try {
154            Waf_Runner::deactivate();
155        } catch ( \Exception $e ) {
156            WP_CLI::error( __( 'Jetpack WAF failed to fully deactivate.', 'jetpack-waf' ) );
157        }
158
159        if ( Waf_Runner::is_enabled() ) {
160            WP_CLI::warning( __( 'The firewall module is still active, so Jetpack will keep showing the firewall as on. A plugin or filter on this site is keeping it active.', 'jetpack-waf' ) );
161        }
162
163        return WP_CLI::success( __( 'Jetpack WAF has been deactivated.', 'jetpack-waf' ) );
164    }
165
166    /**
167     * Generate the rules.php file with latest rules for the WAF.
168     *
169     * @return void|null
170     * @throws WP_CLI\ExitException If there is an error switching the mode.
171     */
172    public function generate_rules() {
173        try {
174            Waf_Constants::define_entrypoint();
175            Waf_Rules_Manager::generate_automatic_rules();
176            Waf_Rules_Manager::generate_rules();
177        } catch ( \Exception $e ) {
178
179            return WP_CLI::error(
180                sprintf(
181                    /* translators: %1$s is the unexpected error message. */
182                    __( 'Jetpack WAF rules file failed to generate: %1$s', 'jetpack-waf' ),
183                    $e->getMessage()
184                )
185            );
186        }
187
188        return WP_CLI::success(
189            sprintf(
190                /* translators: %1$s is the name of the mode that was just switched to. */
191                __( 'Jetpack WAF rules successfully created to: "%1$s".', 'jetpack-waf' ),
192                Waf_Runner::get_waf_file_path( JETPACK_WAF_ENTRYPOINT )
193            )
194        );
195    }
196}