PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.10
Patchstack – WordPress & Plugins Security v2.2.10
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 +65 -32 2.1.42.2.10 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.4
7 + * Version: 2.2.10
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.4';
62 + const VERSION = '2.2.10';
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 *
@@ -149,9 +143,8 @@
149 143 protected $hide_login;
150 144 protected $listener;
151 145 protected $event_log;
152 146 protected $multisite;
153 - protected $notice;
154 147 protected $admin_ajax;
155 148 protected $admin_general;
156 149 protected $admin_menu;
157 150 protected $admin_options;
@@ -176,8 +169,13 @@
176 169 $this->basename = plugin_basename( __FILE__ );
177 170 $this->url = plugin_dir_url( __FILE__ );
178 171 $names = explode( '/', $this->basename );
179 172 $this->name = $names[0];
173 +
174 + // Define WP_CLI command.
175 + if ( defined( 'WP_CLI' ) && WP_CLI && method_exists('\WP_CLI', 'add_command')) {
176 + \WP_CLI::add_command( 'patchstack activate', [ $this, 'cli_activate' ] );
177 + }
180 178 }
181 179
182 180 /**
183 181 * Call the constructor of all the Patchstack related classes.
@@ -185,9 +183,9 @@
185 183 * @return void
186 184 */
187 185 public function plugin_classes() {
188 186 // Define the array of the classes.
189 - foreach ( array(
187 + foreach ( [
190 188 'admin_options' => 'P_Admin_Options',
191 189 'cron' => 'P_Cron',
192 190 'api' => 'P_Api',
193 191 'login' => 'P_Login',
@@ -197,20 +195,20 @@
197 195 'hacker_log' => 'P_Hacker_Log',
198 196 'upload' => 'P_Upload',
199 197 'rules' => 'P_Rules',
200 198 'hide_login' => 'P_Hide_Login',
201 - 'listener' => 'P_Listener',
202 199 'event_log' => 'P_Event_Log',
203 200 'activation' => 'P_Activation',
201 + 'listener' => 'P_Listener',
204 202 'multisite' => 'P_Multisite',
205 - 'notice' => 'P_Cookie_Notice',
206 203 'admin_ajax' => 'P_Admin_Ajax',
207 204 'admin_general' => 'P_Admin_General',
208 205 'admin_menu' => 'P_Admin_Menu',
209 - ) as $var => $class ) {
206 + ] as $var => $class ) {
210 207 $this->$var = new $class( $this );
211 208 }
212 209
210 + // Load firewall base functionality.
213 211 $this->firewall_base = new P_Firewall( true, $this, true );
214 212 }
215 213
216 214 /**
@@ -223,28 +221,63 @@
223 221 $this->activation->activate( $this );
224 222 }
225 223
226 224 /**
227 - * Deactivate the plugin.
225 + * Connects the Patchstack plugin to the API with the license id and secret key.
228 226 *
229 - * @return void
227 + * Returns an error if the connection was not successful.
228 + *
229 + * ## OPTIONS
230 + *
231 + * [<id>]
232 + * : The API client id.
233 + *
234 + * [<secret>]
235 + * : The API secret key.
236 + *
237 + * <secret-id>
238 + * : The API client id and secret key merged together, found in the App. E.g. 2b072e8b60402e30d481df351fc08183906254e0-123456
239 + *
240 + * ## EXAMPLES
241 + *
242 + * $ wp patchstack activate 123456 2b072e8b60402e30d481df351fc08183906254e0
243 + * Success: The Patchstack plugin has been successfully connected.
244 + *
245 + * or
246 + *
247 + * $ wp patchstack activate 2b072e8b60402e30d481df351fc08183906254e0-123456
248 + * Success: The Patchstack plugin has been successfully connected.
230 249 */
231 - public function deactivate() {
232 - $this->plugin_classes();
233 - $this->activation->deactivate();
250 + public function cli_activate( $args ) {
251 + // Handle both ways to activate the plugin.
252 + if ( count( $args ) === 1 && strpos( $args[0], '-' ) !== false ) {
253 + list( $secret, $id ) = explode( '-', $args[0] );
254 + } else {
255 + $id = isset( $args[0] ) ? trim( $args[0] ) : '';
256 + $secret = isset( $args[1] ) ? trim( $args[1] ) : '';
257 + }
258 +
259 + $result = $this->activation->alter_license( $id, $secret, 'activate' );
260 + if ( $result['result'] == 'error' ) {
261 + \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.' );
262 + return;
263 + }
264 +
265 + \WP_CLI::success( 'The Patchstack plugin has been successfully connected.' );
234 266 }
235 267
236 268 /**
237 - * Boot Patchstack and its classes.
269 + * Deactivate the plugin.
238 270 *
239 271 * @return void
240 272 */
241 - public function hooks() {
242 - add_action( 'init', array( $this, 'init' ), ~PHP_INT_MAX );
273 + public function deactivate() {
274 + $this->plugin_classes();
275 + $this->activation->deactivate();
243 276 }
244 277
245 278 /**
246 - * Boot Patchstack
279 + * Boot Patchstack.
247 280 *
248 281 * @return void
249 282 */
250 283 public function init() {
@@ -261,9 +294,9 @@
261 294 if ( get_option( 'patchstack_api_token', '' ) == '' && get_option( 'patchstack_license_expiry', '' ) == '' ) {
262 295 $this->api->update_license_status();
263 296 }
264 297
265 - // Determine if the license is activated and not expired.
298 + // Run firewall if not disabled and license activated.
266 299 if ( get_option( 'patchstack_license_activated', 0 ) == 1 && get_option( 'patchstack_basic_firewall', 0 ) == 1 && get_option( 'patchstack_license_free', 0 ) == 0 ) {
267 300 $this->firewall = new P_Firewall( true, $this );
268 301 }
269 302 }
@@ -318,9 +351,9 @@
318 351 * @return void
319 352 */
320 353 function patchstack_uninstall() {
321 354 // Delete most of the Patchstack options.
322 - $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' );
355 + $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' ];
323 356 foreach ( $options as $option ) {
324 357 delete_option( $option );
325 358
326 359 if ( is_multisite() ) {
@@ -329,9 +362,9 @@
329 362 }
330 363
331 364 // Drop all Patchstack tables.
332 365 global $wpdb;
333 - $tables = array( 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' );
366 + $tables = [ 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' ];
334 367 foreach ( $tables as $table ) {
335 368 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . $table );
336 369 }
337 370 }
@@ -348,10 +381,10 @@
348 381 }
349 382 }
350 383
351 384 // Kick it off.
352 -add_action( 'plugins_loaded', array( patchstack(), 'hooks' ) );
385 +add_action( 'plugins_loaded', [ patchstack(), 'init' ] );
353 386
354 387 // Activation and deactivation hooks.
355 -register_activation_hook( __FILE__, array( patchstack(), 'activate' ) );
356 -register_deactivation_hook( __FILE__, array( patchstack(), 'deactivate' ) );
388 +register_activation_hook( __FILE__, [ patchstack(), 'activate' ] );
389 +register_deactivation_hook( __FILE__, [ patchstack(), 'deactivate' ] );
357 390 register_uninstall_hook( __FILE__, 'patchstack_uninstall' );