PluginProbe
Patchstack – WordPress & Plugins Security / 2.1.4
Patchstack – WordPress & Plugins Security v2.1.4
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
patchstack / patchstack.php

patchstack.php in Patchstack – WordPress & Plugins Security 2.1.4, at patchstack.php

358 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Patchstack Security
4 * Plugin URI: https://patchstack.com
5 * Description: Patchstack identifies security vulnerabilities in WordPress plugins, themes, and core.
6 * Version: 2.1.4
7 * Author: Patchstack
8 * License: GPLv3
9 * Text Domain: patchstack
10 * Domain Path: /languages
11 * Requires at least: 4.4
12 * Requires PHP: 5.6
13 */
14
15 // Do not allow the file to be called directly.
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 if ( ! function_exists( 'patchstack_autoload_classes' ) ) {
21 /**
22 * Autoloads the Patchstack classes when called.
23 *
24 * @param string $class_name The class name to autoload.
25 * @return void
26 */
27 function patchstack_autoload_classes( $class_name ) {
28 // If the requested class doesn't have our prefix, don't load it.
29 if ( strpos( $class_name, 'P_' ) !== 0 ) {
30 return;
31 }
32
33 // Set up our filename.
34 $file_name = strtolower( str_replace( '_', '-', substr( $class_name, strlen( 'P_' ) ) ) );
35 $dir = trailingslashit( dirname( __FILE__ ) ) . 'includes/';
36 $target = array( $dir . $file_name . '.php', $dir . 'admin/' . str_replace( 'admin-', '', $file_name ) . '.php' );
37
38 // Attempt each target and load if it exists.
39 foreach ( $target as $file ) {
40 if ( file_exists( $file ) ) {
41 include_once $file;
42 }
43 }
44 }
45 }
46 spl_autoload_register( 'patchstack_autoload_classes' );
47
48 if ( ! class_exists( 'patchstack' ) ) {
49
50 /**
51 * This is the main Patchstack class used for all Patchstack related features and to launch
52 * the Patchstack plugin.
53 */
54 class Patchstack {
55
56 /**
57 * The plugin version.
58 *
59 * @var string
60 */
61 const VERSION = '2.1.4';
62
63 /**
64 * API URL of Patchstack to communicate with.
65 *
66 * @var string
67 */
68 const API_URL = 'https://api.patchstack.com';
69
70 /**
71 * API Auth URL of Patchstack to communicate with.
72 *
73 * @var string
74 */
75 const AUTH_URL = 'https://auth.patchstack.com';
76
77 /**
78 * Client ID, this is only set when freshly downloaded from the app.
79 *
80 * @var string
81 */
82 const CLIENT_ID = 'PATCHSTACK_CLIENT_ID';
83
84 /**
85 * Client private key, this is only set when freshly downloaded from the app.
86 *
87 * @var string
88 */
89 const PRIVATE_KEY = 'PATCHSTACK_PRIVATE_KEY';
90
91 /**
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 * URL of the plugin directory.
100 *
101 * @var string
102 */
103 protected $url = '';
104
105 /**
106 * Plugin basename.
107 *
108 * @var string
109 */
110 protected $basename = '';
111
112 /**
113 * Plugin name.
114 *
115 * @var string
116 */
117 protected $name = '';
118
119 /**
120 * Detailed activation error messages.
121 *
122 * @var array
123 */
124 protected $activation_errors = array();
125
126 /**
127 * Singleton instance of plugin.
128 *
129 * @var Patchstack
130 */
131 protected static $single_instance = null;
132
133 /**
134 * Define all the variables that will hold the Patchstack classes.
135 * These must be defined because it allows us to communicate from one class to the other.
136 */
137 protected $firewall;
138 protected $firewall_base;
139 protected $activation;
140 protected $cron;
141 protected $api;
142 protected $login;
143 protected $ban;
144 protected $hardening;
145 protected $htaccess;
146 protected $hacker_log;
147 protected $upload;
148 protected $rules;
149 protected $hide_login;
150 protected $listener;
151 protected $event_log;
152 protected $multisite;
153 protected $notice;
154 protected $admin_ajax;
155 protected $admin_general;
156 protected $admin_menu;
157 protected $admin_options;
158
159 /**
160 * Setup a few base variables for the plugin.
161 * Also make sure certain constants are defined.
162 *
163 * @return void
164 */
165 protected function __construct() {
166 // Set the permission constants if not already set.
167 if ( ! defined( 'FS_CHMOD_DIR' ) ) {
168 define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
169 }
170
171 if ( ! defined( 'FS_CHMOD_FILE' ) ) {
172 define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
173 }
174
175 // Define local variables.
176 $this->basename = plugin_basename( __FILE__ );
177 $this->url = plugin_dir_url( __FILE__ );
178 $names = explode( '/', $this->basename );
179 $this->name = $names[0];
180 }
181
182 /**
183 * Call the constructor of all the Patchstack related classes.
184 *
185 * @return void
186 */
187 public function plugin_classes() {
188 // Define the array of the classes.
189 foreach ( array(
190 'admin_options' => 'P_Admin_Options',
191 'cron' => 'P_Cron',
192 'api' => 'P_Api',
193 'login' => 'P_Login',
194 'ban' => 'P_Ban',
195 'hardening' => 'P_Hardening',
196 'htaccess' => 'P_Htaccess',
197 'hacker_log' => 'P_Hacker_Log',
198 'upload' => 'P_Upload',
199 'rules' => 'P_Rules',
200 'hide_login' => 'P_Hide_Login',
201 'listener' => 'P_Listener',
202 'event_log' => 'P_Event_Log',
203 'activation' => 'P_Activation',
204 'multisite' => 'P_Multisite',
205 'notice' => 'P_Cookie_Notice',
206 'admin_ajax' => 'P_Admin_Ajax',
207 'admin_general' => 'P_Admin_General',
208 'admin_menu' => 'P_Admin_Menu',
209 ) as $var => $class ) {
210 $this->$var = new $class( $this );
211 }
212
213 $this->firewall_base = new P_Firewall( true, $this, true );
214 }
215
216 /**
217 * Activate the plugin.
218 *
219 * @return void
220 */
221 public function activate() {
222 $this->plugin_classes();
223 $this->activation->activate( $this );
224 }
225
226 /**
227 * Deactivate the plugin.
228 *
229 * @return void
230 */
231 public function deactivate() {
232 $this->plugin_classes();
233 $this->activation->deactivate();
234 }
235
236 /**
237 * Boot Patchstack and its classes.
238 *
239 * @return void
240 */
241 public function hooks() {
242 add_action( 'init', array( $this, 'init' ), ~PHP_INT_MAX );
243 }
244
245 /**
246 * Boot Patchstack
247 *
248 * @return void
249 */
250 public function init() {
251 // Load translated strings for plugin.
252 load_plugin_textdomain( 'patchstack', false, dirname( $this->basename ) . '/languages/' );
253
254 // Initialize plugin classes.
255 $this->plugin_classes();
256
257 // Perform migrations if necessary.
258 $this->activation->migrate_check();
259
260 // If license expiration has not been fetched yet while the plugin is active, update it.
261 if ( get_option( 'patchstack_api_token', '' ) == '' && get_option( 'patchstack_license_expiry', '' ) == '' ) {
262 $this->api->update_license_status();
263 }
264
265 // Determine if the license is activated and not expired.
266 if ( get_option( 'patchstack_license_activated', 0 ) == 1 && get_option( 'patchstack_basic_firewall', 0 ) == 1 && get_option( 'patchstack_license_free', 0 ) == 0 ) {
267 $this->firewall = new P_Firewall( true, $this );
268 }
269 }
270
271 /**
272 * Creates or returns an instance of this class.
273 *
274 * @return Patchstack
275 */
276 public static function get_instance() {
277 if ( null === self::$single_instance ) {
278 self::$single_instance = new self();
279 }
280
281 return self::$single_instance;
282 }
283
284 /**
285 * Magic getter.
286 *
287 * @param string $field The field to magically get.
288 * @return mixed
289 */
290 public function __get( $field ) {
291 switch ( $field ) {
292 case 'version':
293 return self::VERSION;
294 case 'api_url':
295 return self::API_URL;
296 case 'auth_url':
297 return self::AUTH_URL;
298 case 'client_id':
299 return self::CLIENT_ID;
300 case 'private_key':
301 return self::PRIVATE_KEY;
302 default:
303 try {
304 return $this->$field;
305 } catch ( \Exception $e ) {
306 return null;
307 }
308 }
309 }
310 }
311 }
312
313 if ( ! function_exists( 'patchstack_uninstall' ) ) {
314 /**
315 * Called when the plugin is uninstalled/removed from the site.
316 * This is not the same as deactivation, where the plugin still resides on the site.
317 *
318 * @return void
319 */
320 function patchstack_uninstall() {
321 // 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' );
323 foreach ( $options as $option ) {
324 delete_option( $option );
325
326 if ( is_multisite() ) {
327 delete_site_option( $option );
328 }
329 }
330
331 // Drop all Patchstack tables.
332 global $wpdb;
333 $tables = array( 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' );
334 foreach ( $tables as $table ) {
335 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . $table );
336 }
337 }
338 }
339
340 if ( ! function_exists( 'patchstack' ) ) {
341 /**
342 * Grab the Patchstack object and return it.
343 *
344 * @return Patchstack
345 */
346 function patchstack() {
347 return patchstack::get_instance();
348 }
349 }
350
351 // Kick it off.
352 add_action( 'plugins_loaded', array( patchstack(), 'hooks' ) );
353
354 // Activation and deactivation hooks.
355 register_activation_hook( __FILE__, array( patchstack(), 'activate' ) );
356 register_deactivation_hook( __FILE__, array( patchstack(), 'deactivate' ) );
357 register_uninstall_hook( __FILE__, 'patchstack_uninstall' );
358