PluginProbe
Patchstack – WordPress & Plugins Security / 2.1.5
Patchstack – WordPress & Plugins Security v2.1.5
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 | includes/admin/general.php +90 -45 trunk2.1.5 View file →
@@ -20,15 +20,16 @@
20 20 public function __construct( $core ) {
21 21 parent::__construct( $core );
22 22
23 23 // Add admin and network notices.
24 - add_action( 'admin_notices', [ $this, 'file_error_notice' ] );
25 - add_action( 'network_admin_notices', [ $this, 'file_error_notice' ] );
26 - add_action( 'update_option_siteurl', [ $this, 'update_option_url' ], 10, 2 );
27 - // Use updated_option (fires after the value is written) so auto_prepend_injection()
28 - // re-reads the new value; update_option fires before the write and sees the old value.
29 - add_action( 'updated_option', [ $this, 'update_option_ap' ], 10, 3 );
24 + add_action( 'admin_notices', array( $this, 'file_error_notice' ) );
25 + add_action( 'network_admin_notices', array( $this, 'file_error_notice' ) );
30 26
27 + add_action( 'wp_loaded', array( $this, 'update_rules' ) );
28 + add_action( 'update_option_siteurl', array( $this, 'update_option_url' ), 10, 2 );
29 + add_action( 'admin_init', array( $this, 'alter_ips' ) );
30 + add_action( 'admin_init', array( $this, 'enable_settings' ) );
31 +
31 32 // If the firewall or whitelist rules do not exist, attempt to pull fresh.
32 33 $token = get_option( 'patchstack_api_token', false );
33 34 if ( ! empty( $token ) && ( get_option( 'patchstack_firewall_rules', '' ) == '' || get_option( 'patchstack_whitelist_keys_rules' ) == '' ) && get_option( 'patchstack_license_free', 0 ) != 1 ) {
34 35 do_action( 'patchstack_post_dynamic_firewall_rules' );
@@ -41,24 +42,14 @@
41 42 * @return void
42 43 */
43 44 public function file_error_notice() {
44 45 // No need to display this error if the .htaccess functionality has been disabled.
45 - if ( get_site_option( 'patchstack_disable_htaccess', 0 ) || ( defined( 'PS_DISABLE_HTACCESS' ) && PS_DISABLE_HTACCESS ) ) {
46 + if ( get_site_option( 'patchstack_disable_htaccess', 0 ) ) {
46 47 return;
47 48 }
48 49
49 - // No need to display if a free user without protection.
50 - if ( get_option( 'patchstack_license_free', 0 ) == 1 ) {
51 - return;
52 - }
53 -
54 - // No need to display on nginx.
55 - if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stripos( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) !== false ) {
56 - return;
57 - }
58 -
59 50 // Check root .htaccess file and data folder writability.
60 - $files = [];
51 + $files = array();
61 52 if ( file_exists( ABSPATH . '.htaccess' ) && ! wp_is_writable( ABSPATH . '.htaccess' ) ) {
62 53 array_push( $files, ABSPATH . '.htaccess' );
63 54 }
64 55
@@ -69,12 +60,12 @@
69 60 <h2>Patchstack File Permission Error</h2>
70 61 <p><?php esc_html_e( 'The following file/folder could not be written to:<br />' . implode( '<br />', $files ), 'patchstack' ); ?></p>
71 62 <?php
72 63 foreach ( $files as $file ) {
73 - echo wp_kses( '<p><b>Debug info: </b>' . $file . ' chmod permissions: <b>' . substr( decoct( fileperms( $file ) ), -3 ) . '</b>, owned by <b>' . $this->get_file_owner_name( $file ) . '</b></p>', $this->allowed_html );
64 + echo wp_kses( '<p><b>Debug info: </b>' . $file . ' chmod permissions: <b>' . substr( decoct( fileperms( $file ) ), -3 ) . '</b>, owned by <b>' . posix_getpwuid( fileowner( $file ) )['name'] . '</b></p>', $this->allowed_html );
74 65 }
75 66 ?>
76 - <p><?php esc_html_e( '<strong>How to fix?</strong><br />CHMOD the file/folder to <strong>755</strong> through a <a href="http://www.dummies.com/web-design-development/wordpress/navigation-customization/how-to-change-file-permissions-using-filezilla-on-your-ftp-site/" target="_blank">FTP client</a>, <a href="http://support.hostgator.com/articles/cpanel/how-to-change-permissions-chmod-of-a-file" target="_blank">CPanel</a>, <a href="https://www.inmotionhosting.com/support/website/managing-files/change-file-permissions" target="_blank">WHM</a> or ask your hosting provider. Make sure file or folder ownership is set to <b>' . $this->get_file_owner_name( ABSPATH . 'index.php' ) . '</b> user .', 'patchstack_file_error_notice' ); ?></p>
67 + <p><?php esc_html_e( '<strong>How to fix?</strong><br />CHMOD the file/folder to <strong>755</strong> through a <a href="http://www.dummies.com/web-design-development/wordpress/navigation-customization/how-to-change-file-permissions-using-filezilla-on-your-ftp-site/" target="_blank">FTP client</a>, <a href="http://support.hostgator.com/articles/cpanel/how-to-change-permissions-chmod-of-a-file" target="_blank">CPanel</a>, <a href="https://www.inmotionhosting.com/support/website/managing-files/change-file-permissions" target="_blank">WHM</a> or ask your hosting provider. Make sure file or folder ownership is set to <b>' . posix_getpwuid( fileowner( ABSPATH . 'index.php' ) )['name'] . '</b> user .', 'patchstack_file_error_notice' ); ?></p>
77 68 <p><?php esc_html_e( '<strong>CHMOD properly set but still not working?</strong><br />Make sure the group/owner (chown) settings of the /wp-content/plugins/patchstack/ folder is properly setup, you may have to ask your host to fix this.', 'patchstack_file_error_notice' ); ?></p>
78 69 </div>
79 70 <?php
80 71 }
@@ -80,21 +71,28 @@
80 71 }
81 72 }
82 73
83 74 /**
84 - * Resolve the owning system user name for a file, guarding the POSIX extension
85 - * which is not available on Windows or some hardened hosts.
75 + * When the user changes Patchstack plugin settings, update the firewall rules.
86 76 *
87 - * @param string $file
88 - * @return string
77 + * @return void
89 78 */
90 - private function get_file_owner_name( $file ) {
91 - if ( ! function_exists( 'posix_getpwuid' ) || ! function_exists( 'fileowner' ) ) {
92 - return '';
79 + public function update_rules() {
80 + if ( isset( $_GET['settings-updated'], $_GET['page'] ) && strpos( $_GET['page'], 'patchstack' ) !== false && current_user_can( 'administrator' ) ) {
81 + $this->plugin->rules->post_firewall_rules();
82 + $this->plugin->rules->dynamic_firewall_rules();
83 +
84 + // Update firewall status after settings saved
85 + $token = $this->plugin->api->get_access_token();
86 +
87 + // Update the firewall status.
88 + if ( ! empty( $token ) ) {
89 + $this->plugin->api->update_firewall_status( array( 'status' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ) );
90 + }
91 +
92 + // Update the custom whitelist.
93 + update_option( 'patchstack_custom_whitelist_rules', $this->get_option( 'patchstack_whitelist' ) );
93 94 }
94 -
95 - $owner = posix_getpwuid( fileowner( $file ) );
96 - return isset( $owner['name'] ) ? $owner['name'] : '';
97 95 }
98 96
99 97 /**
100 98 * When the user updates the site URL, update it on the API side as well.
@@ -105,33 +103,80 @@
105 103 * @return void
106 104 */
107 105 public function update_option_url( $old_value, $new_value ) {
108 106 if ( $old_value != $new_value ) {
109 - $this->plugin->api->update_url( [ 'plugin_url' => $new_value ] );
107 + $this->plugin->api->update_url( array( 'plugin_url' => $new_value ) );
110 108 }
111 109 }
112 110
113 111 /**
114 - * When the firewall auto prepend option value is changed, ensure that we prepare the environment or remove it from the environment.
115 - *
116 - * @param mixed $option_name
117 - * @param mixed $old_value
118 - * @param mixed $new_value
112 + * Executed when the user modifies the blocked or whitelisted IP addresses on the
113 + * login protection settings page.
114 + *
119 115 * @return void
120 116 */
121 - public function update_option_ap( $option_name, $old_value, $new_value ) {
122 - if ( $option_name != 'patchstack_firewall_ap_enabled' ) {
117 + public function alter_ips() {
118 + if ( ! isset( $_GET['action'], $_GET['PatchstackNonce'] ) || ! wp_verify_nonce( $_GET['PatchstackNonce'], 'patchstack-nonce-alter-ips' ) || ! current_user_can( 'administrator' ) || ! in_array( $_GET['action'], array( 'patchstack_unblock', 'patchstack_unblock_whitelist', 'patchstack_whitelist' ) ) ) {
123 119 return;
124 120 }
125 121
126 - // No need to perform if user is on free plan.
127 - if ( get_option( 'patchstack_license_activated', 0 ) != 1 ) {
122 + global $wpdb;
123 +
124 + // Unblock the IP; delete the logs of the IP.
125 + if ( $_GET['action'] == 'patchstack_unblock' && isset( $_GET['id'] ) && ctype_digit( $_GET['id'] ) ) {
126 + // First get the IP address to unblock.
127 + $result = $wpdb->get_results(
128 + $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id = %d', array( (int) $_GET['id'] ) )
129 + );
130 +
131 + // Unblock the IP address.
132 + if ( isset( $result[0], $result[0]->ip ) ) {
133 + $wpdb->query(
134 + $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE ip = %s', array( $result[0]->ip ) )
135 + );
136 + }
137 + }
138 +
139 + // Unblock and whitelist the IP.
140 + if ( $_GET['action'] == 'patchstack_unblock_whitelist' && isset( $_GET['id'] ) && ctype_digit( $_GET['id'] ) ) {
141 + // First get the IP address to whitelist.
142 + $result = $wpdb->get_results(
143 + $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id = %d', array( (int) $_GET['id'] ) )
144 + );
145 +
146 + // Whitelist and unblock the IP address.
147 + if ( isset( $result[0], $result[0]->ip ) && filter_var( $result[0]->ip, FILTER_VALIDATE_IP ) ) {
148 + update_option( 'patchstack_login_whitelist', $this->get_option( 'patchstack_login_whitelist', '' ) . "\n" . $result[0]->ip );
149 + $wpdb->query(
150 + $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE ip = %s', array( $result[0]->ip ) )
151 + );
152 + }
153 + }
154 +
155 + // Whitelist an IP address.
156 + if ( $_GET['action'] == 'patchstack_whitelist' && isset( $_GET['ip'] ) && filter_var( $_GET['ip'], FILTER_VALIDATE_IP ) ) {
157 + update_option( 'patchstack_login_whitelist', $this->get_option( 'patchstack_login_whitelist', '' ) . "\n" . $_GET['ip'] );
158 + }
159 +
160 + // Redirect the user back to the login tab.
161 + wp_safe_redirect( admin_url( 'admin.php?page=' . $this->plugin->name . '&tab=login' ) );
162 + exit;
163 + }
164 +
165 + /**
166 + * Turn on the Patchstack settings feature on WordPress.
167 + *
168 + * @return void
169 + */
170 + public function enable_settings() {
171 + if ( ! isset( $_GET['action'], $_GET['patchstack_settings_nonce'] ) || ! wp_verify_nonce( $_GET['patchstack_settings_nonce'], 'patchstack_settings_nonce' ) || ! current_user_can( 'administrator' ) || $_GET['action'] != 'enable_settings' ) {
128 172 return;
129 173 }
130 174
131 - if ( $new_value && (int) get_option( 'patchstack_license_free', 0 ) == 0 ) {
132 - $this->plugin->activation->auto_prepend_injection();
133 - } else {
134 - $this->plugin->activation->auto_prepend_removal();
135 - }
175 + // Turn it on.
176 + update_option( 'patchstack_show_settings', 1 );
177 +
178 + // Redirect the user back to the license page.
179 + wp_safe_redirect( admin_url( 'admin.php?page=' . $this->plugin->name . '&tab=license' ) );
180 + exit;
136 181 }
137 182 }