PluginProbe
Patchstack – WordPress & Plugins Security / 2.1.17
Patchstack – WordPress & Plugins Security v2.1.17
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.17, at patchstack.php

351 lines 10.2 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.17
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.17';
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 * URL of the plugin directory.
93 *
94 * @var string
95 */
96 protected $url = '';
97
98 /**
99 * Plugin basename.
100 *
101 * @var string
102 */
103 protected $basename = '';
104
105 /**
106 * Plugin name.
107 *
108 * @var string
109 */
110 protected $name = '';
111
112 /**
113 * Detailed activation error messages.
114 *
115 * @var array
116 */
117 protected $activation_errors = array();
118
119 /**
120 * Singleton instance of plugin.
121 *
122 * @var Patchstack
123 */
124 protected static $single_instance = null;
125
126 /**
127 * Define all the variables that will hold the Patchstack classes.
128 * These must be defined because it allows us to communicate from one class to the other.
129 */
130 protected $firewall;
131 protected $firewall_base;
132 protected $activation;
133 protected $cron;
134 protected $api;
135 protected $login;
136 protected $ban;
137 protected $hardening;
138 protected $htaccess;
139 protected $hacker_log;
140 protected $upload;
141 protected $rules;
142 protected $hide_login;
143 protected $listener;
144 protected $event_log;
145 protected $multisite;
146 protected $notice;
147 protected $admin_ajax;
148 protected $admin_general;
149 protected $admin_menu;
150 protected $admin_options;
151
152 /**
153 * Setup a few base variables for the plugin.
154 * Also make sure certain constants are defined.
155 *
156 * @return void
157 */
158 protected function __construct() {
159 // Set the permission constants if not already set.
160 if ( ! defined( 'FS_CHMOD_DIR' ) ) {
161 define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
162 }
163
164 if ( ! defined( 'FS_CHMOD_FILE' ) ) {
165 define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
166 }
167
168 // Define local variables.
169 $this->basename = plugin_basename( __FILE__ );
170 $this->url = plugin_dir_url( __FILE__ );
171 $names = explode( '/', $this->basename );
172 $this->name = $names[0];
173 }
174
175 /**
176 * Call the constructor of all the Patchstack related classes.
177 *
178 * @return void
179 */
180 public function plugin_classes() {
181 // Define the array of the classes.
182 foreach ( array(
183 'admin_options' => 'P_Admin_Options',
184 'cron' => 'P_Cron',
185 'api' => 'P_Api',
186 'login' => 'P_Login',
187 'ban' => 'P_Ban',
188 'hardening' => 'P_Hardening',
189 'htaccess' => 'P_Htaccess',
190 'hacker_log' => 'P_Hacker_Log',
191 'upload' => 'P_Upload',
192 'rules' => 'P_Rules',
193 'hide_login' => 'P_Hide_Login',
194 'listener' => 'P_Listener',
195 'event_log' => 'P_Event_Log',
196 'activation' => 'P_Activation',
197 'multisite' => 'P_Multisite',
198 'notice' => 'P_Cookie_Notice',
199 'admin_ajax' => 'P_Admin_Ajax',
200 'admin_general' => 'P_Admin_General',
201 'admin_menu' => 'P_Admin_Menu',
202 ) as $var => $class ) {
203 $this->$var = new $class( $this );
204 }
205
206 $this->firewall_base = new P_Firewall( true, $this, true );
207 }
208
209 /**
210 * Activate the plugin.
211 *
212 * @return void
213 */
214 public function activate() {
215 $this->plugin_classes();
216 $this->activation->activate( $this );
217 }
218
219 /**
220 * Deactivate the plugin.
221 *
222 * @return void
223 */
224 public function deactivate() {
225 $this->plugin_classes();
226 $this->activation->deactivate();
227 }
228
229 /**
230 * Boot Patchstack and its classes.
231 *
232 * @return void
233 */
234 public function hooks() {
235 add_action( 'init', array( $this, 'init' ), ~PHP_INT_MAX );
236 }
237
238 /**
239 * Boot Patchstack
240 *
241 * @return void
242 */
243 public function init() {
244 // Load translated strings for plugin.
245 load_plugin_textdomain( 'patchstack', false, dirname( $this->basename ) . '/languages/' );
246
247 // Initialize plugin classes.
248 $this->plugin_classes();
249
250 // Perform migrations if necessary.
251 $this->activation->migrate_check();
252
253 // If license expiration has not been fetched yet while the plugin is active, update it.
254 if ( get_option( 'patchstack_api_token', '' ) == '' && get_option( 'patchstack_license_expiry', '' ) == '' ) {
255 $this->api->update_license_status();
256 }
257
258 // Determine if the license is activated and not expired.
259 if ( get_option( 'patchstack_license_activated', 0 ) == 1 && get_option( 'patchstack_basic_firewall', 0 ) == 1 && get_option( 'patchstack_license_free', 0 ) == 0 ) {
260 $this->firewall = new P_Firewall( true, $this );
261 }
262 }
263
264 /**
265 * Creates or returns an instance of this class.
266 *
267 * @return Patchstack
268 */
269 public static function get_instance() {
270 if ( null === self::$single_instance ) {
271 self::$single_instance = new self();
272 }
273
274 return self::$single_instance;
275 }
276
277 /**
278 * Magic getter.
279 *
280 * @param string $field The field to magically get.
281 * @return mixed
282 */
283 public function __get( $field ) {
284 switch ( $field ) {
285 case 'version':
286 return self::VERSION;
287 case 'api_url':
288 return self::API_URL;
289 case 'auth_url':
290 return self::AUTH_URL;
291 case 'client_id':
292 return self::CLIENT_ID;
293 case 'private_key':
294 return self::PRIVATE_KEY;
295 default:
296 try {
297 return $this->$field;
298 } catch ( \Exception $e ) {
299 return null;
300 }
301 }
302 }
303 }
304 }
305
306 if ( ! function_exists( 'patchstack_uninstall' ) ) {
307 /**
308 * Called when the plugin is uninstalled/removed from the site.
309 * This is not the same as deactivation, where the plugin still resides on the site.
310 *
311 * @return void
312 */
313 function patchstack_uninstall() {
314 // Delete most of the Patchstack options.
315 $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' );
316 foreach ( $options as $option ) {
317 delete_option( $option );
318
319 if ( is_multisite() ) {
320 delete_site_option( $option );
321 }
322 }
323
324 // Drop all Patchstack tables.
325 global $wpdb;
326 $tables = array( 'patchstack_user_log', 'patchstack_visitor_log', 'patchstack_firewall_log', 'patchstack_file_hashes', 'patchstack_logic', 'patchstack_ip', 'patchstack_event_log' );
327 foreach ( $tables as $table ) {
328 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . $table );
329 }
330 }
331 }
332
333 if ( ! function_exists( 'patchstack' ) ) {
334 /**
335 * Grab the Patchstack object and return it.
336 *
337 * @return Patchstack
338 */
339 function patchstack() {
340 return patchstack::get_instance();
341 }
342 }
343
344 // Kick it off.
345 add_action( 'plugins_loaded', array( patchstack(), 'hooks' ) );
346
347 // Activation and deactivation hooks.
348 register_activation_hook( __FILE__, array( patchstack(), 'activate' ) );
349 register_deactivation_hook( __FILE__, array( patchstack(), 'deactivate' ) );
350 register_uninstall_hook( __FILE__, 'patchstack_uninstall' );
351