PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.7.2
Defender Security – Malware Scanner, Login Security & Firewall v5.7.2
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-plugin-integrity.php

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

252 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles plugin integrity scan item.
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 WP_Defender\Traits\Plugin;
15 use WP_Defender\Traits\Formats;
16 use Calotes\Component\Behavior;
17 use WP_Defender\Model\Scan_Item;
18 use WP_Defender\Traits\File_Operations;
19 use WP_Defender\Component\Quarantine as Quarantine_Component;
20 use WP_Filesystem_Base;
21
22 /**
23 * Class Plugin_Integrity
24 * This class represents a plugin integrity behavior.
25 */
26 class Plugin_Integrity extends Behavior {
27
28 use Formats;
29 use IO;
30 use Plugin;
31 use File_Operations;
32
33 /**
34 * Return general data so we can output on frontend.
35 *
36 * @return array
37 */
38 public function to_array(): array {
39 $data = $this->owner->raw_data;
40 $file = $data['file'];
41 list ( $file_created_at, $file_size, $deleted ) = $this->get_file_meta( $file );
42
43 $is_quarantinable = $this->is_quarantinable( $this->owner->raw_data['file'] );
44
45 $quarantine_data = class_exists( 'WP_Defender\Component\Quarantine' ) ?
46 wd_di()->get( Quarantine_Component::class )->scan_item_data( $this->owner ) :
47 array( 'is_quarantinable' => $is_quarantinable );
48
49 return array_merge(
50 array(
51 'id' => $this->owner->id,
52 'type' => Scan_Item::TYPE_PLUGIN_CHECK,
53 'file_name' => pathinfo( $file, PATHINFO_BASENAME ),
54 'full_path' => $file,
55 'date_added' => $file_created_at,
56 'size' => $file_size,
57 'deleted' => $deleted,
58 'scenario' => $data['type'],
59 'short_desc' => $this->get_short_description(),
60 ),
61 $quarantine_data
62 );
63 }
64
65 /**
66 * We will get the origin code by looking into svn repo.
67 *
68 * @return false|string|WP_Error
69 */
70 private function get_origin_code() {
71 $file = $this->owner->raw_data['file'];
72
73 $directory_name = $this->get_plugin_directory_name( $file );
74 $plugin_header = $this->get_plugin_headers( $file );
75 $plugin_header = reset( $plugin_header );
76 $file_path = $this->get_plugin_relative_path( $file );
77
78 $source_file_url = $this->get_file_url(
79 $directory_name,
80 $plugin_header['Version'],
81 $file_path
82 );
83
84 return $this->get_url_content( $source_file_url );
85 }
86
87 /**
88 * Restore the file with its origin content.
89 *
90 * @return void|array|WP_Error
91 */
92 public function resolve() {
93 global $wp_filesystem;
94 // Initialize the WP filesystem, no more using 'file-put-contents' function.
95 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
96 require_once ABSPATH . '/wp-admin/includes/file.php';
97 WP_Filesystem();
98 }
99 $data = $this->owner->raw_data;
100 if ( 'modified' !== $data['type'] ) {
101 // Should not be here unless case changed.
102 return;
103 }
104
105 $origin = $this->get_origin_code();
106 if ( false === $origin || is_wp_error( $origin ) ) {
107 return;
108 }
109
110 $path = $data['file'];
111 $ret = $wp_filesystem->put_contents( $path, $origin );
112 if ( $ret ) {
113 $scan = Scan::get_last();
114 $scan->remove_issue( $this->owner->id );
115 $this->log( sprintf( '%s is deleted', $path ), \WP_Defender\Controller\Scan::SCAN_LOG );
116
117 do_action( 'wpdef_fixed_scan_issue', 'plugin_integrity', 'resolve' );
118
119 return array( 'message' => esc_html__( 'This item has been resolved.', 'defender-security' ) );
120 } else {
121 return new WP_Error(
122 'defender_permissions_denied',
123 esc_html__(
124 '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.',
125 'defender-security'
126 )
127 );
128 }
129 }
130
131 /**
132 * Ignore the suspicious file for the current scan.
133 *
134 * @return array An array with a message indicating successful ignore.
135 */
136 public function ignore(): array {
137 $scan = Scan::get_last();
138 $data = $this->owner->raw_data;
139 $issue_name = '<b>' . pathinfo( $data['file'], PATHINFO_BASENAME ) . '</b>';
140 $res = $scan->ignore_issue( $this->owner->id );
141 if ( ! $res ) {
142 return array(
143 'type_notice' => 'error',
144 'message' => sprintf(
145 /* translators: %s: Scan issue name. */
146 esc_html__( '%s is already marked as Ignored. It can’t be added to the Ignored list again.', 'defender-security' ),
147 $issue_name
148 ),
149 );
150 }
151
152 return array(
153 'message' => sprintf(
154 /* translators: %s: Scan issue name. */
155 esc_html__( 'You’ve successfully ignored the security issue related to %s.', 'defender-security' ),
156 $issue_name
157 ),
158 );
159 }
160
161 /**
162 * Allow the suspicious file for the current scan.
163 *
164 * @return array An array with a message indicating successful restoration.
165 */
166 public function unignore(): array {
167 $scan = Scan::get_last();
168 $scan->unignore_issue( $this->owner->id );
169
170 return array( 'message' => esc_html__( 'The suspicious file has been successfully restored.', 'defender-security' ) );
171 }
172
173 /**
174 * Delete the file or whole folder.
175 *
176 * @return array|WP_Error
177 */
178 public function delete() {
179 $data = $this->owner->raw_data;
180 $scan = Scan::get_last();
181 $file = $data['file'];
182 if ( ! file_exists( $file ) || ! is_readable( $file ) ) {
183 $scan->remove_issue( $this->owner->id );
184 $this->log(
185 sprintf( '%s is not readable and will be removed from the scan result', $file ),
186 \WP_Defender\Controller\Scan::SCAN_LOG
187 );
188
189 return array( 'message' => esc_html__( 'This item has been deleted.', 'defender-security' ) );
190 } elseif ( 'unversion' === $data['type'] && $this->delete_infected_file( $file ) ) {
191 return $this->after_delete( $file, $scan, 'plugin_integrity' );
192 } elseif ( 'dir' === $data['type'] && $this->delete_dir( $file ) ) {
193 return $this->after_delete( $file, $scan, 'plugin_integrity' );
194 }
195
196 return $this->get_permission_error( $file );
197 }
198
199 /**
200 * Return the source code depending on the type of the issue.
201 *
202 * @return array
203 */
204 public function pull_src(): array {
205 global $wp_filesystem;
206 // Initialize the WP filesystem, no more using 'file-put-contents' function.
207 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
208 require_once ABSPATH . '/wp-admin/includes/file.php';
209 WP_Filesystem();
210 }
211 $data = $this->owner->raw_data;
212 if ( ! file_exists( $data['file'] ) && ! is_dir( $data['file'] ) ) {
213 return array(
214 'code' => '',
215 'origin' => '',
216 );
217 }
218 switch ( $data['type'] ) {
219 case 'unversion':
220 return array( 'code' => $wp_filesystem->get_contents( $data['file'] ) );
221 case 'dir':
222 $dir_tree = new File( $data['file'], true, true, array(), array(), false );
223
224 return array( 'code' => implode( PHP_EOL, $dir_tree->get_dir_tree() ) );
225 case 'modified':
226 default:
227 return array(
228 'code' => $wp_filesystem->get_contents( $data['file'] ),
229 'origin' => $this->get_origin_code(),
230 );
231 }
232 }
233
234 /**
235 * Return a short description of the WordPress plugin file based on its type.
236 *
237 * @return string The short description of the file.
238 */
239 private function get_short_description(): string {
240 $data = $this->owner->raw_data;
241 if ( 'unversion' === $data['type'] ) {
242 return esc_html__( 'Unknown file in the WordPress plugin', 'defender-security' );
243 } elseif ( 'dir' === $data['type'] ) {
244 return esc_html__( 'This directory does not belong to the WordPress plugin', 'defender-security' );
245 } elseif ( 'modified' === $data['type'] ) {
246 return esc_html__( 'This plugin file appears modified', 'defender-security' );
247 }
248
249 return '';
250 }
251 }
252