PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.5.0
Defender Security – Malware Scanner, Login Security & Firewall v5.5.0
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-item / class-core-integrity.php

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

234 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles core integrity scan.
4 *
5 * @package WP_Defender\Behavior\Scan_Item
6 */
7
8 namespace WP_Defender\Behavior\Scan_Item;
9
10 use WP_Error;
11 use Calotes\Base\File;
12 use WP_Defender\Traits\IO;
13 use WP_Defender\Model\Scan;
14 use Calotes\Component\Behavior;
15 use WP_Defender\Traits\Formats;
16 use WP_Defender\Model\Scan_Item;
17 use WP_Defender\Traits\File_Operations;
18 use WP_Defender\Controller\Scan as Scan_Controller;
19
20 /**
21 * This class represents a behavior related to core integrity in the WP_Defender plugin.
22 */
23 class Core_Integrity extends Behavior {
24
25 use Formats;
26 use IO;
27 use File_Operations;
28
29 /**
30 * Return general data so we can output on frontend.
31 *
32 * @return array
33 */
34 public function to_array(): array {
35 $data = $this->owner->raw_data;
36 $file = $data['file'];
37
38 list( $file_created_at, $file_size, $deleted ) = $this->get_file_meta( $file );
39
40 return array(
41 'id' => $this->owner->id,
42 'type' => Scan_Item::TYPE_INTEGRITY,
43 'file_name' => pathinfo( $file, PATHINFO_BASENAME ),
44 'full_path' => $file,
45 'date_added' => $file_created_at,
46 'size' => $file_size,
47 'scenario' => $data['type'],
48 'deleted' => $deleted,
49 'short_desc' => $this->get_short_description(),
50 );
51 }
52
53 /**
54 * We will get the origin code by looking into svn repo.
55 *
56 * @return false|string|WP_Error
57 */
58 private function get_origin_code() {
59 global $wp_filesystem;
60 // Initialize the WP filesystem, no more using 'file-put-contents' function.
61 if ( empty( $wp_filesystem ) ) {
62 require_once ABSPATH . '/wp-admin/includes/file.php';
63 WP_Filesystem();
64 }
65 global $wp_version;
66 $data = $this->owner->raw_data;
67 $file = wp_normalize_path( $data['file'] );
68 $relative_path = str_replace( wp_normalize_path( ABSPATH ), '', $file );
69 $source_file_url = "http://core.svn.wordpress.org/tags/$wp_version/" . $relative_path;
70 $ds = DIRECTORY_SEPARATOR;
71 if ( ! function_exists( 'download_url' ) ) {
72 require_once ABSPATH . 'wp-admin' . $ds . 'includes' . $ds . 'file.php';
73 }
74 $tmp = download_url( $source_file_url );
75 if ( is_wp_error( $tmp ) ) {
76 return $tmp;
77 }
78 $content = $wp_filesystem->get_contents( $tmp );
79 wp_delete_file( $tmp );
80
81 return $content;
82 }
83
84 /**
85 * Restore the file with its origin content.
86 *
87 * @return void|array|WP_Error
88 */
89 public function resolve() {
90 global $wp_filesystem;
91 // Initialize the WP filesystem, no more using 'file-put-contents' function.
92 if ( empty( $wp_filesystem ) ) {
93 include_once ABSPATH . '/wp-admin/includes/file.php';
94 WP_Filesystem();
95 }
96 $data = $this->owner->raw_data;
97 if ( 'modified' !== $data['type'] ) {
98 // Should not be here unless case changed.
99 return;
100 }
101
102 $origin = $this->get_origin_code();
103 if ( false === $origin || is_wp_error( $origin ) ) {
104 return;
105 }
106
107 $path = $data['file'];
108 $ret = $wp_filesystem->put_contents( $path, $origin );
109 if ( $ret ) {
110 $scan = Scan::get_last();
111 $scan->remove_issue( $this->owner->id );
112 $this->log( sprintf( '%s is deleted', $path ), Scan_Controller::SCAN_LOG );
113
114 do_action( 'wpdef_fixed_scan_issue', 'core_integrity', 'resolve' );
115
116 return array( 'message' => esc_html__( 'This item has been resolved.', 'defender-security' ) );
117 } else {
118 return new WP_Error(
119 'defender_permissions_denied',
120 esc_html__(
121 'Permissions Denied. Defender does not have the needed permissions to edit the file. Please change file permissions to 640 or contact your hosting provider so they could change them for you.',
122 'defender-security'
123 )
124 );
125 }
126 }
127
128 /**
129 * Ignore the suspicious file.
130 *
131 * @return array
132 */
133 public function ignore(): array {
134 $scan = Scan::get_last();
135 $scan->ignore_issue( $this->owner->id );
136
137 return array( 'message' => esc_html__( 'The suspicious file has been successfully ignored.', 'defender-security' ) );
138 }
139
140 /**
141 * Restore the ignored suspicious file.
142 *
143 * @return array
144 */
145 public function unignore(): array {
146 $scan = Scan::get_last();
147 $scan->unignore_issue( $this->owner->id );
148
149 return array( 'message' => esc_html__( 'The suspicious file has been successfully restored.', 'defender-security' ) );
150 }
151
152 /**
153 * Delete the file or whole folder.
154 *
155 * @return array|WP_Error
156 */
157 public function delete() {
158 $data = $this->owner->raw_data;
159 $scan = Scan::get_last();
160
161 // Check if the file exists and is readable. If not, remove it from the scan result and log the result.
162 $file = $data['file'];
163 if ( ! file_exists( $file ) || ! is_readable( $file ) ) {
164 $scan->remove_issue( $this->owner->id );
165 $this->log(
166 sprintf( '%s is not readable and will be removed from the scan result', $file ),
167 Scan_Controller::SCAN_LOG
168 );
169
170 return array( 'message' => esc_html__( 'This item has been deleted.', 'defender-security' ) );
171 } elseif ( 'unversion' === $data['type'] && $this->delete_infected_file( $file ) ) {
172 return $this->after_delete( $file, $scan, 'core_integrity' );
173 } elseif ( 'dir' === $data['type'] && $this->delete_dir( $file ) ) {
174 return $this->after_delete( $file, $scan, 'core_integrity' );
175 }
176
177 return $this->get_permission_error( $file );
178 }
179
180 /**
181 * Return the source code depending on the type of the issue:
182 * If it is unversion, return full source,
183 * if it is dir, we return a list of files,
184 * if it is modified, we will return the current code & origin.
185 *
186 * @return array
187 */
188 public function pull_src(): array {
189 global $wp_filesystem;
190 // Initialize the WP filesystem, no more using 'file-put-contents' function.
191 if ( empty( $wp_filesystem ) ) {
192 include_once ABSPATH . '/wp-admin/includes/file.php';
193 WP_Filesystem();
194 }
195 $data = $this->owner->raw_data;
196 if ( ! file_exists( $data['file'] ) && ! is_dir( $data['file'] ) ) {
197 return array(
198 'code' => '',
199 'origin' => '',
200 );
201 }
202 switch ( $data['type'] ) {
203 case 'unversion':
204 return array( 'code' => $wp_filesystem->get_contents( $data['file'] ) );
205 case 'dir':
206 $dir_tree = new File( $data['file'], true, true, array(), array(), false );
207
208 return array( 'code' => implode( PHP_EOL, $dir_tree->get_dir_tree() ) );
209 case 'modified':
210 default:
211 return array(
212 'code' => $wp_filesystem->get_contents( $data['file'] ),
213 'origin' => $this->get_origin_code(),
214 );
215 }
216 }
217
218 /**
219 * Retrieves a short description based on the type of data.
220 *
221 * @return string The short description.
222 */
223 private function get_short_description(): string {
224 $data = $this->owner->raw_data;
225 if ( 'unversion' === $data['type'] ) {
226 return esc_html__( 'Unknown file in WordPress core', 'defender-security' );
227 } elseif ( 'dir' === $data['type'] ) {
228 return esc_html__( 'This directory does not belong to WordPress core', 'defender-security' );
229 } elseif ( 'modified' === $data['type'] ) {
230 return esc_html__( 'This WordPress core file appears modified', 'defender-security' );
231 }
232 }
233 }
234