PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.0.1
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.0.1
9.5.11 9.5.10.1 9.5.10 trunk 9.4.0 9.4.1 9.4.2 9.4.3 9.5.0 9.5.0.1 9.5.0.2 9.5.1 9.5.2 9.5.2.2 9.5.2.3 9.5.3 9.5.3.1 9.5.3.2 9.5.4 9.5.5 9.5.6 9.5.7 9.5.8 9.5.9
really-simple-ssl / class-wp-cli.php
really-simple-ssl Last commit date
assets 9 months ago languages 9 months ago lets-encrypt 9 months ago lib 1 year ago mailer 10 months ago modal 9 months ago onboarding 10 months ago placeholders 9 months ago progress 1 year ago security 9 months ago settings 9 months ago testssl 5 years ago upgrade 9 months ago .wp-env.json 10 months ago SECURITY.md 9 months ago class-admin.php 9 months ago class-cache.php 2 years ago class-certificate.php 2 years ago class-front-end.php 1 year ago class-installer.php 10 months ago class-mixed-content-fixer.php 3 years ago class-multisite.php 1 year ago class-server.php 1 year ago class-site-health.php 1 year ago class-wp-cli.php 11 months ago compatibility.php 1 year ago force-deactivate.txt 1 year ago functions.php 10 months ago index.php 2 years ago readme.txt 9 months ago rector.php 1 year ago rlrsssl-really-simple-ssl.php 9 months ago rsssl-auto-loader.php 1 year ago sbom.json.gz 9 months ago ssl-test-page.php 2 years ago system-status.php 9 months ago uninstall.php 9 months ago upgrade.php 9 months ago
class-wp-cli.php
1547 lines
1 <?php
2 defined( 'ABSPATH' ) or die();
3
4 require_once rsssl_path . 'lib/admin/class-encryption.php';
5
6 use RSSSL\lib\admin\Encryption;
7 use RSSSL\Pro\Security\WordPress\Firewall\Models\Rsssl_404_Block;
8 use RSSSL\Security\WordPress\Two_Fa\Rsssl_Two_Fa_Status;
9
10 /**
11 * WP-CLI integration for Really Simple Security
12 *
13 * For an overview of commands use wp help rsssl
14 *
15 * Usage examples:
16 * wp rsssl activate_ssl
17 * wp rsssl deactivate_ssl
18 * wp rsssl activate_recommended_features
19 * wp rsssl deactivate_recommended_features
20 * wp rsssl activate_security_headers
21 * wp rsssl deactivate_security_headers
22 * wp rsssl update_option --name=site_has_ssl --value=true
23 *
24 * Booleans should be passed to update_option as 0 or 1.
25 *
26 * To complete all standard dashboard notices (recommended features + .htaccess redirect + HSTS + e-mail verification):
27 *
28 * wp rsssl activate_recommended_features
29 * wp rsssl update_option --name=redirect --value=htaccess
30 * wp rsssl update_option --name=hsts --value=1
31 * wp rsssl update_option --name=hsts_preload --value=1
32 * wp rsssl update_option --name=hsts_subdomains --value=1
33 * wp rsssl update_option --name=hsts_max_age --value='63072000'
34 * wp rsssl update_option --name=notifications_email_address --value='you@example.com'
35 * wp option update rsssl_email_verification_status 'completed'
36 */
37 class rsssl_wp_cli {
38
39 use Encryption;
40
41 public function __construct() {
42 if ( $this->wp_cli_active() ) {
43 add_action( 'init', [ $this, 'register_wp_cli_commands' ], 0 );
44 }
45 }
46
47 /**
48 * Checks if the conditions for running a Pro WP-CLI command are met.
49 * This is called *within* the command handler, ensuring plugin is loaded.
50 * Outputs an error and exits if conditions are not met.
51 *
52 * @return bool True if conditions are met, false otherwise (though it usually exits on false).
53 */
54 private function check_pro_command_preconditions(bool $skip_license = false ): bool {
55 // Skip license check for free (non-pro) commands
56 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
57 $command = $backtrace[1]['function'] ?? '';
58 $command_list = $this->get_command_list();
59 if ( isset($command_list[$command]) && $command_list[$command]['pro'] === false ) {
60 return true;
61 }
62 // Check if Pro is active (redundant check, but safe)
63 if ( ! defined( 'rsssl_pro' ) ) {
64 WP_CLI::error(
65 __( 'This command is related to functionality available in Really Simple Security Pro, please consider upgrading to unlock all powerful security features. Read more: https://really-simple-ssl.com/pro', 'really-simple-ssl' ),
66 true // Exit after error
67 );
68 return false; // Should not be reached
69 }
70
71 if ( $skip_license ) {
72 return true; // Skip license check if explicitly requested
73 }
74 // Check if license is valid (now safe to call)
75 if ( ! RSSSL()->licensing->license_is_valid() ) {
76 $activate_command = 'wp rsssl activate_license <YOUR_LICENSE_KEY>';
77 // Check if the command exists in the list just to be safe
78 if (!isset($this->get_command_list()['activate_license'])) {
79 $activate_command = 'activate_license'; // Fallback text
80 }
81 WP_CLI::error(
82 sprintf(
83 __( 'It seems that no valid license key is activated for this domain. Activate your license key using the `%s` command, or purchase a valid license key via https://really-simple-ssl.com/pro', 'really-simple-ssl' ),
84 $activate_command
85 ),
86 true // Exit after error
87 );
88 return false; // Should not be reached
89 }
90
91 // All checks passed
92 return true;
93 }
94
95 /**
96 * Check if WP-CLI is active.
97 *
98 * @return bool True if WP-CLI is active, false otherwise.
99 */
100 public function wp_cli_active() {
101 return defined( 'WP_CLI' ) && WP_CLI;
102 }
103
104 /**
105 * Activate SSL through WP-CLI.
106 *
107 * Provides options for verbose output, forcing activation despite warnings,
108 * skipping confirmation, and performing a dry run.
109 *
110 * ## OPTIONS
111 *
112 * [--verbose]
113 * : Show detailed steps during activation.
114 *
115 * [--force]
116 * : Force activation even if pre-flight checks issue warnings.
117 *
118 * [--yes]
119 * : Skip the confirmation prompt before activating.
120 *
121 * [--dry-run]
122 * : Perform checks and report intended actions without making changes.
123 *
124 * ## EXAMPLES
125 *
126 * wp rsssl activate_ssl
127 * wp rsssl activate_ssl --verbose --yes
128 * wp rsssl activate_ssl --dry-run
129 *
130 * @param array $args Positional arguments (none used here).
131 * @param array $assoc_args Associative arguments (--verbose, --force, --yes, --dry-run).
132 * @return void
133 */
134 public function activate_ssl( $args, $assoc_args ) {
135 if ( ! $this->check_pro_command_preconditions() ) return;
136 $is_verbose = WP_CLI\Utils\get_flag_value( $assoc_args, 'verbose', false );
137 $is_force = WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false );
138 $skip_confirm = WP_CLI\Utils\get_flag_value( $assoc_args, 'yes', false );
139 $is_dry_run = WP_CLI\Utils\get_flag_value( $assoc_args, 'dry-run', false );
140
141 if ( $is_dry_run ) {
142 WP_CLI::line( "-- Dry Run Enabled: No changes will be made. --" );
143 }
144
145 try {
146 // --- Suggestion 3: Pre-flight Checks ---
147 if ( $is_verbose || $is_dry_run ) WP_CLI::debug( 'Running pre-activation checks...', 'rsssl-cli' );
148
149 // Assume this function now exists and returns ['success' => bool, 'message' => string, 'warnings' => array]
150 $checks = $this->perform_pre_flight_checks();
151
152 if ( ! empty( $checks['warnings'] ) ) {
153 foreach ( $checks['warnings'] as $warning ) {
154 WP_CLI::warning( $warning );
155 }
156 if ( ! $is_force && ! $is_dry_run ) {
157 WP_CLI::error( 'Pre-flight checks issued warnings. Use --force to proceed anyway.', false ); // Use false to allow dry-run continue
158 if (!$is_dry_run) return; // Stop if not dry run
159 }
160 }
161
162 if ( ! $checks['success'] ) {
163 // If checks outright fail (not just warnings)
164 WP_CLI::error( 'Pre-flight checks failed: ' . $checks['message'] );
165 return;
166 }
167
168 if ( $is_verbose || $is_dry_run ) WP_CLI::debug( 'Pre-flight checks passed.', 'rsssl-cli' );
169
170
171 // --- Report Intended Actions (Dry Run) ---
172 if ( $is_dry_run ) {
173 WP_CLI::line( "Intended actions:" );
174 WP_CLI::line( "- Update WordPress Site URL and Home URL to HTTPS." );
175 WP_CLI::line( "- Configure redirects (method depends on settings)." );
176 WP_CLI::line( "- Update internal links/content (if mixed content fixer enabled)." );
177 WP_CLI::line( "- Dismiss onboarding notice." );
178 WP_CLI::success( "Dry run complete. No changes were made." );
179 return; // End dry run here
180 }
181
182
183 // --- Suggestion 4: Confirmation Prompt ---
184 if ( ! $skip_confirm ) {
185 WP_CLI::confirm( 'Are you sure you want to activate SSL for this site?' );
186 // WP_CLI::confirm exits script if user doesn't confirm
187 }
188
189 // --- Core Activation Logic ---
190 if ( $is_verbose ) WP_CLI::debug( 'Attempting SSL activation...', 'rsssl-cli' );
191
192 // --- Suggestion 5: Clarify Side Effects ---
193 // Move onboarding dismissal inside the main activation logic or make it explicit
194 // update_option( 'rsssl_onboarding_dismissed', true, false ); // Optionally moved inside activate_ssl or reported
195
196 // --- Suggestion 1: Granular Failure Reasons ---
197 // Assume RSSSL()->admin->activate_ssl() now returns an array or throws specific exceptions
198 // Passing $is_verbose allows the underlying function to potentially output debug info too
199 $result = RSSSL()->admin->activate_ssl( $is_verbose );
200
201 // Check if $result is structured like ['success' => bool, 'message' => string]
202 if ( is_array( $result ) && isset( $result['success'] ) ) {
203 if ( $result['success'] ) {
204 $success_message = 'SSL activated successfully.';
205 // Suggestion 5: Clarify Side Effects (Example)
206 if ( get_option('rsssl_onboarding_dismissed') ) {
207 $success_message .= ' Onboarding notice dismissed.';
208 }
209 WP_CLI::success( $success_message );
210 } else {
211 // Use the detailed message from the function
212 WP_CLI::error( 'SSL activation failed: ' . ( $result['message'] ?? 'Unknown reason.' ) );
213 }
214 } else if ( $result === true ) { // Handle simple boolean success
215 WP_CLI::success( 'SSL activated successfully. Onboarding notice dismissed.' );
216 } else { // Handle simple boolean failure or unexpected return
217 WP_CLI::error( 'SSL activation failed (unknown reason).' );
218 }
219
220
221 } catch ( Exception $e ) { // Catch specific exceptions if activate_ssl throws them
222 // Suggestion 1 & 2: More specific error based on exception type if possible
223 WP_CLI::error( 'Failed to activate SSL due to an unexpected error: ' . $e->getMessage() );
224 }
225 }
226
227 /**
228 * Deactivate SSL through WP-CLI.
229 *
230 * @return void
231 */
232 public function deactivate_ssl() {
233 if ( ! $this->check_pro_command_preconditions() ) return;
234 try {
235 RSSSL()->admin->deactivate();
236 WP_CLI::success( 'SSL deactivated' );
237 } catch ( Exception $e ) {
238 WP_CLI::error( 'Failed to deactivate SSL: ' . $e->getMessage() );
239 }
240 }
241
242 /**
243 * Update a Really Simple Security option via WP-CLI.
244 * Booleans should be passed as 0 or 1.
245 *
246 * @param array $args Command-line positional arguments.
247 * @param array $assoc_args Command-line associative arguments.
248 *
249 * @return void
250 */
251 public function update_option( $args, $assoc_args ) {
252 if ( ! isset( $assoc_args['name'] ) || ! isset( $assoc_args['value'] ) ) {
253 WP_CLI::error( 'Both --name and --value parameters are required.' );
254 }
255
256 $name = sanitize_title( $assoc_args['name'] );
257 $value = $assoc_args['value'];
258
259 try {
260 rsssl_update_option( $name, $value );
261 WP_CLI::success( "Option $name updated to $value" );
262 } catch ( Exception $e ) {
263 WP_CLI::error( 'Failed to update option: ' . $e->getMessage() );
264 }
265 }
266
267 /**
268 * Activate all recommended features via CLI
269 *
270 * @throws Exception
271 * return void
272 */
273 public function activate_recommended_features() {
274 if ( ! $this->check_pro_command_preconditions() ) return;
275 try {
276 RSSSL()->admin->activate_recommended_features();
277 } catch ( Exception $e ) {
278 WP_CLI::error( 'Failed to activate recommended features. ' . $e->getMessage() );
279 }
280
281 WP_CLI::success( 'Recommended features activated.' );
282 }
283
284 /**
285 * Deactivate all recommended features via CLI
286 *
287 * return void
288 */
289 public function deactivate_recommended_features() {
290 if ( ! $this->check_pro_command_preconditions() ) return;
291 try {
292 // Deactivate Vulnerability Scanner
293 rsssl_update_option( 'enable_vulnerability_scanner', false );
294
295 // Deactivate essential WordPress hardening features
296 $recommended_hardening_fields = RSSSL()->onboarding->get_hardening_fields();
297 foreach ( $recommended_hardening_fields as $field ) {
298 rsssl_update_option( $field, false );
299 }
300
301 // Disable Email login protection
302 rsssl_update_option( 'login_protection_enabled', false );
303
304 // Disable Mixed Content Fixer
305 rsssl_update_option( 'mixed_content_fixer', false );
306
307 // Disable firewall
308 rsssl_update_option( 'enable_firewall', false );
309 rsssl_update_option( 'event_log_enabled', false );
310 // Check if PRO version is active, then deactivate premium features
311 if ( defined( 'rsssl_pro' ) ) {
312 // Disable Two-Factor Authentication
313 rsssl_update_option( 'two_fa_enabled_roles_totp', [] );
314
315 // Disable Limit Login Attempts
316 rsssl_update_option( 'enable_limited_login_attempts', false );
317
318 // Disable advanced security headers
319 $security_headers = [
320 'upgrade_insecure_requests',
321 'x_content_type_options',
322 'hsts',
323 'x_xss_protection',
324 'x_frame_options',
325 'referrer_policy',
326 'csp_frame_ancestors',
327 ];
328 foreach ( $security_headers as $header_key => $header_value ) {
329 if ( is_string( $header_key ) ) {
330 rsssl_update_option( $header_key, false );
331 } else {
332 rsssl_update_option( $header_value, false );
333 }
334 }
335
336 // Deactivate password security enforcement
337 rsssl_update_option( 'enforce_password_security_enabled', false );
338 rsssl_update_option( 'enable_hibp_check', false );
339 }
340
341 do_action('rsssl_update_rules');
342 WP_CLI::success( 'Recommended features deactivated.' );
343 } catch ( Exception $e ) {
344 WP_CLI::error( 'Failed to deactivate recommended features: ' . $e->getMessage() );
345 }
346 }
347
348 /**
349 * Activate all recommended hardening features via CLI
350 *
351 * return void
352 */
353 public function activate_recommended_hardening_features() {
354 if ( ! $this->check_pro_command_preconditions() ) return;
355 try {
356 $recommended_hardening_fields = RSSSL()->onboarding->get_hardening_fields();
357 foreach ( $recommended_hardening_fields as $field ) {
358 rsssl_update_option( $field, true );
359 }
360 do_action('rsssl_update_rules');
361 WP_CLI::success( 'Recommended hardening features activated.' );
362 } catch ( Exception $e ) {
363 WP_CLI::error( 'Failed to activate recommended hardening features: ' . $e->getMessage() );
364 }
365 }
366
367 /**
368 * Deactivate all recommended features via CLI
369 *
370 * return void
371 */
372 public function deactivate_recommended_hardening_features() {
373 if ( ! $this->check_pro_command_preconditions() ) return;
374 try {
375 $recommended_hardening_fields = RSSSL()->onboarding->get_hardening_fields();
376 foreach ( $recommended_hardening_fields as $field ) {
377 rsssl_update_option( $field, false );
378 }
379 do_action('rsssl_update_rules');
380 WP_CLI::success( 'Recommended hardening features deactivated.' );
381 } catch ( Exception $e ) {
382 WP_CLI::error( 'Failed to deactivate recommended hardening features: ' . $e->getMessage() );
383 }
384 }
385
386
387 /**
388 * Activate recommended security headers via CLI
389 */
390 public function activate_security_headers() {
391 if ( ! $this->check_pro_command_preconditions() ) return;
392 try {
393 foreach (RSSSL()->headers->get_recommended_security_headers() as $header ) {
394 if (isset($header['option_name'], $header['recommended_setting'])) {
395 rsssl_update_option( $header['option_name'], $header['recommended_setting'] );
396 }
397 }
398 WP_CLI::success( 'Recommended security header settings saved. Run "update_advanced_headers" command to activate them.' );
399 do_action('rsssl_update_rules');
400 } catch ( Exception $e ) {
401 WP_CLI::error( 'Failed to activate security headers: ' . $e->getMessage() );
402 }
403 }
404
405
406 /**
407 * Deactivate recommended security headers via CLI
408 */
409 public function deactivate_security_headers() {
410 if ( ! $this->check_pro_command_preconditions() ) return;
411 try {
412 $recommended_headers = RSSSL()->headers->get_recommended_security_headers();
413
414 foreach ( $recommended_headers as $header ) {
415 if ( isset( $header['option_name'] ) && isset( $header['disabled_setting'] ) ) {
416 rsssl_update_option($header['option_name'], $header['disabled_setting']);
417 }
418 }
419 do_action('rsssl_update_rules');
420 WP_CLI::success( 'Recommended security headers deactivated.' );
421 } catch ( Exception $e ) {
422 WP_CLI::error( 'Failed to deactivate security headers: ' . $e->getMessage() );
423 }
424 }
425
426 /**
427 * Activate firewall via CLI
428 *
429 * return void
430 */
431
432 public function activate_firewall() {
433 if ( ! $this->check_pro_command_preconditions() ) return;
434 try {
435 rsssl_update_option( 'enable_firewall', true );
436 rsssl_update_option( 'event_log_enabled', true );
437 do_action('rsssl_update_rules');
438 WP_CLI::success( 'Firewall activated.' );
439 } catch ( Exception $e ) {
440 WP_CLI::error( 'Failed to activate firewall: ' . $e->getMessage() );
441 }
442 }
443
444 /**
445 * Deactivate firewall via CLI
446 *
447 * return void
448 */
449 public function deactivate_firewall() {
450 if ( ! $this->check_pro_command_preconditions() ) return;
451 try {
452 rsssl_update_option( 'enable_firewall', false );
453 rsssl_update_option( 'event_log_enabled', false );
454 do_action('rsssl_update_rules');
455 WP_CLI::success( 'Firewall deactivated.' );
456 } catch ( Exception $e ) {
457 WP_CLI::error( 'Failed to deactivate firewall: ' . $e->getMessage() );
458 }
459 }
460
461 /**
462 * Activate Two-Factor Authentication via CLI
463 *
464 * return void
465 */
466 public function activate_2fa() {
467 if ( ! $this->check_pro_command_preconditions() ) return;
468 try {
469 rsssl_update_option( 'two_fa_enabled_roles_totp', [ 'administrator' ] );
470 rsssl_update_option( 'login_protection_enabled', true );
471 WP_CLI::success( 'Two-Factor Authentication activated.' );
472 } catch ( Exception $e ) {
473 WP_CLI::error( 'Failed to activate Two-Factor Authentication: ' . $e->getMessage() );
474 }
475 }
476
477 /**
478 * Deactivate Two-Factor Authentication via CLI
479 *
480 * return void
481 */
482 public function deactivate_2fa() {
483 if ( ! $this->check_pro_command_preconditions() ) return;
484 try {
485 rsssl_update_option( 'two_fa_enabled_roles_totp', [] );
486 rsssl_update_option( 'login_protection_enabled', false );
487 WP_CLI::success( 'Two-Factor Authentication deactivated.' );
488 } catch ( Exception $e ) {
489 WP_CLI::error( 'Failed to deactivate Two-Factor Authentication: ' . $e->getMessage() );
490 }
491 }
492
493 /**
494 * Activate password security via CLI
495 *
496 * return void
497 */
498 public function activate_password_security() {
499 if ( ! $this->check_pro_command_preconditions() ) return;
500 try {
501 rsssl_update_option( 'enforce_password_security_enabled', true );
502 rsssl_update_option( 'enforce_frequent_password_change', true );
503 rsssl_update_option( 'hide_rememberme', true );
504 rsssl_update_option( 'enable_hibp_check', true );
505 WP_CLI::success( 'Password security features activated.' );
506 } catch ( Exception $e ) {
507 WP_CLI::error( 'Failed to activate password security: ' . $e->getMessage() );
508 }
509 }
510
511 /**
512 * Deactivate password security via CLI
513 *
514 * return void
515 */
516 public function deactivate_password_security() {
517 if ( ! $this->check_pro_command_preconditions() ) return;
518 try {
519 rsssl_update_option( 'enforce_password_security_enabled', false );
520 rsssl_update_option( 'enforce_frequent_password_change', false );
521 rsssl_update_option( 'hide_rememberme', false );
522 rsssl_update_option( 'enable_hibp_check', false );
523 do_action('rsssl_update_rules');
524 WP_CLI::success( 'Password security features deactivated.' );
525 } catch ( Exception $e ) {
526 WP_CLI::error( 'Failed to deactivate password security: ' . $e->getMessage() );
527 }
528 }
529
530 /**
531 * Activate login attempts limitation via CLI
532 *
533 * return void
534 */
535 public function activate_lla() {
536 if ( ! $this->check_pro_command_preconditions() ) return;
537 try {
538 rsssl_update_option( 'enable_limited_login_attempts', true );
539 rsssl_update_option( 'event_log_enabled', true );
540 WP_CLI::success( 'Limit login attempts activated.' );
541 do_action('rsssl_update_rules');
542 } catch ( Exception $e ) {
543 WP_CLI::error( 'Failed to activate limit login attempts: ' . $e->getMessage() );
544 }
545 }
546
547 /**
548 * Deactivate login attempts limitation via CLI
549 *
550 * return void
551 */
552 public function deactivate_lla() {
553 if ( ! $this->check_pro_command_preconditions() ) return;
554 try {
555 rsssl_update_option( 'enable_limited_login_attempts', false );
556 rsssl_update_option( 'event_log_enabled', false );
557 do_action('rsssl_update_rules');
558 WP_CLI::success( 'Limit login attempts deactivated.' );
559 } catch ( Exception $e ) {
560 WP_CLI::error( 'Failed to deactivate limit login attempts: ' . $e->getMessage() );
561 }
562 }
563
564 /**
565 * Activate vulnerability scanning via CLI
566 *
567 * return void
568 */
569 public function activate_vulnerability_scanning() {
570 if ( ! $this->check_pro_command_preconditions() ) return;
571 try {
572 rsssl_update_option( 'enable_vulnerability_scanner', true );
573
574 WP_CLI::success( 'Vulnerability scanning activated.' );
575 } catch ( Exception $e ) {
576 WP_CLI::error( 'Failed to activate vulnerability scanning: ' . $e->getMessage() );
577 }
578 }
579
580 /**
581 * Deactivate vulnerability scanning via CLI
582 *
583 * return void
584 */
585 public function deactivate_vulnerability_scanning() {
586 if ( ! $this->check_pro_command_preconditions() ) return;
587 try {
588 rsssl_update_option( 'enable_vulnerability_scanner', false );
589
590 WP_CLI::success( 'Vulnerability scanning deactivated.' );
591 } catch ( Exception $e ) {
592 WP_CLI::error( 'Failed to deactivate vulnerability scanning: ' . $e->getMessage() );
593 }
594 }
595
596 /**
597 * Activate license via CLI
598 *
599 * @param array $args Positional arguments. License should be passed as first and only argument
600 *
601 * @return void
602 */
603 public function activate_license( $args ) {
604 if ( ! $this->check_pro_command_preconditions(true) ) return;
605 try {
606 // Check if license key is provided
607 if ( empty( $args[0] ) ) {
608 WP_CLI::error( 'Please provide a license key: wp rsssl activate_license YOUR_LICENSE_KEY' );
609
610 return;
611 }
612
613 $license_key = sanitize_text_field( $args[0] );
614
615 rsssl_update_option( 'license', $this->encrypt_with_prefix( $license_key, 'really_simple_ssl_' ) );
616 $status = RSSSL()->licensing->get_license_status( 'check_license', true );
617
618 update_option( 'rsssl_onboarding_dismissed', true, false );
619
620 if ( $status === 'valid' ) {
621 WP_CLI::success( 'License activated successfully.' );
622 } elseif ( $status === 'invalid' || $status === 'missing' ) {
623 WP_CLI::error( 'Invalid license key. You can find your license key on https://really-simple-ssl.com/account' );
624 } elseif ( $status === 'expired' ) {
625 WP_CLI::error( 'License has expired. Please renew via https://really-simple-ssl.com/account/subscriptions' );
626 } elseif ( $status === 'no_activations_left' ) {
627 WP_CLI::error( 'No activations left. Please upgrade your license via https://really-simple-ssl.com/account/subscriptions' );
628 } elseif ( $status === 'disabled' ) {
629 WP_CLI::error( 'This license is not valid. Find out why on your account page at https://really-simple-ssl.com/account' );
630 }
631 } catch ( Exception $e ) {
632 WP_CLI::error( 'Failed to activate license: ' . $e->getMessage() );
633 }
634 }
635
636 /**
637 * Deactivate license via CLI
638 *
639 * @return void
640 */
641 public function deactivate_license() {
642 if ( ! $this->check_pro_command_preconditions() ) return;
643 try {
644 rsssl_update_option( 'license', '' );
645 $status = RSSSL()->licensing->get_license_status( 'check_license', true );
646 update_option( 'rsssl_onboarding_dismissed', true, false );
647
648 // License key should now be empty
649 if ( $status === 'empty' ) {
650 WP_CLI::success( 'License deactivated successfully.' );
651 } else {
652 WP_CLI::error( 'Something went wrong when deactivating your license. Please try again.' );
653 }
654
655 } catch ( Exception $e ) {
656 WP_CLI::error( 'Failed to deactivate license: ' . $e->getMessage() );
657 }
658 }
659
660 /**
661 * Add lock file for safe mode
662 *
663 * @return void
664 */
665 public function add_lock_file() {
666 if ( ! $this->check_pro_command_preconditions() ) return;
667 try {
668 $lock_file = WP_CONTENT_DIR . '/rsssl-safe-mode.lock';
669
670 // Check if file already exists
671 if ( file_exists( $lock_file ) ) {
672 WP_CLI::warning( 'Lock file already exists.' );
673
674 return;
675 }
676
677 // Create lock file
678 $result = file_put_contents( $lock_file, time() );
679
680 if ( $result === false ) {
681 WP_CLI::error( 'Unable to create lock file.' );
682 }
683
684 // Set proper permissions
685 chmod( $lock_file, 0644 );
686
687 WP_CLI::success( 'Safe mode lock file created successfully.' );
688 } catch ( Exception $e ) {
689 WP_CLI::error( 'Failed to create lock file: ' . $e->getMessage() );
690 }
691 }
692
693 /**
694 * Remove lock file for safe mode
695 *
696 * @return void
697 */
698 public function remove_lock_file() {
699 if ( ! $this->check_pro_command_preconditions() ) return;
700 try {
701 $lock_file = WP_CONTENT_DIR . '/rsssl-safe-mode.lock';
702
703 // Check if file exists
704 if ( ! file_exists( $lock_file ) ) {
705 WP_CLI::warning( 'Lock file does not exist.' );
706
707 return;
708 }
709
710 // Remove lock file
711 if ( ! unlink( $lock_file ) ) {
712 WP_CLI::error( 'Unable to remove lock file.' );
713 }
714
715 WP_CLI::success( 'Safe mode lock file removed successfully.' );
716 } catch ( Exception $e ) {
717 WP_CLI::error( 'Failed to remove lock file: ' . $e->getMessage() );
718 }
719 }
720
721 /**
722 * Reset the 2FA status of a user to disabled
723 *
724 * Usage: wp rsssl reset_2fa 123
725 *
726 * @param array $args User ID should be the first element
727 *
728 * @throws \WP_CLI\ExitException
729 */
730 public function reset_2fa( $args ): void
731 {
732 if ( ! $this->check_pro_command_preconditions() ) return;
733 // When empty array is passed, WP_CLI will return an error
734 if ( empty( $args ) ) {
735 WP_CLI::error( 'Please provide a user ID.', true );
736 }
737 $user_id = intval( $args[0] );
738 $user = get_user_by('id', $user_id);
739
740 if (empty($user)) {
741 WP_CLI::error('User not found.', true);
742 }
743
744 if (!class_exists('Rsssl_Two_Fa_Status')) {
745 require_once rsssl_path . '/security/wordpress/two-fa/class-rsssl-two-fa-status.php';
746 }
747
748 if ( $user ) {
749 // Delete all 2fa related user meta.
750 Rsssl_Two_Fa_Status::delete_two_fa_meta( $user->ID );
751 // Set the last login to now, so the user will be forced to use 2fa.
752 update_user_meta( $user->ID, 'rsssl_two_fa_last_login', gmdate( 'Y-m-d H:i:s' ) );
753 }
754
755 WP_CLI::success( 'Successfully reset 2FA for user id ' . $user_id );
756 }
757
758 /**
759 * Update the advanced-headers.php with the latest rules
760 *
761 * @return void
762 */
763 public function update_advanced_headers() {
764 if ( ! $this->check_pro_command_preconditions() ) return;
765 do_action('rsssl_update_rules');
766 WP_CLI::success( 'Successfully update advanced headers.' );
767 }
768
769 /**
770 * Add an IP to the firewall blocklist.
771 *
772 * @example wp rsssl add_firewall_ip_block 123.123.123.1 --note="This is a temporary block"
773 * @example wp rsssl add_firewall_ip_block 123.123.123.1 --permanent --note="This is a permanent block"
774 *
775 * @param array $args Should contain IP as the first element
776 * @param array $assoc_args Can contain a note with a 'note' key
777 */
778 public function add_firewall_ip_block(array $args, array $assoc_args): void
779 {
780 if ( ! $this->check_pro_command_preconditions() ) return;
781 $this->handleFirewallTableEntry($args, $assoc_args, 'blocked', 'add');
782 }
783
784 /**
785 * Can be used to remove a (temporary) block from the firewall blocklist.
786 * @example wp rsssl remove_firewall_ip_block 123.123.123.1
787 *
788 * @param $args array Should contain the ip address
789 */
790 public function remove_firewall_ip_block(array $args, array $assoc_args ): void
791 {
792 if ( ! $this->check_pro_command_preconditions() ) return;
793 $this->handleFirewallTableEntry($args, $assoc_args, 'blocked', 'remove');
794 }
795
796 /**
797 * Return a table of the current blocked IPs with the headers:
798 * IP Address, Note, Permanent
799 */
800 public function show_blocked_ips() {
801 if ( ! $this->check_pro_command_preconditions() ) return;
802 $columns = [
803 'ip_address',
804 'note',
805 'permanent',
806 ];
807
808 $blockedIps = ( new Rsssl_404_Block() )->get_blocked_ips($columns);
809
810 WP_CLI\Utils\format_items('table', $blockedIps, $columns);
811 }
812
813 /**
814 * Add an IP to the firewall's trusted list.
815 *
816 * Usage: wp rsssl add_firewall_trusted_ip 123.123.123.1
817 *
818 * @param array $args Should contain IP as the first element
819 * @param array $assoc_args Can contain a note with a 'note' key
820 * @uses handleFirewallTableEntry()
821 */
822 public function add_firewall_trusted_ip(array $args, array $assoc_args) {
823 if ( ! $this->check_pro_command_preconditions() ) return;
824 $this->handleFirewallTableEntry($args, $assoc_args, 'trusted', 'add');
825 }
826
827 /**
828 * Remove an IP from the firewall's trusted list.
829 *
830 * Usage: wp rsssl remove_firewall_trusted_ip 123.123.123.1
831 *
832 * @param array $args Should contain IP as the first element
833 * @param array $assoc_args Can contain a note with a 'note' key
834 * @uses handleFirewallTableEntry()
835 */
836 public function remove_firewall_trusted_ip(array $args, array $assoc_args) {
837 if ( ! $this->check_pro_command_preconditions() ) return;
838 $this->handleFirewallTableEntry($args, $assoc_args, 'trusted', 'remove');
839 }
840
841 /**
842 * Add an IP to the LLA's trusted list.
843 *
844 * Usage: wp rsssl add_lla_trusted_ip 123.123.123.1
845 *
846 * @param array $args Command arguments.
847 * @uses handleLlaTableEntry()
848 */
849 public function add_lla_trusted_ip( $args ) {
850 if ( ! $this->check_pro_command_preconditions() ) return;
851 $this->handleLlaTableEntry($args, 'allowed', 'source_ip', 'add');
852 }
853
854 /**
855 * Add an IP to the LLA's blocklist.
856 *
857 * Usage: wp rsssl remove_lla_trusted_ip 123.123.123.1
858 *
859 * @param array $args Command arguments.
860 * @uses handleLlaTableEntry()
861 */
862 public function remove_lla_trusted_ip( $args ) {
863 if ( ! $this->check_pro_command_preconditions() ) return;
864 $this->handleLlaTableEntry($args, 'allowed', 'source_ip', 'remove');
865 }
866
867 /**
868 * Remove an IP from the LLA's trusted list.
869 *
870 * Usage: wp rsssl add_lla_blocked_ip 123.123.123.1
871 * Usage: wp rsssl add_lla_blocked_ip 123.123.123.1 --permanent
872 *
873 * @param array $args Command arguments.
874 * @param array $assoc_args Associative arguments.
875 * @uses handleLlaTableEntry()
876 */
877 public function add_lla_blocked_ip( $args, $assoc_args ) {
878 if ( ! $this->check_pro_command_preconditions() ) return;
879 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
880 $this->handleLlaTableEntry($args, $status, 'source_ip', 'add');
881 }
882
883 /**
884 * Remove an IP from the LLA's blocklist.
885 *
886 * Usage: wp rsssl remove_lla_blocked_ip 123.123.123.1
887 * Usage: wp rsssl remove_lla_blocked_ip 123.123.123.1 --permanent
888 *
889 * @param array $args Command arguments.
890 * @param array $assoc_args Associative arguments.
891 * @uses handleLlaTableEntry()
892 */
893 public function remove_lla_blocked_ip( $args, $assoc_args ) {
894 if ( ! $this->check_pro_command_preconditions() ) return;
895 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
896 $this->handleLlaTableEntry($args, $status, 'source_ip', 'remove');
897 }
898
899 /**
900 * Add a username to the LLA's trusted list.
901 *
902 * Usage: wp rsssl add_lla_trusted_username username
903 *
904 * @param array $args Command arguments.
905 * @uses handleLlaTableEntry()
906 */
907 public function add_lla_trusted_username( $args ) {
908 if ( ! $this->check_pro_command_preconditions() ) return;
909 $this->handleLlaTableEntry($args, 'allowed', 'username', 'add');
910 }
911
912 /**
913 * Remove a username to the LLA's trusted list.
914 *
915 * Usage: wp rsssl remove_lla_trusted_username username
916 *
917 * @param array $args Command arguments.
918 * @uses handleLlaTableEntry()
919 */
920 public function remove_lla_trusted_username( $args ) {
921 if ( ! $this->check_pro_command_preconditions() ) return;
922 $this->handleLlaTableEntry($args, 'allowed', 'username', 'remove');
923 }
924
925 /**
926 * Add a username to the LLA's blocked list.
927 *
928 * Usage: wp rsssl add_lla_blocked_username username
929 * Usage: wp rsssl add_lla_blocked_username username --permanent
930 *
931 * @param array $args Command arguments.
932 * @param array $assoc_args Associative arguments.
933 * @uses handleLlaTableEntry()
934 */
935 public function add_lla_blocked_username( array $args, array $assoc_args ) {
936 if ( ! $this->check_pro_command_preconditions() ) return;
937 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
938 $this->handleLlaTableEntry($args, $status, 'username', 'add');
939 }
940
941 /**
942 * Remove a username to the LLA's blocked list.
943 *
944 * Usage: wp rsssl remove_lla_blocked_username username
945 * Usage: wp rsssl remove_lla_blocked_username username --permanent
946 *
947 * @param array $args Command arguments.
948 * @param array $assoc_args Associative arguments.
949 * @uses handleLlaTableEntry()
950 */
951 public function remove_lla_blocked_username( $args, $assoc_args ) {
952 if ( ! $this->check_pro_command_preconditions() ) return;
953 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
954 $this->handleLlaTableEntry($args, $status, 'username', 'remove');
955 }
956
957 /**
958 * Handle an action for the firewall table for a specific IP address.
959 *
960 * @param array $args Command arguments.
961 * @param array $assoc_args Associative arguments.
962 * @param string $status Should be either 'trusted' or 'blocked'.
963 * @param string $action Should be either 'add' or 'remove'.
964 *
965 * @uses remove_white_list_ip() & add_white_list_ip() from Rsssl_Geo_Block -
966 * Those also handle a block request for an IP address.
967 */
968 protected function handleFirewallTableEntry(array $args, array $assoc_args, string $status, string $action)
969 {
970 if (rsssl_get_option('enable_firewall', false) !== true) {
971 WP_CLI::error('The firewall is not enabled.', true);
972 }
973
974 if (!in_array($status, ['trusted', 'blocked']) || !in_array($action, ['add', 'remove'])) {
975 WP_CLI::error('Could not handle action for the firewall table.', true);
976 }
977
978 if (empty($args[0])) {
979 WP_CLI::error('Please provide an IP address.', true);
980 }
981
982 $ip = $this->getFilteredIpAddress($args[0]);
983
984 // Prepare data for adding to the whitelist.
985 $data = [
986 'ip_address' => $ip,
987 'note' => $assoc_args['note'] ?? '',
988 'status' => $status,
989 'permanent' => isset($assoc_args['permanent']),
990 ];
991
992 // Use the Rsssl_Geo_Block class to add the trusted IP.
993 if (!class_exists('\RSSSL\Pro\Security\WordPress\Rsssl_Geo_Block')) {
994 require_once rsssl_path . 'pro/security/wordpress/rsssl-geo-block.php';
995 }
996
997 try {
998 $geo_block = new \RSSSL\Pro\Security\WordPress\Rsssl_Geo_Block();
999
1000 // fallback
1001 $response = ['success' => false, 'message' => 'Something went wrong!'];
1002
1003 if ($action === 'remove') {
1004 $response = $geo_block->remove_white_list_ip( $data );
1005 }
1006
1007 if ($action === 'add') {
1008 $response = $geo_block->add_white_list_ip( $data );
1009 }
1010 } catch ( \Exception $e ) {
1011 WP_CLI::error( 'Failed to handle IP entry: ' . $e->getMessage(), true );
1012 }
1013
1014 // Handle response.
1015 if ( $response['success'] ) {
1016 WP_CLI::success( $response['message'] );
1017 return;
1018 }
1019
1020 WP_CLI::error( $response['message'], true );
1021 }
1022
1023 /**
1024 * Handle an action for the LLA table for a specific IP address.
1025 *
1026 * @param array $args Command arguments.
1027 * @param string $status Should be either 'allowed' or 'blocked'.
1028 * @param string $type Should be either 'source_ip' or 'username'.
1029 * @param string $action Should be either 'add' or 'remove'.
1030 * @return void
1031 */
1032 protected function handleLlaTableEntry(array $args, string $status, string $type, string $action): void
1033 {
1034 if (rsssl_get_option('enable_limited_login_attempts', false) !== true) {
1035 WP_CLI::error('The LLA feature is not enabled.', true);
1036 }
1037
1038 if (empty($args[0])) {
1039 WP_CLI::error('Please provide the command the necessary arguments', true);
1040 }
1041
1042 if (!in_array($status, ['allowed', 'blocked', 'locked']) || !in_array($type, ['source_ip', 'username'])) {
1043 WP_CLI::error('Something went wrong! Could not handle command.', true);
1044 }
1045
1046 $value = '';
1047 if ($type === 'source_ip') {
1048 $value = $this->getFilteredIpAddress($args[0]);
1049 }
1050
1051 if ($type === 'username') {
1052 $value = sanitize_text_field($args[0]);
1053 }
1054
1055 // Use the Rsssl_Limit_Login_Admin class to add the trusted IP.
1056 if (!class_exists('\RSSSL\Pro\Security\WordPress\Rsssl_Limit_Login_Admin')) {
1057 require_once rsssl_path . 'pro/security/wordpress/class-rsssl-limit-login-admin.php';
1058 }
1059
1060 try {
1061 $lla = new \RSSSL\Pro\Security\WordPress\Rsssl_Limit_Login_Admin();
1062
1063 // fallback
1064 $response = ['success' => false, 'message' => 'Something went wrong!'];
1065
1066 if ($action === 'add') {
1067 $response = $lla->handle_entity([
1068 'value' => $value,
1069 'status' => sanitize_text_field($status),
1070 ], $type);
1071 }
1072
1073 if ($action === 'remove') {
1074 $entry = $lla->get_entry($type, $value, $status);
1075 $response = $lla->delete_entries([
1076 'id' => $entry['id'],
1077 ]);
1078 }
1079 } catch ( Exception $e ) {
1080 WP_CLI::error( 'Failed to handle LLA entry: ' . $e->getMessage(), true );
1081 }
1082
1083 // Handle response.
1084 if ( $response['success'] ) {
1085 WP_CLI::success( $response['message'] );
1086 return;
1087 }
1088
1089 WP_CLI::error( $response['message'], true );
1090 }
1091
1092 /**
1093 * Return a filtered IP address. Method will exit() if the IP address is
1094 * invalid with the WP_CLI error message: Invalid IP address provided.
1095 */
1096 protected function getFilteredIpAddress(string $originalIp): string
1097 {
1098 // Check if the input is potentially a CIDR
1099 if (strpos($originalIp, '/') !== false) {
1100 list($address, $mask_str) = explode('/', $originalIp, 2);
1101
1102 // Validate the IP address part
1103 if (!filter_var($address, FILTER_VALIDATE_IP)) {
1104 WP_CLI::error('Invalid IP address part in CIDR notation: ' . $address, true);
1105 }
1106
1107 // Validate the mask part
1108 if (!is_numeric($mask_str)) {
1109 WP_CLI::error('CIDR mask is not numeric: ' . $mask_str, true);
1110 }
1111 $mask = (int)$mask_str;
1112
1113 // Determine IP version for mask validation
1114 $is_ipv4 = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
1115 $is_ipv6 = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
1116
1117 if ($is_ipv4) {
1118 if ($mask < 0 || $mask > 32) {
1119 WP_CLI::error('Invalid IPv4 CIDR mask (must be 0-32): ' . $mask, true);
1120 }
1121 } elseif ($is_ipv6) {
1122 if ($mask < 0 || $mask > 128) {
1123 WP_CLI::error('Invalid IPv6 CIDR mask (must be 0-128): ' . $mask, true);
1124 }
1125 } else {
1126 // This case should ideally not be reached if filter_var($address, FILTER_VALIDATE_IP) passed
1127 WP_CLI::error('Unknown IP address type for CIDR validation: ' . $address, true);
1128 }
1129
1130 // If all checks pass for CIDR, return the original CIDR string
1131 return $originalIp;
1132
1133 } else {
1134 // Validate as a plain IP address
1135 $ip = filter_var($originalIp, FILTER_VALIDATE_IP);
1136 if (empty($ip)) {
1137 WP_CLI::error('Invalid IP address provided: ' . $originalIp, true);
1138 }
1139 return $ip;
1140 }
1141 }
1142
1143 /**
1144 * Performs pre-flight checks before SSL activation.
1145 * Checks for HTTPS reachability and potentially other issues like .htaccess writability.
1146 *
1147 * @return array ['success' => bool, 'message' => string, 'warnings' => array]
1148 */
1149 private function perform_pre_flight_checks(): array {
1150 $warnings = [];
1151 $message = '';
1152
1153 // --- Check 1: HTTPS Reachability ---
1154 $home_url = home_url();
1155 $https_url = set_url_scheme( $home_url, 'https' );
1156
1157 // Use wp_remote_get to see if the HTTPS version is reachable
1158 // 'sslverify' => false is important for local/staging with self-signed certs
1159 // Timeout set low to avoid long waits on failure
1160 $response = wp_remote_get( $https_url, [
1161 'timeout' => 10, // seconds
1162 'sslverify' => false,
1163 'redirection' => 5, // Follow redirects
1164 ] );
1165
1166 if ( is_wp_error( $response ) ) {
1167 $error_code = $response->get_error_code();
1168 $error_message = $response->get_error_message();
1169 $friendly_message = sprintf(
1170 __( 'Failed to reach %s. The site does not appear to be accessible over HTTPS. Please ensure your server is configured for SSL.', 'really-simple-ssl' ),
1171 $https_url
1172 );
1173
1174 // Check if WP_DEBUG is enabled
1175 $wp_debug_enabled = ( defined( 'WP_DEBUG' ) && WP_DEBUG );
1176
1177 if ( $wp_debug_enabled ) {
1178 // Log the detailed error when WP_DEBUG is on
1179 // Using WP_CLI::debug requires the --debug flag for wp-cli command itself
1180 WP_CLI::debug( sprintf( "HTTPS Check Error Details: Code=%s, Message=%s", $error_code, $error_message ), 'rsssl-cli-debug' );
1181 // Alternatively, or in addition, use standard PHP error logging:
1182 // error_log( sprintf("Really Simple SSL WP-CLI HTTPS Check Error: Code=%s, Message=%s", $error_code, $error_message) );
1183
1184 // Optionally, still show a slightly more informative message than the friendly one
1185 $message_to_show = sprintf(
1186 __( 'Failed to reach %s. The site does not appear to be accessible over HTTPS (Error: %s). Check debug logs for details.', 'really-simple-ssl' ),
1187 $https_url,
1188 $error_code // Show the code, but maybe not the full verbose message
1189 );
1190 } else {
1191 // Show only the user-friendly message if WP_DEBUG is off
1192 $message_to_show = $friendly_message;
1193 }
1194
1195 return [
1196 'success' => false,
1197 'message' => $message_to_show,
1198 'warnings' => $warnings
1199 ];
1200
1201 } else {
1202 // Connected, check the response code
1203 $response_code = wp_remote_retrieve_response_code( $response );
1204 if ( $response_code < 200 || $response_code >= 400 ) {
1205 // Reached server, but got an error response (e.g., 404 Not Found, 500 Internal Server Error)
1206 return [
1207 'success' => false,
1208 'message' => sprintf( __( 'Reached %s, but received an error response code: %d. HTTPS is not properly configured.', 'really-simple-ssl' ), $https_url, $response_code ),
1209 'warnings' => $warnings
1210 ];
1211 }
1212 // If response code is 2xx or 3xx, we consider HTTPS reachable.
1213 // A more robust check could analyze the body for expected content, but this is usually sufficient.
1214 }
1215
1216 // --- Check 2: .htaccess Writability (if needed) ---
1217 // Keep the previous check for .htaccess if the redirect method is set to htaccess
1218 // $htaccess_writable = true; // Replace with actual check logic (e.g., check if WP_Filesystem allows writing)
1219 if ( rsssl_get_option('redirect') === 'htaccess' ) {
1220 // Get the path to the .htaccess file
1221 $htaccess_file = RSSSL()->admin->htaccess_file(); // Assuming a method to get the correct path
1222 if ( ! is_writable( $htaccess_file ) ) {
1223 $warnings[] = sprintf( __( '.htaccess file (%s) is not writable. Redirects cannot be configured automatically.', 'really-simple-ssl' ), $htaccess_file );
1224 // This remains a warning, as activation might still work partially (WP URLs change)
1225 }
1226 }
1227
1228 // Add more checks as needed (e.g., specific certificate details if possible/required)...
1229
1230 $message = __( 'Pre-flight checks passed.', 'really-simple-ssl' );
1231 return ['success' => true, 'message' => $message, 'warnings' => $warnings];
1232 }
1233
1234 /**
1235 * Get command details for WP-CLI commands.
1236 *
1237 * @return array Command details.
1238 */
1239 protected function get_command_list() {
1240 return [
1241 'activate_ssl' => [
1242 'description' => __( 'Activate SSL on the site.', 'really-simple-ssl' ),
1243 'synopsis' => [],
1244 'pro' => false,
1245 ],
1246 'deactivate_ssl' => [
1247 'description' => __( 'Deactivate SSL on the site.', 'really-simple-ssl' ),
1248 'synopsis' => [],
1249 'pro' => false,
1250 ],
1251 'update_option' => [
1252 'description' => __( 'Update a Really Simple Security option. Usage: wp rsssl update_option --name=option_name --value=option_value. Use 0 and 1 for booleans.', 'really-simple-ssl' ),
1253 'synopsis' => [
1254 [
1255 'type' => 'assoc',
1256 'name' => 'name',
1257 'optional' => false,
1258 'description' => __( 'Name of the option to update.', 'really-simple-ssl' ),
1259 ],
1260 [
1261 'type' => 'assoc',
1262 'name' => 'value',
1263 'optional' => false,
1264 'description' => __( 'Value to set for the option.', 'really-simple-ssl' ),
1265 ],
1266 ],
1267 'pro' => false,
1268 ],
1269 'activate_recommended_features' => [
1270 'description' => __( 'Activate all recommended features.', 'really-simple-ssl' ),
1271 'synopsis' => [],
1272 'pro' => false,
1273 ],
1274 'deactivate_recommended_features' => [
1275 'description' => __( 'Deactivate all recommended features.', 'really-simple-ssl' ),
1276 'synopsis' => [],
1277 'pro' => false,
1278 ],
1279 'activate_security_headers' => [
1280 'description' => __( 'Activate essential security headers.', 'really-simple-ssl' ),
1281 'synopsis' => [],
1282 'pro' => true,
1283 ],
1284 'deactivate_security_headers' => [
1285 'description' => __( 'Deactivate essential security headers.', 'really-simple-ssl' ),
1286 'synopsis' => [],
1287 'pro' => true,
1288 ],
1289 'activate_firewall' => [
1290 'description' => __( 'Activate the firewall.', 'really-simple-ssl' ),
1291 'synopsis' => [],
1292 'pro' => true,
1293 ],
1294 'deactivate_firewall' => [
1295 'description' => __( 'Deactivate the firewall.', 'really-simple-ssl' ),
1296 'synopsis' => [],
1297 'pro' => true,
1298 ],
1299 'activate_2fa' => [
1300 'description' => __( 'Activate Two-Factor Authentication.', 'really-simple-ssl' ),
1301 'synopsis' => [],
1302 'pro' => false,
1303 ],
1304 'deactivate_2fa' => [
1305 'description' => __( 'Deactivate Two-Factor Authentication.', 'really-simple-ssl' ),
1306 'synopsis' => [],
1307 'pro' => false,
1308 ],
1309 'activate_password_security' => [
1310 'description' => __( 'Activate password security features.', 'really-simple-ssl' ),
1311 'synopsis' => [],
1312 'pro' => true,
1313 ],
1314 'deactivate_password_security' => [
1315 'description' => __( 'Deactivate password security features.', 'really-simple-ssl' ),
1316 'synopsis' => [],
1317 'pro' => true,
1318 ],
1319 'activate_lla' => [
1320 'description' => __( 'Activate limit login attempts.', 'really-simple-ssl' ),
1321 'synopsis' => [],
1322 'pro' => true,
1323 ],
1324 'deactivate_lla' => [
1325 'description' => __( 'Deactivate limit login attempts.', 'really-simple-ssl' ),
1326 'synopsis' => [],
1327 'pro' => true,
1328 ],
1329 'activate_vulnerability_scanning' => [
1330 'description' => __( 'Activate vulnerability scanning.', 'really-simple-ssl' ),
1331 'synopsis' => [],
1332 'pro' => false,
1333 ],
1334 'deactivate_vulnerability_scanning' => [
1335 'description' => __( 'Deactivate vulnerability scanning.', 'really-simple-ssl' ),
1336 'synopsis' => [],
1337 'pro' => false,
1338 ],
1339 'activate_license' => [
1340 'description' => __( 'Activate a license key. Usage: wp rsssl activate_license YOUR_LICENSE_KEY.', 'really-simple-ssl' ),
1341 'synopsis' => [
1342 [
1343 'type' => 'positional',
1344 'name' => 'license_key',
1345 'optional' => false,
1346 'description' => __( 'The license key to activate.', 'really-simple-ssl' ),
1347 ],
1348 ],
1349 'pro' => true,
1350 ],
1351 'deactivate_license' => [
1352 'description' => __( 'Deactivate the license.', 'really-simple-ssl' ),
1353 'synopsis' => [],
1354 'pro' => true,
1355 ],
1356 'add_lock_file' => [
1357 'description' => __( 'Add a lock file for safe mode.', 'really-simple-ssl' ),
1358 'synopsis' => [],
1359 'pro' => false,
1360 ],
1361 'remove_lock_file' => [
1362 'description' => __( 'Remove the lock file for safe mode.', 'really-simple-ssl' ),
1363 'synopsis' => [],
1364 'pro' => false,
1365 ],
1366 'reset_2fa' => [
1367 'description' => __( 'Reset the 2FA status of a user to disabled.', 'really-simple-ssl' ),
1368 'synopsis' => [],
1369 'pro' => false,
1370 ],
1371 'update_advanced_headers' => [
1372 'description' => __( 'Update the advanced-headers.php with the latest rules.', 'really-simple-ssl' ),
1373 'synopsis' => [],
1374 'pro' => false,
1375 ],
1376 'add_firewall_ip_block' => [
1377 'description' => __( 'Add IP block.', 'really-simple-ssl' ),
1378 'synopsis' => [
1379 [
1380 'type' => 'positional',
1381 'name' => 'ip_address',
1382 'optional' => false,
1383 'description' => __( 'The IP to block.', 'really-simple-ssl' ),
1384 ],
1385 [
1386 'type' => 'flag',
1387 'name' => 'permanent',
1388 'optional' => true,
1389 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1390 ],
1391 [
1392 'type' => 'assoc',
1393 'name' => 'note',
1394 'optional' => true,
1395 'description' => __( 'Optional note for the block.', 'really-simple-ssl' ),
1396 ],
1397 ],
1398 'pro' => true,
1399 ],
1400 'remove_firewall_ip_block' => [
1401 'description' => __( 'Remove IP block.', 'really-simple-ssl' ),
1402 'synopsis' => [
1403 [
1404 'type' => 'positional',
1405 'name' => 'ip_address',
1406 'optional' => false,
1407 'description' => __( 'The IP to remove the block for.', 'really-simple-ssl' ),
1408 ],
1409 ],
1410 'pro' => true,
1411 ],
1412 'show_blocked_ips' => [
1413 'description' => __( 'Show blocked IP\'s.', 'really-simple-ssl' ),
1414 'synopsis' => [],
1415 'pro' => true,
1416 ],
1417 'add_firewall_trusted_ip' => [
1418 'description' => __( 'Add a trusted IP to the firewall.', 'really-simple-ssl' ),
1419 'synopsis' => [],
1420 'pro' => true,
1421 ],
1422 'remove_firewall_trusted_ip' => [
1423 'description' => __( 'Remove a trusted IP from the firewall.', 'really-simple-ssl' ),
1424 'synopsis' => [],
1425 'pro' => true,
1426 ],
1427 'add_lla_trusted_ip' => [
1428 'description' => __( 'Add a trusted IP to the limit login attempts table.', 'really-simple-ssl' ),
1429 'synopsis' => [],
1430 'pro' => true,
1431 ],
1432 'remove_lla_trusted_ip' => [
1433 'description' => __( 'Remove a trusted IP from the limit login attempts table.', 'really-simple-ssl' ),
1434 'synopsis' => [],
1435 'pro' => true,
1436 ],
1437 'add_lla_blocked_ip' => [
1438 'description' => __( 'Add a blocked IP to the limit login attempts table.', 'really-simple-ssl' ),
1439 'synopsis' => [
1440 [
1441 'type' => 'positional',
1442 'name' => 'ip_address',
1443 'optional' => false,
1444 'description' => __( 'The IP to block.', 'really-simple-ssl' ),
1445 ],
1446 [
1447 'type' => 'flag',
1448 'name' => 'permanent',
1449 'optional' => true,
1450 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1451 ],
1452 ],
1453 'pro' => true,
1454 ],
1455 'remove_lla_blocked_ip' => [
1456 'description' => __( 'Remove a blocked IP from the limit login attempts table.', 'really-simple-ssl' ),
1457 'synopsis' => [
1458 [
1459 'type' => 'positional',
1460 'name' => 'ip_address',
1461 'optional' => false,
1462 'description' => __( 'The IP to block.', 'really-simple-ssl' ),
1463 ],
1464 [
1465 'type' => 'flag',
1466 'name' => 'permanent',
1467 'optional' => true,
1468 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1469 ],
1470 ],
1471 'pro' => true,
1472 ],
1473 'add_lla_trusted_username' => [
1474 'description' => __( 'Add a trusted username to the limit login attempts table.', 'really-simple-ssl' ),
1475 'synopsis' => [],
1476 'pro' => true,
1477 ],
1478 'remove_lla_trusted_username' => [
1479 'description' => __( 'Remove a trusted username from the limit login attempts table.', 'really-simple-ssl' ),
1480 'synopsis' => [],
1481 'pro' => true,
1482 ],
1483 'add_lla_blocked_username' => [
1484 'description' => __( 'Add a blocked username to the limit login attempts table.', 'really-simple-ssl' ),
1485 'synopsis' => [
1486 [
1487 'type' => 'positional',
1488 'name' => 'ip_address',
1489 'optional' => false,
1490 'description' => __( 'The username to block.', 'really-simple-ssl' ),
1491 ],
1492 [
1493 'type' => 'flag',
1494 'name' => 'permanent',
1495 'optional' => true,
1496 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1497 ],
1498 ],
1499 'pro' => true,
1500 ],
1501 'remove_lla_blocked_username' => [
1502 'description' => __( 'Remove a blocked username from the limit login attempts table.', 'really-simple-ssl' ),
1503 'synopsis' => [
1504 [
1505 'type' => 'positional',
1506 'name' => 'username',
1507 'optional' => false,
1508 'description' => __( 'The username to remove the block for.', 'really-simple-ssl' ),
1509 ],
1510 [
1511 'type' => 'flag',
1512 'name' => 'permanent',
1513 'optional' => true,
1514 'description' => __( 'Flag to remove a permanent block.', 'really-simple-ssl' ),
1515 ],
1516 ],
1517 'pro' => true,
1518 ],
1519 ];
1520 }
1521
1522 /**
1523 * This method registers our WP-CLI commands and uses {@see get_command_list()}
1524 * to retrieve the list. Do not execute this method before the init hook.
1525 */
1526 public function register_wp_cli_commands() {
1527 $command_details = $this->get_command_list();
1528 foreach ( $command_details as $command => $details ) {
1529 if ( isset( $details['inactive'] ) && $details['inactive'] === true ) {
1530 continue;
1531 }
1532 WP_CLI::add_command(
1533 "rsssl $command",
1534 [ $this, $command ],
1535 [
1536 'shortdesc' => $details['description'],
1537 'synopsis' => $details['synopsis'],
1538 ]
1539 );
1540 }
1541 }
1542 }
1543
1544 // Add devtools command if present
1545 if ( file_exists( rsssl_path . 'pro/assets/tools/cli/class-rsssl-stub-generator.php' ) ) {
1546 require_once rsssl_path . 'pro/assets/tools/cli/class-rsssl-stub-generator.php';
1547 }