PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.8
Patchstack – WordPress & Plugins Security v2.2.8
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
← All changes | patchstack.php +66 -29 2.1.122.2.8 View file →
@@ -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.12
7 + * Version: 2.2.8
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.12';
62 + const VERSION = '2.2.8';
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,63 @@
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 + * <secret-id>
240 + * : The API client id and secret key merged together, found in the App. E.g. 2b072e8b60402e30d481df351fc08183906254e0-123456
241 + *
242 + * ## EXAMPLES
243 + *
244 + * $ wp patchstack activate 123456 2b072e8b60402e30d481df351fc08183906254e0
245 + * Success: The Patchstack plugin has been successfully connected.
246 + *
247 + * or
248 + *
249 + * $ wp patchstack activate 2b072e8b60402e30d481df351fc08183906254e0-123456
250 + * Success: The Patchstack plugin has been successfully connected.
228 251 */
229 - public function deactivate() {
230 - $this->plugin_classes();
231 - $this->activation->deactivate();
252 + public function cli_activate( $args ) {
253 + // Handle both ways to activate the plugin.
254 + if ( count( $args ) === 1 && strpos( $args[0], '-' ) !== false ) {
255 + list( $secret, $id ) = explode( '-', $args[0] );
256 + } else {
257 + $id = isset( $args[0] ) ? trim( $args[0] ) : '';
258 + $secret = isset( $args[1] ) ? trim( $args[1] ) : '';
259 + }
260 +
261 + $result = $this->activation->alter_license( $id, $secret, 'activate' );
262 + if ( $result['result'] == 'error' ) {
263 + \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.' );
264 + return;
265 + }
266 +
267 + \WP_CLI::success( 'The Patchstack plugin has been successfully connected.' );
232 268 }
233 269
234 270 /**
235 - * Boot Patchstack and its classes.
271 + * Deactivate the plugin.
236 272 *
237 273 * @return void
238 274 */
239 - public function hooks() {
240 - add_action( 'init', array( $this, 'init' ), ~PHP_INT_MAX );
275 + public function deactivate() {
276 + $this->plugin_classes();
277 + $this->activation->deactivate();
241 278 }
242 279
243 280 /**
244 - * Boot Patchstack
281 + * Boot Patchstack.
245 282 *
246 283 * @return void
247 284 */
248 285 public function init() {
@@ -259,9 +296,9 @@
259 296 if ( get_option( 'patchstack_api_token', '' ) == '' && get_option( 'patchstack_license_expiry', '' ) == '' ) {
260 297 $this->api->update_license_status();
261 298 }
262 299
263 - // Determine if the license is activated and not expired.
300 + // Run firewall if not disabled and license activated.
264 301 if ( get_option( 'patchstack_license_activated', 0 ) == 1 && get_option( 'patchstack_basic_firewall', 0 ) == 1 && get_option( 'patchstack_license_free', 0 ) == 0 ) {
265 302 $this->firewall = new P_Firewall( true, $this );
266 303 }
267 304 }
@@ -316,9 +353,9 @@
316 353 * @return void
317 354 */
318 355 function patchstack_uninstall() {
319 356 // 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' );
357 + $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 358 foreach ( $options as $option ) {
322 359 delete_option( $option );
323 360
324 361 if ( is_multisite() ) {
@@ -327,9 +364,9 @@
327 364 }
328 365
329 366 // Drop all Patchstack tables.
330 367 global $wpdb;
331 - $tables = array( 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' );
368 + $tables = [ 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' ];
332 369 foreach ( $tables as $table ) {
333 370 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . $table );
334 371 }
335 372 }
@@ -346,10 +383,10 @@
346 383 }
347 384 }
348 385
349 386 // Kick it off.
350 -add_action( 'plugins_loaded', array( patchstack(), 'hooks' ) );
387 +add_action( 'plugins_loaded', [ patchstack(), 'init' ] );
351 388
352 389 // Activation and deactivation hooks.
353 -register_activation_hook( __FILE__, array( patchstack(), 'activate' ) );
354 -register_deactivation_hook( __FILE__, array( patchstack(), 'deactivate' ) );
390 +register_activation_hook( __FILE__, [ patchstack(), 'activate' ] );
391 +register_deactivation_hook( __FILE__, [ patchstack(), 'deactivate' ] );
355 392 register_uninstall_hook( __FILE__, 'patchstack_uninstall' );