PluginProbe
NinjaScanner – Virus & Malware scan / 3.3
NinjaScanner – Virus & Malware scan v3.3
3.3.1 trunk 3.0 3.0.1 3.0.10 3.0.11 3.0.12 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1 3.2 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 All 26 releases
ninjascanner / lib / utils.php

utils.php in NinjaScanner – Virus & Malware scan 3.3, at lib/utils.php

508 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 +=====================================================================+
4 | _ _ _ _ ____ |
5 | | \ | (_)_ __ (_) __ _/ ___| ___ __ _ _ __ _ __ ___ _ __ |
6 | | \| | | '_ \ | |/ _` \___ \ / __/ _` | '_ \| '_ \ / _ \ '__| |
7 | | |\ | | | | || | (_| |___) | (_| (_| | | | | | | | __/ | |
8 | |_| \_|_|_| |_|/ |\__,_|____/ \___\__,_|_| |_|_| |_|\___|_| |
9 | |__/ |
10 | |
11 | (c) NinTechNet ~ https://nintechnet.com/ |
12 +=====================================================================+
13 */
14
15 if (! defined( 'ABSPATH' ) ) { die( 'Forbidden' ); }
16
17 // =====================================================================
18 // We don't want to be bothered by other themes/plugins admin notices.
19
20 add_action('admin_head', 'nscan_hide_admin_notices');
21
22 function nscan_hide_admin_notices() {
23 if ( isset( $_GET['page'] ) && $_GET['page'] == 'NinjaScanner' ) {
24 remove_all_actions('admin_notices');
25 remove_all_actions('all_admin_notices');
26 }
27 }
28
29 // ===================================================================== 2023-06-07
30 // Clean-up the scan temp files.
31
32 function nscan_cleanup_tempfiles() {
33
34 global $nscan_temp_files;
35
36 foreach( $nscan_temp_files as $file ) {
37 if ( file_exists( $file ) ) {
38 unlink( $file );
39 }
40 }
41 }
42
43 // ===================================================================== 2023-06-07
44 // Disable PHP display_errors so that notice, warning and error messages
45 // don't show up in the AJAX response.
46
47 function nscan_hide_errors() {
48
49 ini_set('display_errors', 0 );
50 }
51
52 // ===================================================================== 2023-06-07
53 // Recursively delete all files and directories. Used to delete
54 // extracted ZIP files (plugins and themes) in the cache folder
55 // after file integrity check:
56
57 function nscan_remove_dir( $dir ) {
58
59 // Play safe: make sure that whatever we delete,
60 // it's located inside our cache folder:
61 $dir = realpath( $dir );
62 if ( strpos( $dir, NSCAN_CACHEDIR ) === false ) {
63 nscan_log_error( sprintf(
64 __('Directory path does not match NSCAN_CACHEDIR: %s',
65 'ninjascanner'),
66 $dir
67 ));
68 }
69
70 if ( is_dir( $dir ) ) {
71 $files = scandir( $dir );
72 foreach ( $files as $file ) {
73 if ( $file == '.' || $file == '..') {
74 continue;
75 }
76 if ( is_dir("$dir/$file" ) ) {
77 nscan_remove_dir( "$dir/$file");
78 } else {
79 unlink("$dir/$file");
80 }
81 }
82 rmdir( $dir );
83 }
84 }
85
86 // ===================================================================== 2023-06-07
87 // Read file content from the ZIP file.
88
89 function nscan_read_zipped_file( $zip, $file ) {
90
91 // By default we use ZipArchive, but if it's not available,
92 // we fall back to the built-in PclZip library:
93 if ( class_exists('ZipArchive') ) {
94 return file_get_contents("zip://{$zip}#{$file}");
95
96 } else {
97 // PclZip
98 require_once ABSPATH .'wp-admin/includes/class-pclzip.php';
99 $extract = new PclZip( $zip );
100 if ( $extract->extract( NSCAN_CACHEDIR .'/tmp') !== 0 ) {
101 $content = file_get_contents( NSCAN_CACHEDIR ."/tmp/$file");
102 nscan_remove_dir( NSCAN_CACHEDIR .'/tmp');
103 return $content;
104 }
105 }
106 }
107 // =====================================================================
108 // Retrieve the current scan's status
109 // (error|success|notfound|cancelled|stopped).
110
111 function nscan_get_lock_status() {
112
113 global $nscan_steps;
114
115 $lock_status = array(
116 'current_step' => 0,
117 'status' => 'error',
118 'message' => __('Unknown error.', 'ninjascanner'),
119 'last' => '',
120 'total_steps' => count( $nscan_steps )
121 );
122
123 if ( file_exists( NSCAN_CANCEL ) ) {
124 $lock_status['message'] = __('Scan was cancelled.', 'ninjascanner');
125 $lock_status['status'] = 'cancelled';
126 }
127
128 if (! file_exists( NSCAN_LOCKFILE ) ) {
129 $lock_status['message'] = __('Missing lock file.', 'ninjascanner');
130 $lock_status['status'] = 'notfound';
131 return $lock_status;
132 }
133
134 $status = json_decode( file_get_contents( NSCAN_LOCKFILE ), true );
135
136 if (! empty( $status['current_step'] ) ) {
137 $lock_status['current_step'] = (int) $status['current_step'];
138 }
139 if (! empty( $status['status'] ) ) {
140 $lock_status['status'] = $status['status'];
141 }
142 if (! empty( $status['message'] ) ) {
143 $lock_status['message'] = $status['message'];
144 }
145 if (! empty( $status['last'] ) ) {
146 $lock_status['last'] = $status['last'];
147 }
148
149 return $lock_status;
150 }
151
152 // ===================================================================== 2023-06-07
153 // Set the current scan's status
154 // (error|success|notfound|cancelled|stopped).
155
156 function nscan_set_lock_status( $step, $status, $message, $last = '') {
157
158 global $nscan_steps;
159
160 $lock_status = array(
161 'current_step' => $step,
162 'status' => $status,
163 'message' => $message,
164 'last' => $last,
165 'total_steps' => count( $nscan_steps )
166 );
167
168 file_put_contents( NSCAN_LOCKFILE, json_encode( $lock_status ) );
169 }
170
171 // ===================================================================== 2023-06-07
172 // Stop the scanning process.
173
174 function nscan_stop_scan() {
175
176 nscan_cleanup_tempfiles();
177
178 exit( json_encode( ['status' => 'success'] ) );
179 }
180
181 // ===================================================================== 2023-06-07
182 // Cancel a running scan.
183
184 function nscan_cancel_scan() {
185
186 if ( empty( $_POST['message'] ) ) {
187 $_POST['message'] = '';
188 }
189 nscan_log_info(
190 sprintf(
191 __('Cancelling scanning process (%s)', 'ninjascanner'),
192 $_POST['message']
193 ), false
194 );
195
196 touch( NSCAN_CANCEL );
197 if ( file_exists( NSCAN_LOCKFILE ) ) {
198 unlink( NSCAN_LOCKFILE );
199 }
200
201 wp_send_json( [
202 'status' => 'success',
203 'message' => __('Scan cancelled', 'ninjascanner')
204 ] );
205 }
206
207 // ===================================================================== 2023-06-07
208 // Check if a scan is running.
209
210 function nscan_is_scan_running() {
211
212 return json_encode( nscan_get_lock_status() );
213 }
214
215 // ===================================================================== 2023-06-07
216 // Check if a scan process was cancelled.
217
218 function nscan_is_scan_cancelled() {
219
220 if ( file_exists( NSCAN_CANCEL ) ) {
221 nscan_log_error( __('Scan was cancelled.', 'ninjascanner') );
222 exit;
223 }
224 }
225
226 // ===================================================================== 2023-06-07
227 // Write message to the log. Log level can be a combination of INFO (1),
228 // WARN (2), ERROR (4) and DEBUG (8) and can be adjusted while viewing
229 // the log. Check also if the scanning process was cancelled (missing
230 // lock file) and exit.
231
232 function nscan_log( $string, $level = 1, $exit = true ) {
233
234 if ( $exit == true ) {
235 $lock_status = nscan_get_lock_status();
236 if ( in_array( $lock_status['status'], ['notfound', 'cancelled'] ) ) {
237 file_put_contents(
238 NSCAN_DEBUGLOG,
239 time() . "~~8~~{$lock_status['message']}\n",
240 FILE_APPEND
241 );
242 nscan_stop_scan();
243 }
244 }
245 file_put_contents(
246 NSCAN_DEBUGLOG,
247 time() ."~~$level~~$string\n",
248 FILE_APPEND
249 );
250 }
251
252 function nscan_log_info( $string, $exit = true ) {
253 nscan_log( $string, 1, $exit );
254 }
255 function nscan_log_warn( $string, $exit = true ) {
256 nscan_log( $string, 2, $exit );
257 }
258 function nscan_log_error( $string, $exit = true ) {
259 nscan_log( $string, 4, $exit );
260 }
261 function nscan_log_debug( $string, $exit = true ) {
262 nscan_log( $string, 8, $exit );
263 }
264
265 // ===================================================================== 2023-06-07
266 // Generate a nonce key.
267
268 function nscan_generate_key() {
269
270 $key = bin2hex( openssl_random_pseudo_bytes(40) );
271 set_transient(
272 'nscan_ajax_start',
273 hash('sha256', $key ),
274 60 * NSCAN_KEYTIMEOUT
275 );
276 return $key;
277 }
278
279 // ===================================================================== 2023-06-07
280 // Verify nonce for on-demand scan.
281
282 function nscan_check_nonce() {
283
284 if ( empty( $_POST['nscan_key'] ) ||
285 ! wp_verify_nonce( $_POST['nscan_key'], 'nscan_on_demand_nonce') ) {
286
287 $return['status'] = 'error';
288 $return['message'] = __('Security nonces do not match.', 'ninjascanner');
289 nscan_log_error( $return['message'], false );
290 nscan_set_lock_status(
291 1,
292 $return['status'],
293 $return['message'],
294 null
295 );
296 wp_send_json( $return );
297 }
298 }
299 // =====================================================================
300 // Make sure we have a Linux or Windows absolute path.
301
302 function ns_win_or_linux( $file ) {
303
304 if (! preg_match( '`^(?i:[a-z]:|/)`', $file ) || preg_match( '`\.\.\B`', $file ) ) {
305 wp_die( sprintf(
306 __('File does not seem valid: %s', 'ninjascanner' ),
307 htmlentities( $file )
308 ) );
309 }
310 }
311
312 // =====================================================================
313 // Verify the security key.
314
315 function nscan_check_key() {
316
317 $success = array(
318 'status' => 'success',
319 'message' => __('Keys match.', 'ninjascanner')
320 );
321 $error = array(
322 'status' => 'error'
323 );
324 $error_msg = __('Security keys do not match (#%s). Try to reload this page.', 'ninjascanner');
325
326 if ( empty( $_POST['nscan_key'] ) ) {
327 $error['message'] = sprintf( $error_msg, 1 );
328 return $error;
329 }
330
331 $key = get_transient( 'nscan_ajax_start' );
332 if ( $key === false ) {
333 $error['message'] = sprintf( $error_msg, 2 );
334 return $error;
335 }
336
337 if ( hash( 'sha256', $_POST['nscan_key'] ) !== $key ) {
338 delete_transient( 'nscan_ajax_start' );
339 $error['message'] = sprintf( $error_msg, 3 );
340 return $error;
341 }
342
343 return $success;
344 }
345
346 // =====================================================================
347 // Get the blog timezone.
348
349 function nscan_get_blogtimezone() {
350
351 $tzstring = get_option( 'timezone_string' );
352 if (! $tzstring ) {
353 $tzstring = ini_get( 'date.timezone' );
354 if (! $tzstring ) {
355 $tzstring = 'UTC';
356 }
357 }
358 date_default_timezone_set( $tzstring );
359 }
360
361 // =====================================================================
362
363 function nscan_is_valid() {
364
365 $nscan_options = get_option( 'nscan_options' );
366 nscan_get_blogtimezone();
367 if ( empty( $nscan_options['key'] ) ) { return -1; }
368 if (! empty( $nscan_options['exp'] ) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $nscan_options['exp'] ) ) {
369 if ( $nscan_options['exp'] < date( 'Y-m-d', strtotime( '-1 day' ) ) ) {
370 return -1;
371 } elseif ( $nscan_options['exp'] < date( 'Y-m-d', strtotime( '+30 day' ) ) ) {
372 return 30;
373 }
374 return 1;
375 }
376 return 0;
377 }
378
379 // =====================================================================
380
381 function nscan_check_license( $nscan_options, $key = '' ) {
382
383 if ( is_multisite() ) {
384 $site_url = rtrim( strtolower( network_site_url('','http') ), '/' );
385 } else {
386 $site_url = rtrim( strtolower(site_url('','http') ), '/' );
387 }
388
389 global $wp_version;
390 $opt_update = 0;
391 $res = array();
392
393 if ( empty( $key ) && ! empty( $nscan_options['key'] ) ) {
394 $key = $nscan_options['key'];
395 }
396
397 if ( empty( $key ) ) {
398 $res['nscan_err'] = __('Error: You do not have a Premium license.', 'ninjascanner');
399 return $res;
400 }
401
402 $request_string = array(
403 'body' => array(
404 'action' => 'check_license',
405 'key' => $key,
406 'cache_id' => sha1( home_url() ),
407 'host' => @strtolower( $_SERVER['HTTP_HOST'] )
408 ),
409 'user-agent' => 'Mozilla/5.0 (compatible; NinjaScanner/'. NSCAN_VERSION ."; WordPress/{$wp_version})",
410 'timeout' => NSCAN_CURL_TIMEOUT,
411 'httpversion' => '1.1' ,
412 'sslverify' => true
413 );
414 // POST the request:
415 $res = wp_remote_post( NSCAN_SIGNATURES_URL, $request_string );
416
417 if (! is_wp_error($res) ) {
418
419 if ( $res['response']['code'] == 200 ) {
420
421 // Fetch the array:
422 $data = json_decode( $res['body'], true );
423 // Verify its content:
424 if ( empty( $data['checked'] ) ) {
425 $res['nscan_err'] = __('An unknown error occurred while connecting to NinjaScanner API server. Please try again in a few minutes.', 'ninjascanner');
426 return $res;
427 }
428 if (! empty( $data['exp'] ) ) {
429 $nscan_options['exp'] = $data['exp'];
430 $res['nscan_exp'] = $data['exp'];
431 update_option( 'nscan_options', $nscan_options );
432 }
433
434 if (! empty( $data['err'] ) ) {
435 $res['nscan_err'] = sprintf(
436 __('Error: Your license is not valid (#%s).', 'ninjascanner'),
437 (int)$data['err']
438 );
439 return $res;
440 }
441
442 $res['nscan_msg'] = __('You have a valid license', 'ninjascanner');
443 return $res;
444
445 } else {
446 // HTTP error:
447 $res['nscan_err'] = sprintf(
448 __('HTTP Error (%s): Cannot connect to the API server. Try again later', 'ninjascanner'),
449 (int)$res['response']['code']
450 );
451 return $res;
452 }
453 } else {
454 // Unknown error:
455 $res['nscan_err'] = __('Error: Cannot connect to the API server. Try again later', 'ninjascanner');
456 return $res;
457 }
458 }
459
460 // =====================================================================
461
462 function nscan_save_license( $nscan_options ) {
463
464 $res = array();
465 $key = trim( $_POST['key'] );
466 $res = nscan_check_license( $nscan_options, $key );
467 if ( empty( $res['nscan_err'] ) ) {
468 $nscan_options['key'] = $key;
469 $nscan_options['exp'] = $res['nscan_exp'];
470 update_option( 'nscan_options', $nscan_options );
471 $res['nscan_msg'] = __('Your license has been accepted and saved.', 'ninjascanner');
472 }
473 return $res;
474
475 }
476 // =====================================================================
477 // Send an email to the admin if there were an error.
478
479 function nscan_error_email( $error ) {
480
481 $nscan_options = get_option( 'nscan_options' );
482 if ( empty( $nscan_options['admin_email'] ) ) {
483 return;
484 }
485
486 $message = sprintf(
487 __('Cannot start the scan! More details may be available in the scanner log: %s', 'ninjascanner',
488 $error
489 ) );
490
491 if ( is_multisite() ) {
492 $blog = network_home_url('/');
493 } else {
494 $blog = home_url('/');
495 }
496 $subject = __('[NinjaScanner] Scan error', 'ninjascanner');
497 $message = sprintf( __('A fatal error occurred while running NinjaScanner: %s.', 'ninjascanner'), $error );
498 $message .= "\n\n". __('More details may be available in the scanner log.', 'ninjascanner' ) ."\n";
499 $signature = "\nNinjaScanner - https://nintechnet.com/\n" .
500 __('Help Desk (Premium customers only):', 'ninjascanner') . " https://secure.nintechnet.com/login/\n";
501 wp_mail( $nscan_options['admin_email'], $subject, $message . $signature );
502
503 }
504
505
506 // =====================================================================
507 // EOF
508