Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 278
0.00% covered (danger)
0.00%
0 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
REST_Controller
0.00% covered (danger)
0.00%
0 / 278
0.00% covered (danger)
0.00%
0 / 17
1406
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 register_rest_endpoints
0.00% covered (danger)
0.00%
0 / 178
0.00% covered (danger)
0.00%
0 / 1
2
 api_ignore_threat
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 api_unignore_threat
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 api_fix_threats
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 api_fix_threats_status
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 api_check_credentials
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 api_scan
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 api_toggle_account_protection
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 api_get_account_protection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 api_toggle_waf
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
30
 api_get_waf
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
 api_get_waf_upgrade_seen_status
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 api_set_waf_upgrade_seen_status
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 api_get_onboarding_progress
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 api_complete_onboarding_steps
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 api_get_scan_history
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Class file for managing REST API endpoints for Jetpack Protect.
4 *
5 * @since 1.2.2
6 *
7 * @package automattic/jetpack-protect-plugin
8 */
9
10namespace Automattic\Jetpack\Protect;
11
12use Automattic\Jetpack\Account_Protection\Account_Protection;
13use Automattic\Jetpack\Account_Protection\Settings as Account_Protection_Settings;
14use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
15use Automattic\Jetpack\IP\Utils as IP_Utils;
16use Automattic\Jetpack\Protect_Status\REST_Controller as Protect_Status_REST_Controller;
17use Automattic\Jetpack\Waf\Waf_Runner;
18use Automattic\Jetpack\Waf\Waf_Stats;
19use Jetpack_Protect;
20use WP_Error;
21use WP_REST_Request;
22use WP_REST_Response;
23
24/**
25 * Class REST_Controller
26 */
27class REST_Controller {
28
29    /**
30     * Initialize the plugin's REST API.
31     *
32     * @return void
33     */
34    public static function init() {
35        // Set up the REST authentication hooks.
36        Connection_Rest_Authentication::init();
37
38        // Add custom WP REST API endoints.
39        add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
40    }
41
42    /**
43     * Register the REST API routes.
44     *
45     * @return void
46     */
47    public static function register_rest_endpoints() {
48        Protect_Status_REST_Controller::register_rest_endpoints();
49
50        register_rest_route(
51            'jetpack-protect/v1',
52            'ignore-threat',
53            array(
54                'methods'             => \WP_REST_Server::EDITABLE,
55                'callback'            => __CLASS__ . '::api_ignore_threat',
56                'permission_callback' => function () {
57                    return current_user_can( 'manage_options' );
58                },
59            )
60        );
61
62        register_rest_route(
63            'jetpack-protect/v1',
64            'unignore-threat',
65            array(
66                'methods'             => \WP_REST_Server::EDITABLE,
67                'callback'            => __CLASS__ . '::api_unignore_threat',
68                'permission_callback' => function () {
69                    return current_user_can( 'manage_options' );
70                },
71            )
72        );
73
74        register_rest_route(
75            'jetpack-protect/v1',
76            'fix-threats',
77            array(
78                'methods'             => \WP_REST_Server::EDITABLE,
79                'callback'            => __CLASS__ . '::api_fix_threats',
80                'permission_callback' => function () {
81                    return current_user_can( 'manage_options' );
82                },
83            )
84        );
85
86        register_rest_route(
87            'jetpack-protect/v1',
88            'fix-threats-status',
89            array(
90                'methods'             => \WP_REST_Server::READABLE,
91                'callback'            => __CLASS__ . '::api_fix_threats_status',
92                'permission_callback' => function () {
93                    return current_user_can( 'manage_options' );
94                },
95            )
96        );
97
98        register_rest_route(
99            'jetpack-protect/v1',
100            'check-credentials',
101            array(
102                'methods'             => \WP_REST_Server::EDITABLE,
103                'callback'            => __CLASS__ . '::api_check_credentials',
104                'permission_callback' => function () {
105                    return current_user_can( 'manage_options' );
106                },
107            )
108        );
109
110        register_rest_route(
111            'jetpack-protect/v1',
112            'scan',
113            array(
114                'methods'             => \WP_REST_Server::EDITABLE,
115                'callback'            => __CLASS__ . '::api_scan',
116                'permission_callback' => function () {
117                    return current_user_can( 'manage_options' );
118                },
119            )
120        );
121
122        register_rest_route(
123            'jetpack-protect/v1',
124            'toggle-account-protection',
125            array(
126                'methods'             => \WP_REST_Server::EDITABLE,
127                'callback'            => __CLASS__ . '::api_toggle_account_protection',
128                'permission_callback' => function () {
129                    return current_user_can( 'manage_options' );
130                },
131                'args'                => array(
132                    'enabled' => array(
133                        'description' => __( 'Whether account protection should be on. Omit to flip the current state.', 'jetpack-protect' ),
134                        'type'        => 'boolean',
135                    ),
136                ),
137            )
138        );
139
140        register_rest_route(
141            'jetpack-protect/v1',
142            'account-protection',
143            array(
144                'methods'             => \WP_REST_Server::READABLE,
145                'callback'            => __CLASS__ . '::api_get_account_protection',
146                'permission_callback' => function () {
147                    return current_user_can( 'manage_options' );
148                },
149            )
150        );
151
152        register_rest_route(
153            'jetpack-protect/v1',
154            'toggle-waf',
155            array(
156                'methods'             => \WP_REST_Server::EDITABLE,
157                'callback'            => __CLASS__ . '::api_toggle_waf',
158                'permission_callback' => function () {
159                    return current_user_can( 'manage_options' );
160                },
161                'args'                => array(
162                    'enabled' => array(
163                        'description' => __( 'Whether the firewall should be on. Omit to flip the current state.', 'jetpack-protect' ),
164                        'type'        => 'boolean',
165                    ),
166                ),
167            )
168        );
169
170        register_rest_route(
171            'jetpack-protect/v1',
172            'waf',
173            array(
174                'methods'             => \WP_REST_Server::READABLE,
175                'callback'            => __CLASS__ . '::api_get_waf',
176                'permission_callback' => function () {
177                    return current_user_can( 'manage_options' );
178                },
179            )
180        );
181
182        register_rest_route(
183            'jetpack-protect/v1',
184            'waf-upgrade-seen',
185            array(
186                'methods'             => \WP_REST_Server::READABLE,
187                'callback'            => __CLASS__ . '::api_get_waf_upgrade_seen_status',
188                'permission_callback' => function () {
189                    return current_user_can( 'manage_options' );
190                },
191            )
192        );
193
194        register_rest_route(
195            'jetpack-protect/v1',
196            'waf-upgrade-seen',
197            array(
198                'methods'             => \WP_REST_Server::EDITABLE,
199                'callback'            => __CLASS__ . '::api_set_waf_upgrade_seen_status',
200                'permission_callback' => function () {
201                    return current_user_can( 'manage_options' );
202                },
203            )
204        );
205
206        register_rest_route(
207            'jetpack-protect/v1',
208            'onboarding-progress',
209            array(
210                'methods'             => \WP_REST_Server::READABLE,
211                'callback'            => __CLASS__ . '::api_get_onboarding_progress',
212                'permission_callback' => function () {
213                    return current_user_can( 'manage_options' );
214                },
215            )
216        );
217
218        register_rest_route(
219            'jetpack-protect/v1',
220            'onboarding-progress',
221            array(
222                'methods'             => \WP_REST_Server::EDITABLE,
223                'callback'            => __CLASS__ . '::api_complete_onboarding_steps',
224                'permission_callback' => function () {
225                    return current_user_can( 'manage_options' );
226                },
227            )
228        );
229
230        register_rest_route(
231            'jetpack-protect/v1',
232            'scan-history',
233            array(
234                'methods'             => \WP_REST_Server::READABLE,
235                'callback'            => __CLASS__ . '::api_get_scan_history',
236                'permission_callback' => function () {
237                    return current_user_can( 'manage_options' );
238                },
239            )
240        );
241    }
242
243    /**
244     * Ignores a threat for the API endpoint
245     *
246     * @param WP_REST_Request $request The request object.
247     *
248     * @return WP_REST_Response
249     */
250    public static function api_ignore_threat( $request ) {
251        if ( ! $request['threat_id'] ) {
252            return new WP_REST_Response( 'Missing threat ID.', 400 );
253        }
254
255        $threat_ignored = Threats::ignore_threat( $request['threat_id'] );
256
257        if ( ! $threat_ignored ) {
258            return new WP_REST_Response( 'An error occurred while attempting to ignore the threat.', 500 );
259        }
260
261        return new WP_REST_Response( 'Threat ignored.' );
262    }
263
264    /**
265     * Unignores a threat for the API endpoint
266     *
267     * @param WP_REST_Request $request The request object.
268     *
269     * @return WP_REST_Response
270     */
271    public static function api_unignore_threat( $request ) {
272        if ( ! $request['threat_id'] ) {
273            return new WP_REST_Response( 'Missing threat ID.', 400 );
274        }
275
276        $threat_ignored = Threats::unignore_threat( $request['threat_id'] );
277
278        if ( ! $threat_ignored ) {
279            return new WP_REST_Response( 'An error occurred while attempting to unignore the threat.', 500 );
280        }
281
282        return new WP_REST_Response( 'Threat unignored.' );
283    }
284
285    /**
286     * Fixes threats for the API endpoint
287     *
288     * @param WP_REST_Request $request The request object.
289     *
290     * @return WP_REST_Response
291     */
292    public static function api_fix_threats( $request ) {
293        if ( empty( $request['threat_ids'] ) ) {
294            return new WP_REST_Response( 'Missing threat IDs.', 400 );
295        }
296
297        $threats_fixed = Threats::fix_threats( $request['threat_ids'] );
298
299        if ( ! $threats_fixed ) {
300            return new WP_REST_Response( 'An error occurred while attempting to fix the threat.', 500 );
301        }
302
303        return new WP_REST_Response( $threats_fixed );
304    }
305
306    /**
307     * Fixes threats for the API endpoint
308     *
309     * @param WP_REST_Request $request The request object.
310     *
311     * @return WP_REST_Response
312     */
313    public static function api_fix_threats_status( $request ) {
314        if ( empty( $request['threat_ids'] ) ) {
315            return new WP_REST_Response( 'Missing threat IDs.', 400 );
316        }
317
318        $threats_fixed = Threats::fix_threats_status( $request['threat_ids'] );
319
320        if ( ! $threats_fixed ) {
321            return new WP_REST_Response( 'An error occurred while attempting to get the fixer status of the threats.', 500 );
322        }
323
324        return new WP_REST_Response( $threats_fixed );
325    }
326
327    /**
328     * Checks if the site has credentials configured
329     *
330     * @return WP_REST_Response
331     */
332    public static function api_check_credentials() {
333        $credential_array = Credentials::get_credential_array();
334
335        if ( ! isset( $credential_array ) ) {
336            return new WP_REST_Response( 'An error occurred while attempting to fetch the credentials array', 500 );
337        }
338
339        return new WP_REST_Response( $credential_array );
340    }
341
342    /**
343     * Enqueues a scan for the API endpoint
344     *
345     * @return WP_REST_Response
346     */
347    public static function api_scan() {
348        $scan_enqueued = Threats::scan();
349
350        if ( ! $scan_enqueued ) {
351            return new WP_REST_Response( 'An error occurred while attempting to enqueue the scan.', 500 );
352        }
353
354        return new WP_REST_Response( 'Scan enqueued.' );
355    }
356
357    /**
358     * Turns the Account Protection module on or off for the API endpoint
359     *
360     * @param WP_REST_Request $request The request object.
361     *
362     * @return WP_REST_Response|WP_Error
363     */
364    public static function api_toggle_account_protection( $request ) {
365        $account_protection = Account_Protection::instance();
366        $enable             = $request['enabled'] ?? ! $account_protection->is_enabled();
367
368        if ( ! $enable ) {
369            $disabled = $account_protection->disable();
370            if ( ! $disabled ) {
371                return new WP_Error(
372                    'account_protection_disable_failed',
373                    __( 'An error occurred disabling account protection.', 'jetpack-protect' ),
374                    array( 'status' => 500 )
375                );
376            }
377
378            return rest_ensure_response( true );
379        }
380
381        $enabled = $account_protection->enable();
382        if ( ! $enabled ) {
383            return new WP_Error(
384                'account_protection_enable_failed',
385                __( 'An error occurred enabling account protection.', 'jetpack-protect' ),
386                array( 'status' => 500 )
387            );
388        }
389
390        return rest_ensure_response( true );
391    }
392
393    /**
394     * Get Account Protection data for the API endpoint
395     *
396     * @return WP_Rest_Response
397     */
398    public static function api_get_account_protection() {
399        return new WP_REST_Response( ( new Account_Protection_Settings() )->get() );
400    }
401
402    /**
403     * Turns the WAF module on or off for the API endpoint
404     *
405     * @param WP_REST_Request $request The request object.
406     *
407     * @return WP_REST_Response|WP_Error
408     */
409    public static function api_toggle_waf( $request ) {
410        $is_enabled = Waf_Runner::is_enabled();
411        $enable     = $request['enabled'] ?? ! $is_enabled;
412
413        if ( $enable === $is_enabled ) {
414            return rest_ensure_response( true );
415        }
416
417        if ( ! $enable ) {
418            $disabled = Waf_Runner::disable();
419            if ( ! $disabled ) {
420                return new WP_Error(
421                    'waf_disable_failed',
422                    __( 'An error occurred disabling the firewall.', 'jetpack-protect' ),
423                    array( 'status' => 500 )
424                );
425            }
426
427            return rest_ensure_response( true );
428        }
429
430        $enabled = Waf_Runner::enable();
431        if ( ! $enabled ) {
432            return new WP_Error(
433                'waf_enable_failed',
434                __( 'An error occurred enabling the firewall.', 'jetpack-protect' ),
435                array( 'status' => 500 )
436            );
437        }
438
439        return rest_ensure_response( true );
440    }
441
442    /**
443     * Get WAF data for the API endpoint
444     *
445     * @return WP_Rest_Response
446     */
447    public static function api_get_waf() {
448        // Ensure plugin activation has been performed so WAF module is available.
449        Jetpack_Protect::do_plugin_activation_activities();
450
451        return new WP_REST_Response(
452            array(
453                'wafSupported'        => Waf_Runner::is_supported_environment(),
454                'currentIp'           => IP_Utils::get_ip(),
455                'upgradeIsSeen'       => Jetpack_Protect::get_waf_upgrade_seen_status(),
456                'displayUpgradeBadge' => Jetpack_Protect::get_waf_upgrade_badge_display_status(),
457                'isEnabled'           => Waf_Runner::is_enabled(),
458                'config'              => Waf_Runner::get_config(),
459                'stats'               => Jetpack_Protect::get_waf_stats(),
460                'globalStats'         => Waf_Stats::get_global_stats(),
461            )
462        );
463    }
464
465    /**
466     * Get WAF Upgrade "Seen" Status for the API endpoint
467     *
468     * @return bool Whether the current user has dismissed the upgrade popover or enabled the automatic rules feature.
469     */
470    public static function api_get_waf_upgrade_seen_status() {
471        return Jetpack_Protect::get_waf_upgrade_seen_status();
472    }
473
474    /**
475     * Set WAF Upgrade "Seen" Status for the API endpoint
476     *
477     * @return bool True if upgrade seen status updated to true, false on failure.
478     */
479    public static function api_set_waf_upgrade_seen_status() {
480        return Jetpack_Protect::set_waf_upgrade_seen_status();
481    }
482
483    /**
484     * Gets the current user's onboarding progress for the API endpoint
485     *
486     * @return WP_REST_Response
487     */
488    public static function api_get_onboarding_progress() {
489        $progress = Onboarding::get_current_user_progress();
490        return rest_ensure_response( $progress );
491    }
492
493    /**
494     * Set an onboarding step as completed for the API endpoint
495     *
496     * @param WP_REST_Request $request The request object.
497     *
498     * @return WP_REST_Response
499     */
500    public static function api_complete_onboarding_steps( $request ) {
501        if ( empty( $request['step_ids'] ) || ! is_array( $request['step_ids'] ) ) {
502            return new WP_REST_Response( 'Missing or invalid onboarding step IDs.', 400 );
503        }
504
505        $completed = Onboarding::complete_steps( $request['step_ids'] );
506
507        if ( ! $completed ) {
508            return new WP_REST_Response( 'An error occurred completing the onboarding step(s).', 500 );
509        }
510
511        return new WP_REST_Response( 'Onboarding step(s) completed.' );
512    }
513
514    /**
515     * Return Scan History for the API endpoint
516     *
517     * @return WP_REST_Response
518     */
519    public static function api_get_scan_history() {
520        $scan_history = Scan_History::get_scan_history( false );
521        return rest_ensure_response( $scan_history );
522    }
523}