PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.2
Patchstack – WordPress & Plugins Security v2.2.2
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 / includes / upload.php

upload.php in Patchstack – WordPress & Plugins Security 2.2.2, at includes/upload.php

294 lines 9.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Do not allow the file to be called directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * This class is used to upload the local logs to our API so it can
10 * be shown on the app.
11 */
12 class P_Upload extends P_Core {
13
14 /**
15 * Add the actions required to upload logs to our API.
16 *
17 * @param Patchstack $core
18 * @return void
19 */
20 public function __construct( $core ) {
21 parent::__construct( $core );
22
23 // In case the software has never been synchronized, force it.
24 if ( ! get_option( 'patchstack_software_data_hash', false ) ) {
25 $this->upload_software();
26 }
27
28 // Register the actions.
29 add_action( 'patchstack_send_software_data', [ $this, 'upload_software' ] );
30 add_action( 'patchstack_send_hacker_logs', [ $this, 'upload_firewall_logs' ] );
31 add_action( 'patchstack_send_event_logs', [ $this, 'upload_activity_logs' ] );
32
33 // In case a plugin or upgrade has been performed, re-synchronize with the app.
34 add_action( 'activated_plugin', [ $this, 'upload_software' ] );
35 add_action( 'deactivated_plugin', [ $this, 'upload_software' ] );
36 add_action( 'deleted_plugin', [ $this, 'upload_software' ] );
37 add_action( 'upgrader_process_complete', [ $this, 'upload_software' ] );
38 add_action( '_core_updated_successfully', [ &$this, 'upload_software' ] );
39 }
40
41 /**
42 * Synchronize the software data with our API.
43 * This includes plugins, themes, WordPress and PHP version.
44 *
45 * @return void|array
46 */
47 public function upload_software() {
48 // Get the software data and hash.
49 $data = $this->get_software_data();
50 $hash = sha1( json_encode( $data ) );
51 if ( ! defined( 'DOING_CRON' ) && ! isset( $_POST['webarx_secret'] ) && get_option( 'patchstack_software_data_hash', false ) === $hash && ! is_admin() ) {
52 return;
53 }
54
55 // Synchronize the software list with the API.
56 $results = $this->plugin->api->upload_software( [ 'software' => json_encode( $data ) ] );
57 if ( isset( $results['success'] ) ) {
58 update_option( 'patchstack_software_data_hash', $hash );
59
60 // The result will also contain a list of all vulnerable plugins on the site that is returned by the API.
61 // If the auto update setting is enabled for vulnerable plugins, perform the update once the 15 minute
62 // scheduled task "patchstack_update_plugins" is executed.
63 $update = get_site_option( 'patchstack_auto_update', [] );
64 if ( isset( $results['vulnerable'] ) && is_array( $update ) && in_array( 'vulnerable', $update ) ) {
65 update_site_option( 'patchstack_vulnerable_plugins', $results['vulnerable'] );
66 }
67
68 // If we have vulnerable plugins, determine if we had them before and if not, pull latest firewall rules.
69 if ( isset( $results['vulnerable'] ) && count( $results['vulnerable'] ) > 0 ) {
70 $prev = get_site_option( 'patchstack_latest_vulnerable', [] );
71 foreach ( $results['vulnerable'] as $vuln ) {
72 if ( ! in_array ( $vuln, $prev ) ) {
73 do_action( 'patchstack_post_dynamic_firewall_rules' );
74 break;
75 }
76 }
77
78 update_site_option( 'patchstack_latest_vulnerable', $results['vulnerable'] );
79 } else {
80 update_site_option( 'patchstack_latest_vulnerable', [] );
81 }
82
83 return $results;
84 }
85
86 return;
87 }
88
89 /**
90 * Synchronize the firewall logs with our API.
91 *
92 * @return void
93 */
94 public function upload_firewall_logs() {
95 global $wpdb;
96 $lastid = get_option( 'patchstack_firewall_log_lastid', 0 );
97 $items = $wpdb->get_results( $wpdb->prepare( 'SELECT ip, log_date, request_uri, user_agent, fid, method, post_data FROM ' . $wpdb->prefix . 'patchstack_firewall_log WHERE id > %d ORDER BY id', $lastid ) );
98
99 // No need to synchronize if there are no new logs present.
100 if ( $wpdb->num_rows == 0 ) {
101 return;
102 }
103
104 // Construct the array to be uploaded to our API.
105 $logs = [];
106 foreach ( $items as $item ) {
107
108 // Entries that we don't want to store on the API side.
109 if ( stripos( $item->request_uri, 'wp-comments-post' ) !== false ) {
110 continue;
111 }
112
113 // Push to entries to be uploaded.
114 $logs[] = [
115 'ip' => $item->ip,
116 'fid' => $item->fid,
117 'request_uri' => $item->request_uri,
118 'user_agent' => $item->user_agent,
119 'method' => $item->method,
120 'log_date' => $item->log_date,
121 'post_data' => $item->post_data,
122 ];
123 }
124
125 // JSON encode the logs and upload.
126 $logs = json_encode( $logs );
127 $results = $this->plugin->api->upload_firewall_logs(
128 [
129 'logs' => $logs,
130 'type' => 'firewall',
131 ]
132 );
133 if ( isset( $results['errors'] ) ) {
134 return;
135 }
136
137 // Get the most recent id of the logs.
138 $lastid = $wpdb->get_var( 'SELECT id FROM ' . $wpdb->prefix . 'patchstack_firewall_log ORDER BY id DESC LIMIT 0, 1' );
139 update_option( 'patchstack_firewall_log_lastid', $lastid );
140
141 // Delete logs that are older than 2 weeks.
142 $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_firewall_log WHERE log_date < DATE_SUB(NOW(), INTERVAL 14 DAY)' );
143 }
144
145 /**
146 * Synchronize the activity logs with our API.
147 *
148 * @return void
149 */
150 public function upload_activity_logs() {
151 global $wpdb;
152
153 // Determine if we should upload failed logins to the app.
154 $where = " AND action != 'failed login' ";
155 if ( $this->get_option( 'patchstack_activity_log_failed_logins_db', 0 ) == 1 ) {
156 $where = ' ';
157 }
158
159 // Do we have data to upload?
160 $lastid = get_option( 'patchstack_eventlog_lastid', 0 );
161 $items = $wpdb->get_results( $wpdb->prepare( 'SELECT author, ip, object, object_id, object_name, action, date FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id > %d' . $where . 'ORDER BY id', [ $lastid ] ) );
162 if ( $wpdb->num_rows == 0 ) {
163 return;
164 }
165
166 // Send to the API.
167 $logs = json_encode( $items );
168 $results = $this->plugin->api->upload_activity_logs( [ 'logs' => $logs ] );
169 if ( isset( $results['errors'] ) ) {
170 return;
171 }
172
173 // Get the most recent id of the logs.
174 $lastid = $wpdb->get_var( 'SELECT id FROM ' . $wpdb->prefix . 'patchstack_event_log ORDER BY id DESC LIMIT 0, 1' );
175 update_option( 'patchstack_eventlog_lastid', $lastid );
176
177 // Delete logs that are older than 2 weeks.
178 $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE date < DATE_SUB(NOW(), INTERVAL 14 DAY)' );
179 }
180
181 /**
182 * Obtain information about the software that the user has installed.
183 * This includes plugins, themes, WordPress and PHP version.
184 *
185 * @return array
186 */
187 public function get_software_data() {
188 if ( ! function_exists( 'get_plugins' ) ) {
189 require_once ABSPATH . 'wp-admin/includes/plugin.php';
190 }
191 if ( ! function_exists( 'get_plugin_updates' ) ) {
192 require_once ABSPATH . 'wp-admin/includes/update.php';
193 }
194
195 // Refetch updates data if we are performing a plugin listener related action.
196 if ( isset( $_POST['webarx_secret'] ) ) {
197 @require_once ABSPATH . 'wp-includes/update.php';
198 @wp_update_themes();
199 @wp_update_plugins();
200 }
201
202 // Fetch list of plugins.
203 $all_plugin = get_plugins();
204 $installed_plugins = array_keys( $all_plugin );
205 $updatable_plugins = get_plugin_updates();
206 $software_list = [];
207
208 foreach ( $installed_plugins as $plugin ) {
209 if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) {
210 continue;
211 }
212
213 $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
214 $new_version = empty( $updatable_plugins[ $plugin ]->update->new_version ) ? '' : $updatable_plugins[ $plugin ]->update->new_version;
215 $plugin_name = empty( $plugin_data['Name'] ) ? '' : $plugin_data['Name'];
216 $plugin_version = empty( $plugin_data['Version'] ) ? '' : $plugin_data['Version'];
217
218 if ( ! empty( $plugin_name ) && ! empty( $plugin_version ) ) {
219
220 // Determine the active state.
221 if ( isset( $_GET['action'], $_GET['plugin'] ) && $_GET['action'] == 'deactivate' && $_GET['plugin'] == $plugin) {
222 $active = 0;
223 } else {
224 $active = (int) is_plugin_active( $plugin );
225 }
226
227 $software_list[] = [
228 'sw_type' => 'plugin',
229 'sw_name' => $plugin_name,
230 'sw_cur_ver' => $plugin_version,
231 'sw_new_ver' => $new_version,
232 'sw_key' => $plugin,
233 'sw_active' => $active
234 ];
235 }
236 }
237
238 // Fetch list of themes.
239 $themes = wp_get_themes();
240 $themes_keys = array_keys( $themes );
241 $updatable_themes = get_theme_updates();
242
243 foreach ( $themes_keys as $theme_key ) {
244 $themes_data = $themes[ $theme_key ];
245 $theme_temporary = empty( $updatable_themes[ $theme_key ] ) ? '' : $updatable_themes[ $theme_key ];
246 $theme_new_version = empty( $updatable_themes[ $theme_key ] ) || ! isset( $theme_temporary->update, $theme_temporary->update['new_version'] ) ? '' : $theme_temporary->update['new_version'];
247 $theme_name = $themes_data->get( 'Name' );
248 $theme_version = $themes_data->get( 'Version' );
249
250 if ( ! empty( $theme_name ) && ! empty( $theme_version ) ) {
251 $software_list[] = [
252 'sw_type' => 'theme',
253 'sw_name' => $theme_name,
254 'sw_cur_ver' => $theme_version,
255 'sw_new_ver' => $theme_new_version,
256 'sw_key' => $theme_key,
257 ];
258 }
259 }
260
261 // Fetch WordPress version.
262 global $wp_version;
263 $core_updates = get_core_updates();
264 $new_wp_version = ( ! empty( $core_updates ) && $core_updates[0]->response == 'upgrade' ) ? $core_updates[0]->version : '';
265 $software_list[] = [
266 'sw_type' => 'wordpress',
267 'sw_name' => 'WordPress',
268 'sw_cur_ver' => $wp_version,
269 'sw_new_ver' => $new_wp_version,
270 ];
271
272 // Fetch PHP version.
273 $software_list[] = [
274 'sw_type' => 'php',
275 'sw_name' => 'PHP',
276 'sw_cur_ver' => phpversion(),
277 'sw_new_ver' => '',
278 ];
279
280 // Fetch database server version.
281 global $wpdb;
282 if ( ! is_null( $wpdb ) ) {
283 $software_list[] = [
284 'sw_type' => 'database',
285 'sw_name' => 'Database',
286 'sw_cur_ver' => $wpdb->get_var( 'SELECT VERSION()' ),
287 'sw_new_ver' => ''
288 ];
289 }
290
291 return $software_list;
292 }
293 }
294