| @@ -1,10 +1,11 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | 3 | * Plugin Name: Patchstack Security |
| 4 | - * Plugin URI: https://patchstack.com | |
| 4 | + * Plugin URI: https://patchstack.com/?utm_medium=wp&utm_source=dashboard&utm_campaign=patchstack%20plugin | |
| 5 | + * Author URI: https://patchstack.com/?utm_medium=wp&utm_source=dashboard&utm_campaign=patchstack%20plugin | |
| 5 | 6 | * Description: Patchstack identifies security vulnerabilities in WordPress plugins, themes, and core. |
| 6 | - * Version: 2.1.11 | |
| 7 | + * Version: 2.2.0 | |
| 7 | 8 | * Author: Patchstack |
| 8 | 9 | * License: GPLv3 |
| 9 | 10 | * Text Domain: patchstack |
| 10 | 11 | * Domain Path: /languages |
| @@ -32,9 +33,9 @@ | ||
| 32 | 33 | |
| 33 | 34 | // Set up our filename. |
| 34 | 35 | $file_name = strtolower( str_replace( '_', '-', substr( $class_name, strlen( 'P_' ) ) ) ); |
| 35 | 36 | $dir = trailingslashit( dirname( __FILE__ ) ) . 'includes/'; |
| 36 | - $target = array( $dir . $file_name . '.php', $dir . 'admin/' . str_replace( 'admin-', '', $file_name ) . '.php' ); | |
| 37 | + $target = [ $dir . $file_name . '.php', $dir . 'admin/' . str_replace( 'admin-', '', $file_name ) . '.php' ]; | |
| 37 | 38 | |
| 38 | 39 | // Attempt each target and load if it exists. |
| 39 | 40 | foreach ( $target as $file ) { |
| 40 | 41 | if ( file_exists( $file ) ) { |
| @@ -57,9 +58,9 @@ | ||
| 57 | 58 | * The plugin version. |
| 58 | 59 | * |
| 59 | 60 | * @var string |
| 60 | 61 | */ |
| 61 | - const VERSION = '2.1.11'; | |
| 62 | + const VERSION = '2.2.0'; | |
| 62 | 63 | |
| 63 | 64 | /** |
| 64 | 65 | * API URL of Patchstack to communicate with. |
| 65 | 66 | * |
| @@ -88,15 +89,8 @@ | ||
| 88 | 89 | */ |
| 89 | 90 | const PRIVATE_KEY = 'PATCHSTACK_PRIVATE_KEY'; |
| 90 | 91 | |
| 91 | 92 | /** |
| 92 | - * Known IP addresses. | |
| 93 | - * | |
| 94 | - * @var array | |
| 95 | - */ | |
| 96 | - protected $ips = array( '18.221.197.243', '52.15.237.250', '3.19.3.34', '3.18.238.17', '13.58.49.77', '18.222.191.77', '3.131.108.250', '3.23.157.140', '18.220.70.233', '3.140.84.221', '185.212.171.100' ); | |
| 97 | - | |
| 98 | - /** | |
| 99 | 93 | * URL of the plugin directory. |
| 100 | 94 | * |
| 101 | 95 | * @var string |
| 102 | 96 | */ |
| @@ -120,9 +114,9 @@ | ||
| 120 | 114 | * Detailed activation error messages. |
| 121 | 115 | * |
| 122 | 116 | * @var array |
| 123 | 117 | */ |
| 124 | - protected $activation_errors = array(); | |
| 118 | + protected $activation_errors = []; | |
| 125 | 119 | |
| 126 | 120 | /** |
| 127 | 121 | * Singleton instance of plugin. |
| 128 | 122 | * |
| @@ -145,8 +139,9 @@ | ||
| 145 | 139 | protected $htaccess; |
| 146 | 140 | protected $hacker_log; |
| 147 | 141 | protected $upload; |
| 148 | 142 | protected $rules; |
| 143 | + protected $hide_login; | |
| 149 | 144 | protected $listener; |
| 150 | 145 | protected $event_log; |
| 151 | 146 | protected $multisite; |
| 152 | 147 | protected $notice; |
| @@ -175,8 +170,13 @@ | ||
| 175 | 170 | $this->basename = plugin_basename( __FILE__ ); |
| 176 | 171 | $this->url = plugin_dir_url( __FILE__ ); |
| 177 | 172 | $names = explode( '/', $this->basename ); |
| 178 | 173 | $this->name = $names[0]; |
| 174 | + | |
| 175 | + // Define WP_CLI command. | |
| 176 | + if ( defined( 'WP_CLI' ) && WP_CLI && method_exists('\WP_CLI', 'add_command')) { | |
| 177 | + \WP_CLI::add_command( 'patchstack activate', [ $this, 'cli_activate' ] ); | |
| 178 | + } | |
| 179 | 179 | } |
| 180 | 180 | |
| 181 | 181 | /** |
| 182 | 182 | * Call the constructor of all the Patchstack related classes. |
| @@ -184,9 +184,9 @@ | ||
| 184 | 184 | * @return void |
| 185 | 185 | */ |
| 186 | 186 | public function plugin_classes() { |
| 187 | 187 | // Define the array of the classes. |
| 188 | - foreach ( array( | |
| 188 | + foreach ( [ | |
| 189 | 189 | 'admin_options' => 'P_Admin_Options', |
| 190 | 190 | 'cron' => 'P_Cron', |
| 191 | 191 | 'api' => 'P_Api', |
| 192 | 192 | 'login' => 'P_Login', |
| @@ -195,8 +195,9 @@ | ||
| 195 | 195 | 'htaccess' => 'P_Htaccess', |
| 196 | 196 | 'hacker_log' => 'P_Hacker_Log', |
| 197 | 197 | 'upload' => 'P_Upload', |
| 198 | 198 | 'rules' => 'P_Rules', |
| 199 | + 'hide_login' => 'P_Hide_Login', | |
| 199 | 200 | 'listener' => 'P_Listener', |
| 200 | 201 | 'event_log' => 'P_Event_Log', |
| 201 | 202 | 'activation' => 'P_Activation', |
| 202 | 203 | 'multisite' => 'P_Multisite', |
| @@ -203,12 +204,13 @@ | ||
| 203 | 204 | 'notice' => 'P_Cookie_Notice', |
| 204 | 205 | 'admin_ajax' => 'P_Admin_Ajax', |
| 205 | 206 | 'admin_general' => 'P_Admin_General', |
| 206 | 207 | 'admin_menu' => 'P_Admin_Menu', |
| 207 | - ) as $var => $class ) { | |
| 208 | + ] as $var => $class ) { | |
| 208 | 209 | $this->$var = new $class( $this ); |
| 209 | 210 | } |
| 210 | 211 | |
| 212 | + // Load firewall base functionality. | |
| 211 | 213 | $this->firewall_base = new P_Firewall( true, $this, true ); |
| 212 | 214 | } |
| 213 | 215 | |
| 214 | 216 | /** |
| @@ -221,28 +223,50 @@ | ||
| 221 | 223 | $this->activation->activate( $this ); |
| 222 | 224 | } |
| 223 | 225 | |
| 224 | 226 | /** |
| 225 | - * Deactivate the plugin. | |
| 227 | + * Connects the Patchstack plugin to the API with the license id and secret key. | |
| 226 | 228 | * |
| 227 | - * @return void | |
| 229 | + * Returns an error if the connection was not successful. | |
| 230 | + * | |
| 231 | + * ## OPTIONS | |
| 232 | + * | |
| 233 | + * <id> | |
| 234 | + * : The API client id. | |
| 235 | + * | |
| 236 | + * <secret> | |
| 237 | + * : The API secret key. | |
| 238 | + * | |
| 239 | + * ## EXAMPLES | |
| 240 | + * | |
| 241 | + * $ wp patchstack activate 123456 2b072e8b60402e30d481df351fc08183906254e0 | |
| 242 | + * Success: The Patchstack plugin has been successfully connected. | |
| 228 | 243 | */ |
| 229 | - public function deactivate() { | |
| 230 | - $this->plugin_classes(); | |
| 231 | - $this->activation->deactivate(); | |
| 244 | + public function cli_activate( $args ) { | |
| 245 | + $id = isset( $args[0] ) ? trim( $args[0] ) : ''; | |
| 246 | + $secret = isset( $args[1] ) ? trim( $args[1] ) : ''; | |
| 247 | + | |
| 248 | + $result = $this->activation->alter_license( $id, $secret, 'activate' ); | |
| 249 | + if ( $result['result'] == 'error' ) { | |
| 250 | + \WP_CLI::error( 'The Patchstack plugin could not be connected. Make sure the id and secret key are valid and that api.patchstack.com is not blocked.' ); | |
| 251 | + return; | |
| 252 | + } | |
| 253 | + | |
| 254 | + \WP_CLI::success( 'The Patchstack plugin has been successfully connected.' ); | |
| 232 | 255 | } |
| 233 | 256 | |
| 234 | 257 | /** |
| 235 | - * Boot Patchstack and its classes. | |
| 258 | + * Deactivate the plugin. | |
| 236 | 259 | * |
| 237 | 260 | * @return void |
| 238 | 261 | */ |
| 239 | - public function hooks() { | |
| 240 | - add_action( 'init', array( $this, 'init' ), ~PHP_INT_MAX ); | |
| 262 | + public function deactivate() { | |
| 263 | + $this->plugin_classes(); | |
| 264 | + $this->activation->deactivate(); | |
| 241 | 265 | } |
| 242 | 266 | |
| 243 | 267 | /** |
| 244 | - * Boot Patchstack | |
| 268 | + * Boot Patchstack. | |
| 245 | 269 | * |
| 246 | 270 | * @return void |
| 247 | 271 | */ |
| 248 | 272 | public function init() { |
| @@ -259,9 +283,9 @@ | ||
| 259 | 283 | if ( get_option( 'patchstack_api_token', '' ) == '' && get_option( 'patchstack_license_expiry', '' ) == '' ) { |
| 260 | 284 | $this->api->update_license_status(); |
| 261 | 285 | } |
| 262 | 286 | |
| 263 | - // Determine if the license is activated and not expired. | |
| 287 | + // Run firewall if not disabled and license activated. | |
| 264 | 288 | if ( get_option( 'patchstack_license_activated', 0 ) == 1 && get_option( 'patchstack_basic_firewall', 0 ) == 1 && get_option( 'patchstack_license_free', 0 ) == 0 ) { |
| 265 | 289 | $this->firewall = new P_Firewall( true, $this ); |
| 266 | 290 | } |
| 267 | 291 | } |
| @@ -316,9 +340,9 @@ | ||
| 316 | 340 | * @return void |
| 317 | 341 | */ |
| 318 | 342 | function patchstack_uninstall() { |
| 319 | 343 | // Delete most of the Patchstack options. |
| 320 | - $options = array( 'patchstack_eventlog_lastid', 'patchstack_api_token', 'patchstack_dashboardlock', 'patchstack_pluginedit', 'patchstack_move_logs', 'patchstack_userenum', 'patchstack_basicscanblock', 'patchstack_hidewpcontent', 'patchstack_hidewpversionk', 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_known_blacklist', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_proxy_comment_posting', 'patchstack_bad_query_strings', 'patchstack_advanced_character_string_filter', 'patchstack_advanced_blacklist_firewall', 'patchstack_forbid_rfi', 'patchstack_image_hotlinking', 'patchstack_add_security_headers', 'patchstack_firewall_log_lastid', 'patchstack_user_log_lastid', 'patchstack_captcha_public_key', 'patchstack_captcha_private_key', 'patchstack_scan_interval', 'patchstack_scan_day', 'patchstack_scan_time', 'patchstack_hackers_log', 'patchstack_users_log', 'patchstack_visitors_log', 'external_updates-webarx', 'patchstack_wp_stats', 'patchstack_captcha_login_form', 'patchstack_license_activated', 'patchstack_license_expiry', 'patchstack_software_data_hash', 'patchstack_mv_wp_login', 'patchstack_rename_wp_login', 'patchstack_googledrive_backup_is_running', 'patchstack_googledrive_upload_state', 'patchstack_googledrive_access_token', 'patchstack_googledrive_refresh_token', 'patchstack_cron_offset', 'patchstack_htaccess_rules_hash' ); | |
| 344 | + $options = [ 'patchstack_eventlog_lastid', 'patchstack_api_token', 'patchstack_dashboardlock', 'patchstack_pluginedit', 'patchstack_move_logs', 'patchstack_userenum', 'patchstack_basicscanblock', 'patchstack_hidewpcontent', 'patchstack_hidewpversionk', 'patchstack_prevent_default_file_access', 'patchstack_basic_firewall', 'patchstack_known_blacklist', 'patchstack_block_debug_log_access', 'patchstack_block_fake_bots', 'patchstack_index_views', 'patchstack_proxy_comment_posting', 'patchstack_bad_query_strings', 'patchstack_advanced_character_string_filter', 'patchstack_advanced_blacklist_firewall', 'patchstack_forbid_rfi', 'patchstack_image_hotlinking', 'patchstack_add_security_headers', 'patchstack_firewall_log_lastid', 'patchstack_user_log_lastid', 'patchstack_captcha_public_key', 'patchstack_captcha_private_key', 'patchstack_scan_interval', 'patchstack_scan_day', 'patchstack_scan_time', 'patchstack_hackers_log', 'patchstack_users_log', 'patchstack_visitors_log', 'external_updates-webarx', 'patchstack_wp_stats', 'patchstack_captcha_login_form', 'patchstack_license_activated', 'patchstack_license_expiry', 'patchstack_software_data_hash', 'patchstack_mv_wp_login', 'patchstack_rename_wp_login', 'patchstack_googledrive_backup_is_running', 'patchstack_googledrive_upload_state', 'patchstack_googledrive_access_token', 'patchstack_googledrive_refresh_token', 'patchstack_cron_offset', 'patchstack_htaccess_rules_hash' ]; | |
| 321 | 345 | foreach ( $options as $option ) { |
| 322 | 346 | delete_option( $option ); |
| 323 | 347 | |
| 324 | 348 | if ( is_multisite() ) { |
| @@ -327,9 +351,9 @@ | ||
| 327 | 351 | } |
| 328 | 352 | |
| 329 | 353 | // Drop all Patchstack tables. |
| 330 | 354 | global $wpdb; |
| 331 | - $tables = array( 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' ); | |
| 355 | + $tables = [ 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' ]; | |
| 332 | 356 | foreach ( $tables as $table ) { |
| 333 | 357 | $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . $table ); |
| 334 | 358 | } |
| 335 | 359 | } |
| @@ -346,10 +370,10 @@ | ||
| 346 | 370 | } |
| 347 | 371 | } |
| 348 | 372 | |
| 349 | 373 | // Kick it off. |
| 350 | -add_action( 'plugins_loaded', array( patchstack(), 'hooks' ) ); | |
| 374 | +add_action( 'plugins_loaded', [ patchstack(), 'init' ] ); | |
| 351 | 375 | |
| 352 | 376 | // Activation and deactivation hooks. |
| 353 | -register_activation_hook( __FILE__, array( patchstack(), 'activate' ) ); | |
| 354 | -register_deactivation_hook( __FILE__, array( patchstack(), 'deactivate' ) ); | |
| 377 | +register_activation_hook( __FILE__, [ patchstack(), 'activate' ] ); | |
| 378 | +register_deactivation_hook( __FILE__, [ patchstack(), 'deactivate' ] ); | |
| 355 | 379 | register_uninstall_hook( __FILE__, 'patchstack_uninstall' ); |