PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.1
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.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 8 months ago languages 8 months ago lets-encrypt 8 months ago lib 8 months ago mailer 8 months ago modal 8 months ago onboarding 8 months ago placeholders 8 months ago progress 8 months ago security 8 months ago settings 8 months ago testssl 8 months ago upgrade 8 months ago .wp-env.json 8 months ago SECURITY.md 8 months ago class-admin.php 8 months ago class-cache.php 8 months ago class-certificate.php 8 months ago class-front-end.php 8 months ago class-installer.php 8 months ago class-mixed-content-fixer.php 8 months ago class-multisite.php 8 months ago class-server.php 8 months ago class-site-health.php 8 months ago class-wp-cli.php 8 months ago compatibility.php 8 months ago force-deactivate.txt 8 months ago functions.php 8 months ago index.php 8 months ago readme.txt 8 months ago rector.php 8 months ago rlrsssl-really-simple-ssl.php 8 months ago rsssl-auto-loader.php 8 months ago sbom.json.gz 8 months ago ssl-test-page.php 8 months ago system-status.php 8 months ago uninstall.php 8 months ago upgrade.php 8 months ago
class-wp-cli.php
1548 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_login_protection 123
725 *
726 * @param array $args User ID should be the first element
727 *
728 * @throws \WP_CLI\ExitException
729 */
730 public function reset_login_protection( $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 delete_user_meta( $user->ID, 'rsssl_passkey_configured'); // Remove passkey configuration if it exists
754 }
755
756 WP_CLI::success( 'Successfully reset Login Protection for user id ' . $user_id );
757 }
758
759 /**
760 * Update the advanced-headers.php with the latest rules
761 *
762 * @return void
763 */
764 public function update_advanced_headers() {
765 if ( ! $this->check_pro_command_preconditions() ) return;
766 do_action('rsssl_update_rules');
767 WP_CLI::success( 'Successfully update advanced headers.' );
768 }
769
770 /**
771 * Add an IP to the firewall blocklist.
772 *
773 * @example wp rsssl add_firewall_ip_block 123.123.123.1 --note="This is a temporary block"
774 * @example wp rsssl add_firewall_ip_block 123.123.123.1 --permanent --note="This is a permanent block"
775 *
776 * @param array $args Should contain IP as the first element
777 * @param array $assoc_args Can contain a note with a 'note' key
778 */
779 public function add_firewall_ip_block(array $args, array $assoc_args): void
780 {
781 if ( ! $this->check_pro_command_preconditions() ) return;
782 $this->handleFirewallTableEntry($args, $assoc_args, 'blocked', 'add');
783 }
784
785 /**
786 * Can be used to remove a (temporary) block from the firewall blocklist.
787 * @example wp rsssl remove_firewall_ip_block 123.123.123.1
788 *
789 * @param $args array Should contain the ip address
790 */
791 public function remove_firewall_ip_block(array $args, array $assoc_args ): void
792 {
793 if ( ! $this->check_pro_command_preconditions() ) return;
794 $this->handleFirewallTableEntry($args, $assoc_args, 'blocked', 'remove');
795 }
796
797 /**
798 * Return a table of the current blocked IPs with the headers:
799 * IP Address, Note, Permanent
800 */
801 public function show_blocked_ips() {
802 if ( ! $this->check_pro_command_preconditions() ) return;
803 $columns = [
804 'ip_address',
805 'note',
806 'permanent',
807 ];
808
809 $blockedIps = ( new Rsssl_404_Block() )->get_blocked_ips($columns);
810
811 WP_CLI\Utils\format_items('table', $blockedIps, $columns);
812 }
813
814 /**
815 * Add an IP to the firewall's trusted list.
816 *
817 * Usage: wp rsssl add_firewall_trusted_ip 123.123.123.1
818 *
819 * @param array $args Should contain IP as the first element
820 * @param array $assoc_args Can contain a note with a 'note' key
821 * @uses handleFirewallTableEntry()
822 */
823 public function add_firewall_trusted_ip(array $args, array $assoc_args) {
824 if ( ! $this->check_pro_command_preconditions() ) return;
825 $this->handleFirewallTableEntry($args, $assoc_args, 'trusted', 'add');
826 }
827
828 /**
829 * Remove an IP from the firewall's trusted list.
830 *
831 * Usage: wp rsssl remove_firewall_trusted_ip 123.123.123.1
832 *
833 * @param array $args Should contain IP as the first element
834 * @param array $assoc_args Can contain a note with a 'note' key
835 * @uses handleFirewallTableEntry()
836 */
837 public function remove_firewall_trusted_ip(array $args, array $assoc_args) {
838 if ( ! $this->check_pro_command_preconditions() ) return;
839 $this->handleFirewallTableEntry($args, $assoc_args, 'trusted', 'remove');
840 }
841
842 /**
843 * Add an IP to the LLA's trusted list.
844 *
845 * Usage: wp rsssl add_lla_trusted_ip 123.123.123.1
846 *
847 * @param array $args Command arguments.
848 * @uses handleLlaTableEntry()
849 */
850 public function add_lla_trusted_ip( $args ) {
851 if ( ! $this->check_pro_command_preconditions() ) return;
852 $this->handleLlaTableEntry($args, 'allowed', 'source_ip', 'add');
853 }
854
855 /**
856 * Add an IP to the LLA's blocklist.
857 *
858 * Usage: wp rsssl remove_lla_trusted_ip 123.123.123.1
859 *
860 * @param array $args Command arguments.
861 * @uses handleLlaTableEntry()
862 */
863 public function remove_lla_trusted_ip( $args ) {
864 if ( ! $this->check_pro_command_preconditions() ) return;
865 $this->handleLlaTableEntry($args, 'allowed', 'source_ip', 'remove');
866 }
867
868 /**
869 * Remove an IP from the LLA's trusted list.
870 *
871 * Usage: wp rsssl add_lla_blocked_ip 123.123.123.1
872 * Usage: wp rsssl add_lla_blocked_ip 123.123.123.1 --permanent
873 *
874 * @param array $args Command arguments.
875 * @param array $assoc_args Associative arguments.
876 * @uses handleLlaTableEntry()
877 */
878 public function add_lla_blocked_ip( $args, $assoc_args ) {
879 if ( ! $this->check_pro_command_preconditions() ) return;
880 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
881 $this->handleLlaTableEntry($args, $status, 'source_ip', 'add');
882 }
883
884 /**
885 * Remove an IP from the LLA's blocklist.
886 *
887 * Usage: wp rsssl remove_lla_blocked_ip 123.123.123.1
888 * Usage: wp rsssl remove_lla_blocked_ip 123.123.123.1 --permanent
889 *
890 * @param array $args Command arguments.
891 * @param array $assoc_args Associative arguments.
892 * @uses handleLlaTableEntry()
893 */
894 public function remove_lla_blocked_ip( $args, $assoc_args ) {
895 if ( ! $this->check_pro_command_preconditions() ) return;
896 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
897 $this->handleLlaTableEntry($args, $status, 'source_ip', 'remove');
898 }
899
900 /**
901 * Add a username to the LLA's trusted list.
902 *
903 * Usage: wp rsssl add_lla_trusted_username username
904 *
905 * @param array $args Command arguments.
906 * @uses handleLlaTableEntry()
907 */
908 public function add_lla_trusted_username( $args ) {
909 if ( ! $this->check_pro_command_preconditions() ) return;
910 $this->handleLlaTableEntry($args, 'allowed', 'username', 'add');
911 }
912
913 /**
914 * Remove a username to the LLA's trusted list.
915 *
916 * Usage: wp rsssl remove_lla_trusted_username username
917 *
918 * @param array $args Command arguments.
919 * @uses handleLlaTableEntry()
920 */
921 public function remove_lla_trusted_username( $args ) {
922 if ( ! $this->check_pro_command_preconditions() ) return;
923 $this->handleLlaTableEntry($args, 'allowed', 'username', 'remove');
924 }
925
926 /**
927 * Add a username to the LLA's blocked list.
928 *
929 * Usage: wp rsssl add_lla_blocked_username username
930 * Usage: wp rsssl add_lla_blocked_username username --permanent
931 *
932 * @param array $args Command arguments.
933 * @param array $assoc_args Associative arguments.
934 * @uses handleLlaTableEntry()
935 */
936 public function add_lla_blocked_username( array $args, array $assoc_args ) {
937 if ( ! $this->check_pro_command_preconditions() ) return;
938 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
939 $this->handleLlaTableEntry($args, $status, 'username', 'add');
940 }
941
942 /**
943 * Remove a username to the LLA's blocked list.
944 *
945 * Usage: wp rsssl remove_lla_blocked_username username
946 * Usage: wp rsssl remove_lla_blocked_username username --permanent
947 *
948 * @param array $args Command arguments.
949 * @param array $assoc_args Associative arguments.
950 * @uses handleLlaTableEntry()
951 */
952 public function remove_lla_blocked_username( $args, $assoc_args ) {
953 if ( ! $this->check_pro_command_preconditions() ) return;
954 $status = (isset($assoc_args['permanent']) ? 'blocked' : 'locked');
955 $this->handleLlaTableEntry($args, $status, 'username', 'remove');
956 }
957
958 /**
959 * Handle an action for the firewall table for a specific IP address.
960 *
961 * @param array $args Command arguments.
962 * @param array $assoc_args Associative arguments.
963 * @param string $status Should be either 'trusted' or 'blocked'.
964 * @param string $action Should be either 'add' or 'remove'.
965 *
966 * @uses remove_white_list_ip() & add_white_list_ip() from Rsssl_Geo_Block -
967 * Those also handle a block request for an IP address.
968 */
969 protected function handleFirewallTableEntry(array $args, array $assoc_args, string $status, string $action)
970 {
971 if (rsssl_get_option('enable_firewall', false) !== true) {
972 WP_CLI::error('The firewall is not enabled.', true);
973 }
974
975 if (!in_array($status, ['trusted', 'blocked']) || !in_array($action, ['add', 'remove'])) {
976 WP_CLI::error('Could not handle action for the firewall table.', true);
977 }
978
979 if (empty($args[0])) {
980 WP_CLI::error('Please provide an IP address.', true);
981 }
982
983 $ip = $this->getFilteredIpAddress($args[0]);
984
985 // Prepare data for adding to the whitelist.
986 $data = [
987 'ip_address' => $ip,
988 'note' => $assoc_args['note'] ?? '',
989 'status' => $status,
990 'permanent' => isset($assoc_args['permanent']),
991 ];
992
993 // Use the Rsssl_Geo_Block class to add the trusted IP.
994 if (!class_exists('\RSSSL\Pro\Security\WordPress\Rsssl_Geo_Block')) {
995 require_once rsssl_path . 'pro/security/wordpress/rsssl-geo-block.php';
996 }
997
998 try {
999 $geo_block = new \RSSSL\Pro\Security\WordPress\Rsssl_Geo_Block();
1000
1001 // fallback
1002 $response = ['success' => false, 'message' => 'Something went wrong!'];
1003
1004 if ($action === 'remove') {
1005 $response = $geo_block->remove_white_list_ip( $data );
1006 }
1007
1008 if ($action === 'add') {
1009 $response = $geo_block->add_white_list_ip( $data );
1010 }
1011 } catch ( \Exception $e ) {
1012 WP_CLI::error( 'Failed to handle IP entry: ' . $e->getMessage(), true );
1013 }
1014
1015 // Handle response.
1016 if ( $response['success'] ) {
1017 WP_CLI::success( $response['message'] );
1018 return;
1019 }
1020
1021 WP_CLI::error( $response['message'], true );
1022 }
1023
1024 /**
1025 * Handle an action for the LLA table for a specific IP address.
1026 *
1027 * @param array $args Command arguments.
1028 * @param string $status Should be either 'allowed' or 'blocked'.
1029 * @param string $type Should be either 'source_ip' or 'username'.
1030 * @param string $action Should be either 'add' or 'remove'.
1031 * @return void
1032 */
1033 protected function handleLlaTableEntry(array $args, string $status, string $type, string $action): void
1034 {
1035 if (rsssl_get_option('enable_limited_login_attempts', false) !== true) {
1036 WP_CLI::error('The LLA feature is not enabled.', true);
1037 }
1038
1039 if (empty($args[0])) {
1040 WP_CLI::error('Please provide the command the necessary arguments', true);
1041 }
1042
1043 if (!in_array($status, ['allowed', 'blocked', 'locked']) || !in_array($type, ['source_ip', 'username'])) {
1044 WP_CLI::error('Something went wrong! Could not handle command.', true);
1045 }
1046
1047 $value = '';
1048 if ($type === 'source_ip') {
1049 $value = $this->getFilteredIpAddress($args[0]);
1050 }
1051
1052 if ($type === 'username') {
1053 $value = sanitize_text_field($args[0]);
1054 }
1055
1056 // Use the Rsssl_Limit_Login_Admin class to add the trusted IP.
1057 if (!class_exists('\RSSSL\Pro\Security\WordPress\Rsssl_Limit_Login_Admin')) {
1058 require_once rsssl_path . 'pro/security/wordpress/class-rsssl-limit-login-admin.php';
1059 }
1060
1061 try {
1062 $lla = new \RSSSL\Pro\Security\WordPress\Rsssl_Limit_Login_Admin();
1063
1064 // fallback
1065 $response = ['success' => false, 'message' => 'Something went wrong!'];
1066
1067 if ($action === 'add') {
1068 $response = $lla->handle_entity([
1069 'value' => $value,
1070 'status' => sanitize_text_field($status),
1071 ], $type);
1072 }
1073
1074 if ($action === 'remove') {
1075 $entry = $lla->get_entry($type, $value, $status);
1076 $response = $lla->delete_entries([
1077 'id' => $entry['id'],
1078 ]);
1079 }
1080 } catch ( Exception $e ) {
1081 WP_CLI::error( 'Failed to handle LLA entry: ' . $e->getMessage(), true );
1082 }
1083
1084 // Handle response.
1085 if ( $response['success'] ) {
1086 WP_CLI::success( $response['message'] );
1087 return;
1088 }
1089
1090 WP_CLI::error( $response['message'], true );
1091 }
1092
1093 /**
1094 * Return a filtered IP address. Method will exit() if the IP address is
1095 * invalid with the WP_CLI error message: Invalid IP address provided.
1096 */
1097 protected function getFilteredIpAddress(string $originalIp): string
1098 {
1099 // Check if the input is potentially a CIDR
1100 if (strpos($originalIp, '/') !== false) {
1101 list($address, $mask_str) = explode('/', $originalIp, 2);
1102
1103 // Validate the IP address part
1104 if (!filter_var($address, FILTER_VALIDATE_IP)) {
1105 WP_CLI::error('Invalid IP address part in CIDR notation: ' . $address, true);
1106 }
1107
1108 // Validate the mask part
1109 if (!is_numeric($mask_str)) {
1110 WP_CLI::error('CIDR mask is not numeric: ' . $mask_str, true);
1111 }
1112 $mask = (int)$mask_str;
1113
1114 // Determine IP version for mask validation
1115 $is_ipv4 = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
1116 $is_ipv6 = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
1117
1118 if ($is_ipv4) {
1119 if ($mask < 0 || $mask > 32) {
1120 WP_CLI::error('Invalid IPv4 CIDR mask (must be 0-32): ' . $mask, true);
1121 }
1122 } elseif ($is_ipv6) {
1123 if ($mask < 0 || $mask > 128) {
1124 WP_CLI::error('Invalid IPv6 CIDR mask (must be 0-128): ' . $mask, true);
1125 }
1126 } else {
1127 // This case should ideally not be reached if filter_var($address, FILTER_VALIDATE_IP) passed
1128 WP_CLI::error('Unknown IP address type for CIDR validation: ' . $address, true);
1129 }
1130
1131 // If all checks pass for CIDR, return the original CIDR string
1132 return $originalIp;
1133
1134 } else {
1135 // Validate as a plain IP address
1136 $ip = filter_var($originalIp, FILTER_VALIDATE_IP);
1137 if (empty($ip)) {
1138 WP_CLI::error('Invalid IP address provided: ' . $originalIp, true);
1139 }
1140 return $ip;
1141 }
1142 }
1143
1144 /**
1145 * Performs pre-flight checks before SSL activation.
1146 * Checks for HTTPS reachability and potentially other issues like .htaccess writability.
1147 *
1148 * @return array ['success' => bool, 'message' => string, 'warnings' => array]
1149 */
1150 private function perform_pre_flight_checks(): array {
1151 $warnings = [];
1152 $message = '';
1153
1154 // --- Check 1: HTTPS Reachability ---
1155 $home_url = home_url();
1156 $https_url = set_url_scheme( $home_url, 'https' );
1157
1158 // Use wp_remote_get to see if the HTTPS version is reachable
1159 // 'sslverify' => false is important for local/staging with self-signed certs
1160 // Timeout set low to avoid long waits on failure
1161 $response = wp_remote_get( $https_url, [
1162 'timeout' => 10, // seconds
1163 'sslverify' => false,
1164 'redirection' => 5, // Follow redirects
1165 ] );
1166
1167 if ( is_wp_error( $response ) ) {
1168 $error_code = $response->get_error_code();
1169 $error_message = $response->get_error_message();
1170 $friendly_message = sprintf(
1171 __( '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' ),
1172 $https_url
1173 );
1174
1175 // Check if WP_DEBUG is enabled
1176 $wp_debug_enabled = ( defined( 'WP_DEBUG' ) && WP_DEBUG );
1177
1178 if ( $wp_debug_enabled ) {
1179 // Log the detailed error when WP_DEBUG is on
1180 // Using WP_CLI::debug requires the --debug flag for wp-cli command itself
1181 WP_CLI::debug( sprintf( "HTTPS Check Error Details: Code=%s, Message=%s", $error_code, $error_message ), 'rsssl-cli-debug' );
1182 // Alternatively, or in addition, use standard PHP error logging:
1183 // error_log( sprintf("Really Simple SSL WP-CLI HTTPS Check Error: Code=%s, Message=%s", $error_code, $error_message) );
1184
1185 // Optionally, still show a slightly more informative message than the friendly one
1186 $message_to_show = sprintf(
1187 __( 'Failed to reach %s. The site does not appear to be accessible over HTTPS (Error: %s). Check debug logs for details.', 'really-simple-ssl' ),
1188 $https_url,
1189 $error_code // Show the code, but maybe not the full verbose message
1190 );
1191 } else {
1192 // Show only the user-friendly message if WP_DEBUG is off
1193 $message_to_show = $friendly_message;
1194 }
1195
1196 return [
1197 'success' => false,
1198 'message' => $message_to_show,
1199 'warnings' => $warnings
1200 ];
1201
1202 } else {
1203 // Connected, check the response code
1204 $response_code = wp_remote_retrieve_response_code( $response );
1205 if ( $response_code < 200 || $response_code >= 400 ) {
1206 // Reached server, but got an error response (e.g., 404 Not Found, 500 Internal Server Error)
1207 return [
1208 'success' => false,
1209 'message' => sprintf( __( 'Reached %s, but received an error response code: %d. HTTPS is not properly configured.', 'really-simple-ssl' ), $https_url, $response_code ),
1210 'warnings' => $warnings
1211 ];
1212 }
1213 // If response code is 2xx or 3xx, we consider HTTPS reachable.
1214 // A more robust check could analyze the body for expected content, but this is usually sufficient.
1215 }
1216
1217 // --- Check 2: .htaccess Writability (if needed) ---
1218 // Keep the previous check for .htaccess if the redirect method is set to htaccess
1219 // $htaccess_writable = true; // Replace with actual check logic (e.g., check if WP_Filesystem allows writing)
1220 if ( rsssl_get_option('redirect') === 'htaccess' ) {
1221 // Get the path to the .htaccess file
1222 $htaccess_file = RSSSL()->admin->htaccess_file(); // Assuming a method to get the correct path
1223 if ( ! is_writable( $htaccess_file ) ) {
1224 $warnings[] = sprintf( __( '.htaccess file (%s) is not writable. Redirects cannot be configured automatically.', 'really-simple-ssl' ), $htaccess_file );
1225 // This remains a warning, as activation might still work partially (WP URLs change)
1226 }
1227 }
1228
1229 // Add more checks as needed (e.g., specific certificate details if possible/required)...
1230
1231 $message = __( 'Pre-flight checks passed.', 'really-simple-ssl' );
1232 return ['success' => true, 'message' => $message, 'warnings' => $warnings];
1233 }
1234
1235 /**
1236 * Get command details for WP-CLI commands.
1237 *
1238 * @return array Command details.
1239 */
1240 protected function get_command_list() {
1241 return [
1242 'activate_ssl' => [
1243 'description' => __( 'Activate SSL on the site.', 'really-simple-ssl' ),
1244 'synopsis' => [],
1245 'pro' => false,
1246 ],
1247 'deactivate_ssl' => [
1248 'description' => __( 'Deactivate SSL on the site.', 'really-simple-ssl' ),
1249 'synopsis' => [],
1250 'pro' => false,
1251 ],
1252 'update_option' => [
1253 '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' ),
1254 'synopsis' => [
1255 [
1256 'type' => 'assoc',
1257 'name' => 'name',
1258 'optional' => false,
1259 'description' => __( 'Name of the option to update.', 'really-simple-ssl' ),
1260 ],
1261 [
1262 'type' => 'assoc',
1263 'name' => 'value',
1264 'optional' => false,
1265 'description' => __( 'Value to set for the option.', 'really-simple-ssl' ),
1266 ],
1267 ],
1268 'pro' => false,
1269 ],
1270 'activate_recommended_features' => [
1271 'description' => __( 'Activate all recommended features.', 'really-simple-ssl' ),
1272 'synopsis' => [],
1273 'pro' => false,
1274 ],
1275 'deactivate_recommended_features' => [
1276 'description' => __( 'Deactivate all recommended features.', 'really-simple-ssl' ),
1277 'synopsis' => [],
1278 'pro' => false,
1279 ],
1280 'activate_security_headers' => [
1281 'description' => __( 'Activate essential security headers.', 'really-simple-ssl' ),
1282 'synopsis' => [],
1283 'pro' => true,
1284 ],
1285 'deactivate_security_headers' => [
1286 'description' => __( 'Deactivate essential security headers.', 'really-simple-ssl' ),
1287 'synopsis' => [],
1288 'pro' => true,
1289 ],
1290 'activate_firewall' => [
1291 'description' => __( 'Activate the firewall.', 'really-simple-ssl' ),
1292 'synopsis' => [],
1293 'pro' => true,
1294 ],
1295 'deactivate_firewall' => [
1296 'description' => __( 'Deactivate the firewall.', 'really-simple-ssl' ),
1297 'synopsis' => [],
1298 'pro' => true,
1299 ],
1300 'activate_2fa' => [
1301 'description' => __( 'Activate Two-Factor Authentication.', 'really-simple-ssl' ),
1302 'synopsis' => [],
1303 'pro' => false,
1304 ],
1305 'deactivate_2fa' => [
1306 'description' => __( 'Deactivate Two-Factor Authentication.', 'really-simple-ssl' ),
1307 'synopsis' => [],
1308 'pro' => false,
1309 ],
1310 'activate_password_security' => [
1311 'description' => __( 'Activate password security features.', 'really-simple-ssl' ),
1312 'synopsis' => [],
1313 'pro' => true,
1314 ],
1315 'deactivate_password_security' => [
1316 'description' => __( 'Deactivate password security features.', 'really-simple-ssl' ),
1317 'synopsis' => [],
1318 'pro' => true,
1319 ],
1320 'activate_lla' => [
1321 'description' => __( 'Activate limit login attempts.', 'really-simple-ssl' ),
1322 'synopsis' => [],
1323 'pro' => true,
1324 ],
1325 'deactivate_lla' => [
1326 'description' => __( 'Deactivate limit login attempts.', 'really-simple-ssl' ),
1327 'synopsis' => [],
1328 'pro' => true,
1329 ],
1330 'activate_vulnerability_scanning' => [
1331 'description' => __( 'Activate vulnerability scanning.', 'really-simple-ssl' ),
1332 'synopsis' => [],
1333 'pro' => false,
1334 ],
1335 'deactivate_vulnerability_scanning' => [
1336 'description' => __( 'Deactivate vulnerability scanning.', 'really-simple-ssl' ),
1337 'synopsis' => [],
1338 'pro' => false,
1339 ],
1340 'activate_license' => [
1341 'description' => __( 'Activate a license key. Usage: wp rsssl activate_license YOUR_LICENSE_KEY.', 'really-simple-ssl' ),
1342 'synopsis' => [
1343 [
1344 'type' => 'positional',
1345 'name' => 'license_key',
1346 'optional' => false,
1347 'description' => __( 'The license key to activate.', 'really-simple-ssl' ),
1348 ],
1349 ],
1350 'pro' => true,
1351 ],
1352 'deactivate_license' => [
1353 'description' => __( 'Deactivate the license.', 'really-simple-ssl' ),
1354 'synopsis' => [],
1355 'pro' => true,
1356 ],
1357 'add_lock_file' => [
1358 'description' => __( 'Add a lock file for safe mode.', 'really-simple-ssl' ),
1359 'synopsis' => [],
1360 'pro' => false,
1361 ],
1362 'remove_lock_file' => [
1363 'description' => __( 'Remove the lock file for safe mode.', 'really-simple-ssl' ),
1364 'synopsis' => [],
1365 'pro' => false,
1366 ],
1367 'reset_login_protection' => [
1368 'description' => __( 'Reset the settings for the Login Protection.', 'really-simple-ssl' ),
1369 'synopsis' => [],
1370 'pro' => false,
1371 ],
1372 'update_advanced_headers' => [
1373 'description' => __( 'Update the advanced-headers.php with the latest rules.', 'really-simple-ssl' ),
1374 'synopsis' => [],
1375 'pro' => false,
1376 ],
1377 'add_firewall_ip_block' => [
1378 'description' => __( 'Add IP block.', 'really-simple-ssl' ),
1379 'synopsis' => [
1380 [
1381 'type' => 'positional',
1382 'name' => 'ip_address',
1383 'optional' => false,
1384 'description' => __( 'The IP to block.', 'really-simple-ssl' ),
1385 ],
1386 [
1387 'type' => 'flag',
1388 'name' => 'permanent',
1389 'optional' => true,
1390 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1391 ],
1392 [
1393 'type' => 'assoc',
1394 'name' => 'note',
1395 'optional' => true,
1396 'description' => __( 'Optional note for the block.', 'really-simple-ssl' ),
1397 ],
1398 ],
1399 'pro' => true,
1400 ],
1401 'remove_firewall_ip_block' => [
1402 'description' => __( 'Remove IP block.', 'really-simple-ssl' ),
1403 'synopsis' => [
1404 [
1405 'type' => 'positional',
1406 'name' => 'ip_address',
1407 'optional' => false,
1408 'description' => __( 'The IP to remove the block for.', 'really-simple-ssl' ),
1409 ],
1410 ],
1411 'pro' => true,
1412 ],
1413 'show_blocked_ips' => [
1414 'description' => __( 'Show blocked IP\'s.', 'really-simple-ssl' ),
1415 'synopsis' => [],
1416 'pro' => true,
1417 ],
1418 'add_firewall_trusted_ip' => [
1419 'description' => __( 'Add a trusted IP to the firewall.', 'really-simple-ssl' ),
1420 'synopsis' => [],
1421 'pro' => true,
1422 ],
1423 'remove_firewall_trusted_ip' => [
1424 'description' => __( 'Remove a trusted IP from the firewall.', 'really-simple-ssl' ),
1425 'synopsis' => [],
1426 'pro' => true,
1427 ],
1428 'add_lla_trusted_ip' => [
1429 'description' => __( 'Add a trusted IP to the limit login attempts table.', 'really-simple-ssl' ),
1430 'synopsis' => [],
1431 'pro' => true,
1432 ],
1433 'remove_lla_trusted_ip' => [
1434 'description' => __( 'Remove a trusted IP from the limit login attempts table.', 'really-simple-ssl' ),
1435 'synopsis' => [],
1436 'pro' => true,
1437 ],
1438 'add_lla_blocked_ip' => [
1439 'description' => __( 'Add a blocked IP to the limit login attempts table.', 'really-simple-ssl' ),
1440 'synopsis' => [
1441 [
1442 'type' => 'positional',
1443 'name' => 'ip_address',
1444 'optional' => false,
1445 'description' => __( 'The IP to block.', 'really-simple-ssl' ),
1446 ],
1447 [
1448 'type' => 'flag',
1449 'name' => 'permanent',
1450 'optional' => true,
1451 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1452 ],
1453 ],
1454 'pro' => true,
1455 ],
1456 'remove_lla_blocked_ip' => [
1457 'description' => __( 'Remove a blocked IP from the limit login attempts table.', 'really-simple-ssl' ),
1458 'synopsis' => [
1459 [
1460 'type' => 'positional',
1461 'name' => 'ip_address',
1462 'optional' => false,
1463 'description' => __( 'The IP to block.', 'really-simple-ssl' ),
1464 ],
1465 [
1466 'type' => 'flag',
1467 'name' => 'permanent',
1468 'optional' => true,
1469 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1470 ],
1471 ],
1472 'pro' => true,
1473 ],
1474 'add_lla_trusted_username' => [
1475 'description' => __( 'Add a trusted username to the limit login attempts table.', 'really-simple-ssl' ),
1476 'synopsis' => [],
1477 'pro' => true,
1478 ],
1479 'remove_lla_trusted_username' => [
1480 'description' => __( 'Remove a trusted username from the limit login attempts table.', 'really-simple-ssl' ),
1481 'synopsis' => [],
1482 'pro' => true,
1483 ],
1484 'add_lla_blocked_username' => [
1485 'description' => __( 'Add a blocked username to the limit login attempts table.', 'really-simple-ssl' ),
1486 'synopsis' => [
1487 [
1488 'type' => 'positional',
1489 'name' => 'ip_address',
1490 'optional' => false,
1491 'description' => __( 'The username to block.', 'really-simple-ssl' ),
1492 ],
1493 [
1494 'type' => 'flag',
1495 'name' => 'permanent',
1496 'optional' => true,
1497 'description' => __( 'Flag to add a permanent block.', 'really-simple-ssl' ),
1498 ],
1499 ],
1500 'pro' => true,
1501 ],
1502 'remove_lla_blocked_username' => [
1503 'description' => __( 'Remove a blocked username from the limit login attempts table.', 'really-simple-ssl' ),
1504 'synopsis' => [
1505 [
1506 'type' => 'positional',
1507 'name' => 'username',
1508 'optional' => false,
1509 'description' => __( 'The username to remove the block for.', 'really-simple-ssl' ),
1510 ],
1511 [
1512 'type' => 'flag',
1513 'name' => 'permanent',
1514 'optional' => true,
1515 'description' => __( 'Flag to remove a permanent block.', 'really-simple-ssl' ),
1516 ],
1517 ],
1518 'pro' => true,
1519 ],
1520 ];
1521 }
1522
1523 /**
1524 * This method registers our WP-CLI commands and uses {@see get_command_list()}
1525 * to retrieve the list. Do not execute this method before the init hook.
1526 */
1527 public function register_wp_cli_commands() {
1528 $command_details = $this->get_command_list();
1529 foreach ( $command_details as $command => $details ) {
1530 if ( isset( $details['inactive'] ) && $details['inactive'] === true ) {
1531 continue;
1532 }
1533 WP_CLI::add_command(
1534 "rsssl $command",
1535 [ $this, $command ],
1536 [
1537 'shortdesc' => $details['description'],
1538 'synopsis' => $details['synopsis'],
1539 ]
1540 );
1541 }
1542 }
1543 }
1544
1545 // Add devtools command if present
1546 if ( file_exists( rsssl_path . 'pro/assets/tools/cli/class-rsssl-stub-generator.php' ) ) {
1547 require_once rsssl_path . 'pro/assets/tools/cli/class-rsssl-stub-generator.php';
1548 }