PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.8
Patchstack – WordPress & Plugins Security v2.2.8
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.8, at includes/upload.php

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