PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.2.4
Defender Security – Malware Scanner, Login Security & Firewall v6.2.4
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 6.2.4, at src/behavior/scan-item/class-plugin-integrity.php

245 lines 7.0 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_Filesystem_Base;
20
21 /**
22 * Class Plugin_Integrity
23 * This class represents a plugin integrity behavior.
24 */
25 class Plugin_Integrity extends Behavior {
26
27 use Formats;
28 use IO;
29 use Plugin;
30 use File_Operations;
31
32 /**
33 * Return general data so we can output on frontend.
34 *
35 * @return array
36 */
37 public function to_array(): array {
38 $data = $this->owner->raw_data;
39 $file = $data['file'];
40 list( $file_created_at, $file_size, $deleted ) = $this->get_file_meta( $file );
41
42 if ( class_exists( 'WP_Defender\Component\Quarantine' ) ) {
43 $quarantine_data = wd_di()->get( \WP_Defender\Component\Quarantine::class )->scan_item_data( $this->owner );
44 }
45
46 return array_merge(
47 array(
48 'id' => $this->owner->id,
49 'type' => Scan_Item::TYPE_PLUGIN_CHECK,
50 'file_name' => pathinfo( $file, PATHINFO_BASENAME ),
51 'full_path' => $file,
52 'date_added' => $file_created_at,
53 'size' => $file_size,
54 'deleted' => $deleted,
55 'scenario' => $data['type'],
56 'short_desc' => $this->get_short_description(),
57 ),
58 $quarantine_data
59 );
60 }
61
62 /**
63 * We will get the origin code by looking into svn repo.
64 *
65 * @return false|string|WP_Error
66 */
67 private function get_origin_code() {
68 $file = $this->owner->raw_data['file'];
69
70 $directory_name = $this->get_plugin_directory_name( $file );
71 $plugin_header = $this->get_plugin_headers( $file );
72 $plugin_header = reset( $plugin_header );
73 $file_path = $this->get_plugin_relative_path( $file );
74
75 $source_file_url = $this->get_file_url(
76 $directory_name,
77 $plugin_header['Version'],
78 $file_path
79 );
80
81 return $this->get_url_content( $source_file_url );
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 ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
93 require_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 ), \WP_Defender\Controller\Scan::SCAN_LOG );
113
114 do_action( 'wpdef_fixed_scan_issue', 'plugin_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 for the current scan.
130 *
131 * @return array An array with a message indicating successful ignore.
132 */
133 public function ignore(): array {
134 $scan = Scan::get_last();
135 $data = $this->owner->raw_data;
136 $issue_name = '<b>' . pathinfo( $data['file'], PATHINFO_BASENAME ) . '</b>';
137 $res = $scan->ignore_issue( $this->owner->id );
138 if ( ! $res ) {
139 return array(
140 'type_notice' => 'error',
141 'message' => $this->get_failed_ignore_result( $issue_name ),
142 );
143 }
144
145 return array(
146 'message' => sprintf(
147 /* translators: %s: Scan issue name. */
148 esc_html__( 'You’ve successfully ignored the security issue related to %s.', 'defender-security' ),
149 $issue_name
150 ),
151 );
152 }
153
154 /**
155 * Allow the suspicious file for the current scan.
156 *
157 * @return array An array with a message indicating successful restoration.
158 */
159 public function unignore(): array {
160 $scan = Scan::get_last();
161 $scan->unignore_issue( $this->owner->id );
162
163 return array( 'message' => esc_html__( 'The suspicious file has been successfully restored.', 'defender-security' ) );
164 }
165
166 /**
167 * Delete the file or whole folder.
168 *
169 * @return array|WP_Error
170 */
171 public function delete() {
172 $data = $this->owner->raw_data;
173 $scan = Scan::get_last();
174 $file = $data['file'];
175 if ( ! file_exists( $file ) || ! is_readable( $file ) ) {
176 $scan->remove_issue( $this->owner->id );
177 $this->log(
178 sprintf( '%s is not readable and will be removed from the scan result', $file ),
179 \WP_Defender\Controller\Scan::SCAN_LOG
180 );
181
182 return array( 'message' => esc_html__( 'This item has been deleted.', 'defender-security' ) );
183 } elseif ( 'unversion' === $data['type'] && $this->delete_infected_file( $file ) ) {
184 return $this->after_delete( $file, $scan, 'plugin_integrity' );
185 } elseif ( 'dir' === $data['type'] && $this->delete_dir( $file ) ) {
186 return $this->after_delete( $file, $scan, 'plugin_integrity' );
187 }
188
189 return $this->get_permission_error( $file );
190 }
191
192 /**
193 * Return the source code depending on the type of the issue.
194 *
195 * @return array
196 */
197 public function pull_src(): array {
198 global $wp_filesystem;
199 // Initialize the WP filesystem, no more using 'file-put-contents' function.
200 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
201 require_once ABSPATH . '/wp-admin/includes/file.php';
202 WP_Filesystem();
203 }
204 $data = $this->owner->raw_data;
205 if ( ! file_exists( $data['file'] ) && ! is_dir( $data['file'] ) ) {
206 return array(
207 'code' => '',
208 'origin' => '',
209 );
210 }
211 switch ( $data['type'] ) {
212 case 'unversion':
213 return array( 'code' => $wp_filesystem->get_contents( $data['file'] ) );
214 case 'dir':
215 $dir_tree = new File( $data['file'], true, true, array(), array(), false );
216
217 return array( 'code' => implode( PHP_EOL, $dir_tree->get_dir_tree() ) );
218 case 'modified':
219 default:
220 return array(
221 'code' => $wp_filesystem->get_contents( $data['file'] ),
222 'origin' => $this->get_origin_code(),
223 );
224 }
225 }
226
227 /**
228 * Return a short description of the WordPress plugin file based on its type.
229 *
230 * @return string The short description of the file.
231 */
232 private function get_short_description(): string {
233 $data = $this->owner->raw_data;
234 if ( 'unversion' === $data['type'] ) {
235 return esc_html__( 'Unknown file in the WordPress plugin', 'defender-security' );
236 } elseif ( 'dir' === $data['type'] ) {
237 return esc_html__( 'This directory does not belong to the WordPress plugin', 'defender-security' );
238 } elseif ( 'modified' === $data['type'] ) {
239 return esc_html__( 'This plugin file appears modified', 'defender-security' );
240 }
241
242 return '';
243 }
244 }
245