PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.0.1
Defender Security – Malware Scanner, Login Security & Firewall v3.0.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / behavior / scan / core-integrity.php

core-integrity.php in Defender Security – Malware Scanner, Login Security & Firewall 3.0.1, at src/behavior/scan/core-integrity.php

175 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WP_Defender\Behavior\Scan;
4
5 use Calotes\Component\Behavior;
6 use WP_Defender\Component\Timer;
7 use WP_Defender\Model\Scan;
8 use WP_Defender\Model\Scan_Item;
9 use WP_Defender\Traits\IO;
10
11 class Core_Integrity extends Behavior {
12 use IO;
13
14 public const CACHE_CHECKSUMS = 'wd_cache_checksums';
15
16 /**
17 * Check that the folder is empty.
18 * @param string $path
19 *
20 * @return bool
21 */
22 protected function is_dir_empty( $path ) {
23 $rfiles = scandir( $path );
24 foreach ( $rfiles as $rfile ) {
25 if ( ! in_array( $rfile, array( '.', '..' ), true ) ) {
26 return false;
27 }
28 }
29
30 return true;
31 }
32
33 /**
34 * Check if the core file is on touch.
35 */
36 public function core_integrity_check() {
37 $core_files = get_site_option( Gather_Fact::CACHE_CORE, array() );
38 $core_files = new \ArrayIterator( $core_files );
39 $checksums = $this->get_checksum();
40 $timer = new Timer();
41 $model = $this->owner->scan;
42 $pos = (int) $model->task_checkpoint;
43 $core_files->seek( $pos );
44 $this->log( sprintf( 'current pos %s', $pos ), 'scan.log' );
45 while ( $core_files->valid() ) {
46 if ( ! $timer->check() ) {
47 $this->log( 'break out cause too long', 'scan.log' );
48 break;
49 }
50
51 if ( $model->is_issue_whitelisted( $core_files->current() ) ) {
52 // This is whitelisted, so do nothing.
53 $this->log( sprintf( 'skip %s because of file is whitelisted', $core_files->current() ), 'scan.log' );
54 $core_files->next();
55 continue;
56 }
57
58 if ( $model->is_issue_ignored( $core_files->current() ) ) {
59 // This is ignored, so do nothing.
60 $this->log( sprintf( 'skip %s because of file is ignored', $core_files->current() ), 'scan.log' );
61 $core_files->next();
62 continue;
63 }
64
65 // The file will be '\' instead of '/' on Windows OS, so we need to convert everything to '/'.
66 $file = $core_files->current();
67 // Get relative so we can compare.
68 $abs_path = ABSPATH;
69 if ( defender_is_windows() ) {
70 // This mean we are on Windows.
71 $abs_path = str_replace( '/', DIRECTORY_SEPARATOR, $abs_path );
72 }
73 $rev_file = str_replace( $abs_path, '', $file );
74 // Remove the first '\' on Windows.
75 $rev_file = str_replace( DIRECTORY_SEPARATOR, '/', $rev_file );
76 if ( isset( $checksums[ $rev_file ] ) ) {
77 if ( ! $this->compare_hashes( $file, $checksums[ $rev_file ] ) ) {
78 $this->log( sprintf( 'modified %s', $file ), 'scan.log' );
79 $model->add_item(
80 Scan_Item::TYPE_INTEGRITY,
81 array(
82 'file' => $file,
83 'type' => 'modified',
84 )
85 );
86 }
87 } else {
88 if ( is_dir( $file ) ) {
89 if ( $this->is_dir_empty( $core_files->current() ) ) {
90 $this->log(
91 sprintf( 'skip %s because of non-WP directory is empty', $core_files->current() ),
92 'scan.log'
93 );
94 $core_files->next();
95 continue;
96 }
97 $item_type = 'dir';
98 } else {
99 $item_type = 'unversion';
100 }
101
102 $model->add_item(
103 Scan_Item::TYPE_INTEGRITY,
104 array(
105 'file' => $file,
106 'type' => $item_type,
107 )
108 );
109 }
110 $model->calculate_percent( $core_files->key() * 100 / $core_files->count(), 2 );
111 if ( 0 === $core_files->key() % 100 ) {
112 // We should update the model percent each 100 files, so we have some progress ont he screen$pos * 100 / $core_files->count().
113 $model->save();
114 }
115 $core_files->next();
116 }
117 if ( $core_files->valid() ) {
118 // Save the current progress and quit.
119 $model->task_checkpoint = $core_files->key();
120 } else {
121 // We will check if we have any ignore issue from last scan, so we can bring it here.
122 $last = Scan::get_last();
123 if ( is_object( $last ) ) {
124 $ignored_issues = $last->get_issues( Scan_Item::TYPE_INTEGRITY, Scan_Item::STATUS_IGNORE );
125 foreach ( $ignored_issues as $issue ) {
126 $model->add_item( Scan_Item::TYPE_INTEGRITY, $issue->raw_data, Scan_Item::STATUS_IGNORE );
127 }
128 }
129 //Done, reset this, so we can use it later.
130 $model->task_checkpoint = null;
131 }
132 $model->save();
133
134 return ! $core_files->valid();
135 }
136
137 /**
138 * Fetch the checksums.
139 *
140 * @return bool|array
141 */
142 protected function get_checksum() {
143 $cache = get_site_option( self::CACHE_CHECKSUMS, false );
144 if ( is_array( $cache ) ) {
145
146 return $cache;
147 }
148 $this->log( 'Fetch checksums, should only once', 'scan.log' );
149
150 global $wp_version, $wp_local_package;
151
152 if ( ! function_exists( 'get_core_checksums' ) ) {
153 include_once ABSPATH . 'wp-admin/includes/update.php';
154 }
155 $checksums = get_core_checksums( $wp_version, empty( $wp_local_package ) ? 'en_US' : $wp_local_package );
156 if ( false === $checksums ) {
157 $this->log( 'Error from fetching checksums from wp.org', 'scan.log' );
158 $scan = $this->owner->scan;
159 $scan->status = Scan::STATUS_ERROR;
160
161 return false;
162 }
163 if ( isset( $checksums[ $wp_version ] ) ) {
164 /**
165 * Sometimes the API returns the format [$wp_version]=>'...' when locale not right.
166 * Use this as fail-safe.
167 */
168 $checksums = $wp_version[ $checksums ];
169 }
170 update_site_option( self::CACHE_CHECKSUMS, $checksums );
171
172 return $checksums;
173 }
174 }
175