PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.1.0
Defender Security – Malware Scanner, Login Security & Firewall v3.1.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 / component / audit / core-audit.php

core-audit.php in Defender Security – Malware Scanner, Login Security & Firewall 3.1.0, at src/component/audit/core-audit.php

504 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Author: Hoang Ngo
4 */
5
6 namespace WP_Defender\Component\Audit;
7
8 use WP_Defender\Behavior\Utils;
9 use WP_Defender\Model\Audit_Log;
10
11 class Core_Audit extends Audit_Event {
12 public const ACTION_ACTIVATED = 'activated', ACTION_DEACTIVATED = 'deactivated', ACTION_INSTALLED = 'installed', ACTION_UPGRADED = 'upgraded';
13 public const FILE_ADDED = 'file_added', FILE_MODIFIED = 'file_modified';
14 public const CONTEXT_THEME = 'ct_theme', CONTEXT_PLUGIN = 'ct_plugin', CONTEXT_CORE = 'ct_core';
15
16 public function get_hooks() {
17 $data = array(
18 'switch_theme' => array(
19 'args' => array( 'new_name', 'new_theme' ),
20 'callback' => array( self::class, 'process_activate_theme' ),
21 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
22 'action_type' => self::ACTION_ACTIVATED,
23 ),
24 'activated_plugin' => array(
25 'args' => array( 'plugin' ),
26 'text' => sprintf(
27 /* translators: */
28 esc_html__( '%1$s %2$s activated plugin: %3$s, version %4$s', 'wpdef' ),
29 '{{blog_name}}',
30 '{{wp_user}}',
31 '{{plugin_name}}',
32 '{{plugin_version}}'
33 ),
34 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
35 'action_type' => self::ACTION_ACTIVATED,
36 'context' => self::CONTEXT_PLUGIN,
37 'program_args' => array(
38 'plugin_abs_path' => array(
39 'callable' => array( self::class, 'get_plugin_abs_path' ),
40 'params' => array(
41 '{{plugin}}',
42 ),
43 ),
44 'plugin_name' => array(
45 'callable' => 'get_plugin_data',
46 'params' => array(
47 '{{plugin_abs_path}}',
48 ),
49 'result_property' => 'Name',
50 ),
51 'plugin_version' => array(
52 'callable' => 'get_plugin_data',
53 'params' => array(
54 '{{plugin_abs_path}}',
55 ),
56 'result_property' => 'Version',
57 ),
58 ),
59 ),
60 'deleted_plugin' => array(
61 'args' => array( 'plugin_file', 'deleted' ),
62 'callback' => array( self::class, 'process_delete_plugin' ),
63 'action_type' => self::ACTION_DEACTIVATED,
64 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
65 ),
66 'deactivated_plugin' => array(
67 'args' => array( 'plugin' ),
68 'text' => sprintf(
69 /* translators: */
70 esc_html__( '%1$s %2$s deactivated plugin: %3$s, version %4$s', 'wpdef' ),
71 '{{blog_name}}',
72 '{{wp_user}}',
73 '{{plugin_name}}',
74 '{{plugin_version}}'
75 ),
76 'action_type' => self::ACTION_DEACTIVATED,
77 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
78 'context' => self::CONTEXT_PLUGIN,
79 'program_args' => array(
80 'plugin_abs_path' => array(
81 'callable' => array( self::class, 'get_plugin_abs_path' ),
82 'params' => array(
83 '{{plugin}}',
84 ),
85 ),
86 'plugin_name' => array(
87 'callable' => 'get_plugin_data',
88 'params' => array(
89 '{{plugin_abs_path}}',
90 ),
91 'result_property' => 'Name',
92 ),
93 'plugin_version' => array(
94 'callable' => 'get_plugin_data',
95 'params' => array(
96 '{{plugin_abs_path}}',
97 ),
98 'result_property' => 'Version',
99 ),
100 ),
101 ),
102 'upgrader_process_complete' => array(
103 'args' => array( 'upgrader', 'options' ),
104 'callback' => array( self::class, 'process_installer' ),
105 'action_type' => self::ACTION_UPGRADED,
106 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
107 ),
108 'wd_plugin/theme_changed' => array(
109 'args' => array( 'type', 'object', 'file' ),
110 'action_type' => 'update',
111 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
112 'callback' => array( self::class, 'process_content_changed' ),
113 ),
114 'wd_checksum/new_file' => array(
115 'args' => array( 'file' ),
116 'action_type' => self::FILE_ADDED,
117 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
118 'context' => self::CONTEXT_CORE,
119 'text' => sprintf(
120 /* translators: */
121 esc_html__( '%1$s A new file added, path %2$s', 'wpdef' ),
122 '{{blog_name}}',
123 '{{file}}'
124 ),
125 ),
126 'wd_checksum_file_modified' => array(
127 'args' => array( 'file' ),
128 'action_type' => self::FILE_MODIFIED,
129 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
130 'context' => self::CONTEXT_CORE,
131 'text' => sprintf(
132 /* translators: */
133 esc_html__( '%1$s A file has been modified, path %2$s', 'wpdef' ),
134 '{{blog_name}}',
135 '{{file}}'
136 ),
137 ),
138 );
139
140 global $wp_version;
141 // @since 2.7.0 Add hook for deleted theme. Use 'deleted_theme' hook that was added since WP v5.8.0.
142 if ( version_compare( $wp_version, '5.8.0', '>=' ) ) {
143 $data['deleted_theme'] = array(
144 'args' => array( 'stylesheet', 'deleted' ),
145 'text' => sprintf(
146 /* translators: */
147 esc_html__( '%1$s %2$s deleted theme: %3$s', 'wpdef' ),
148 '{{blog_name}}',
149 '{{wp_user}}',
150 '{{stylesheet}}'
151 ),
152 'action_type' => self::ACTION_DEACTIVATED,
153 'event_type' => Audit_Log::EVENT_TYPE_SYSTEM,
154 'context' => self::CONTEXT_THEME,
155 );
156 }
157
158 return $data;
159 }
160
161 public function process_content_changed() {
162 $args = func_get_args();
163 $type = $args[1]['type'];
164 $object = $args[1]['object'];
165 $file = $args[1]['file'];
166 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
167
168 return array(
169 sprintf(
170 /* translators: */
171 esc_html__( '%1$s %2$s updated file %3$s of %4$s %5$s', 'wpdef' ),
172 $blog_name,
173 $this->get_source_of_action(),
174 $file,
175 $type,
176 $object
177 ),
178 'plugin' === $type ? self::CONTEXT_PLUGIN : self::CONTEXT_THEME,
179 );
180 }
181
182 public function upgrade_core() {
183 $update_core = get_site_transient( 'update_core' );
184 if ( is_object( $update_core ) ) {
185 $updates = $update_core->updates;
186 $updates = array_shift( $updates );
187 if ( is_object( $updates ) && property_exists( $updates, 'version' ) ) {
188 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
189
190 return array(
191 sprintf(
192 /* translators: */
193 esc_html__( '%1$s %2$s updated WordPress to %3$s', 'wpdef' ),
194 $blog_name,
195 $this->get_source_of_action(),
196 $updates->version
197 ),
198 self::CONTEXT_CORE,
199 );
200 }
201 }
202
203 return false;
204 }
205
206 public function bulk_upgrade( $upgrader, $options ) {
207 if ( ! is_object( $upgrader->skin ) ) {
208 return false;
209 }
210 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
211 if ( isset( $upgrader->skin->result ) && is_wp_error( $upgrader->skin->result ) ) {
212 // Todo: extend Audit table with a new column for the result of the process and divide it into success and failure.
213 $failed_result = true;
214 } else {
215 $failed_result = false;
216 }
217
218 if ( 'theme' === $options['type'] ) {
219 $texts = array();
220 foreach ( $options['themes'] as $slug ) {
221 $theme = wp_get_theme( $slug );
222 if ( is_object( $theme ) ) {
223 $texts[] = sprintf(
224 /* translators: */
225 esc_html( '%1$s %2$s %3$s' ),
226 $theme->Name,
227 $failed_result ? __( 'version', 'wpdef' ) : __( 'to', 'wpdef' ),
228 $theme->get( 'Version' )
229 );
230 }
231 }
232 if ( count( $texts ) ) {
233 return array(
234 sprintf(
235 /* translators: */
236 esc_html__( '%1$s %2$s %3$s themes: %4$s', 'wpdef' ),
237 $blog_name,
238 $this->get_source_of_action(),
239 $failed_result ? __( 'was unable to update', 'wpdef' ) : __( 'updated', 'wpdef' ),
240 implode( ', ', $texts )
241 ),
242 self::CONTEXT_THEME,
243 );
244 } else {
245 return false;
246 }
247 } elseif ( 'plugin' === $options['type'] ) {
248 $texts = array();
249 foreach ( $options['plugins'] as $slug ) {
250 $plugin = get_plugin_data( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $slug );
251 if ( is_array( $plugin ) && isset( $plugin['Name'] ) && ! empty( $plugin['Name'] ) ) {
252 $texts[] = sprintf(
253 /* translators: */
254 esc_html( '%1$s %2$s %3$s' ),
255 $plugin['Name'],
256 $failed_result ? __( 'version', 'wpdef' ) : __( 'to', 'wpdef' ),
257 $plugin['Version']
258 );
259 }
260 }
261 if ( count( $texts ) ) {
262 return array(
263 sprintf(
264 /* translators: */
265 esc_html__( '%1$s %2$s %3$s plugins: %4$s', 'wpdef' ),
266 $blog_name,
267 $this->get_source_of_action(),
268 $failed_result ? __( 'was unable to update', 'wpdef' ) : __( 'updated', 'wpdef' ),
269 implode( ', ', $texts )
270 ),
271 self::CONTEXT_PLUGIN,
272 );
273 } else {
274 return false;
275 }
276 }
277 }
278
279 public function single_upgrade( $upgrader, $options ) {
280 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
281
282 if ( 'theme' === $options['type'] ) {
283 $theme = wp_get_theme( $options['theme'] );
284 if ( is_object( $theme ) ) {
285 $name = $theme->Name;
286 $version = $theme->get( 'Version' );
287
288 return array(
289 sprintf(
290 /* translators: */
291 esc_html__( '%1$s %2$s updated theme: %3$s, version %4$s', 'wpdef' ),
292 $blog_name,
293 $this->get_source_of_action(),
294 $name,
295 $version
296 ),
297 self::CONTEXT_THEME,
298 );
299 } else {
300 return false;
301 }
302 } elseif ( 'plugin' === $options['type'] ) {
303 $slug = $options['plugin'];
304 $data = get_plugin_data( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $slug );
305 if ( is_array( $data ) ) {
306 $name = $data['Name'];
307 $version = $data['Version'];
308
309 return array(
310 sprintf(
311 /* translators: */
312 esc_html__( '%1$s %2$s updated plugin: %3$s, version %4$s', 'wpdef' ),
313 $blog_name,
314 $this->get_source_of_action(),
315 $name,
316 $version
317 ),
318 self::CONTEXT_PLUGIN,
319 );
320 } else {
321 return false;
322 }
323 }
324 }
325
326 /**
327 * Install process.
328 * Log in the format: {BLOG_NAME} {USERNAME} installed {theme/plugin}: {theme/plugin name}, version {VERSION}.
329 *
330 * @return bool|array
331 */
332 private function single_install( $upgrader, $options ) {
333 if ( ! is_object( $upgrader->skin ) ) {
334 return false;
335 }
336 // Only for plugins, themes. No for translation, core.
337 if ( ! in_array( $options['type'], array( 'theme', 'plugin' ), true ) ) {
338 return false;
339 }
340
341 $name = '';
342 if ( @is_object( $upgrader->skin->api ) ) {// phpcs:ignore
343 $name = $upgrader->skin->api->name;
344 } elseif ( ! empty( $upgrader->skin->upgrader) ) {
345 $type_data = ( 'theme' === $options['type'] && isset( $upgrader->skin->upgrader->new_theme_data ) )
346 ? $upgrader->skin->upgrader->new_theme_data
347 : $upgrader->skin->upgrader->new_plugin_data;
348 if ( ! empty( $type_data['Name'] ) && ! empty( $type_data['Version'] ) ) {
349 $name = $type_data['Name'] . ', version ' . $type_data['Version'];
350 }
351 } elseif ( ! empty( $upgrader->skin->result ) ) {
352 if ( is_array( $upgrader->skin->result ) && isset( $upgrader->skin->result['destination_name'] ) ) {
353 $name = $upgrader->skin->result['destination_name'];
354 } elseif ( is_object( $upgrader->skin->result ) && property_exists( $upgrader->skin->result, 'destination_name' ) ) {
355 $name = $upgrader->skin->result->destination_name;
356 }
357 }
358
359 if ( empty( $name ) ) {
360 return false;
361 }
362
363 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
364 if ( 'theme' === $options['type'] ) {
365 return array(
366 sprintf(
367 /* translators: %s - blog name, %s - username, %s - theme name */
368 esc_html__( '%1$s %2$s installed theme: %3$s', 'wpdef' ),
369 $blog_name,
370 $this->get_source_of_action(),
371 $name
372 ),
373 self::CONTEXT_THEME,
374 self::ACTION_INSTALLED,
375 );
376 } else {
377 return array(
378 sprintf(
379 /* translators: %s - blog name, %s - username, %s - plugin name */
380 esc_html__( '%1$s %2$s installed plugin: %3$s', 'wpdef' ),
381 $blog_name,
382 $this->get_source_of_action(),
383 $name
384 ),
385 self::CONTEXT_PLUGIN,
386 self::ACTION_INSTALLED,
387 );
388 }
389
390 return false;
391 }
392
393 /**
394 * Param 'upgrader' is WP_Upgrader instance. It might be a Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance.
395 * Param 'options' is array of bulk item update data. Keys:
396 * 'action' (string) Type of action. Default 'update'.
397 * 'type' (string) Type of update process. Accepts 'plugin', 'theme', 'translation', or 'core'.
398 * 'bulk' (bool) Whether the update process is a bulk update. Default true.
399 * 'plugins' (array) Array of the basename paths of the plugins' main files.
400 * 'themes' (array) The theme slugs.
401 * 'translations' (array) Array of translations update data: 'language', 'type', 'slug', 'version'.
402 *
403 * @return mixed
404 */
405 public function process_installer() {
406 $args = func_get_args();
407 $upgrader = $args[1]['upgrader'];
408 $options = $args[1]['options'];
409 if ( 'core' === $options['type'] ) {
410 return $this->upgrade_core();
411 // If this is core, we just create text and return.
412 } elseif ( isset( $options['bulk'] ) && true === $options['bulk'] ) {
413 // Case: local install/update work with 'bulk' => true (for mass action and no).
414 return $this->bulk_upgrade( $upgrader, $options );
415 } elseif ( 'install' === $options['action'] ) {
416 // Case: actions from Hub.
417 return $this->single_install( $upgrader, $options );
418 } elseif ( 'update' === $options['action'] ) {
419 // Case: actions from Hub.
420 return $this->single_upgrade( $upgrader, $options );
421 }
422 }
423
424 /**
425 * Fires after the theme is switched.
426 */
427 public function process_activate_theme() {
428 $args = func_get_args();
429 $new_theme = $args[1]['new_theme'];
430 if ( ! is_object( $new_theme ) ) {
431 return false;
432 }
433 $new_name = $args[1]['new_name'];
434 $version = $new_theme->get( 'Version' );
435 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
436
437 return array(
438 sprintf(
439 /* translators: */
440 esc_html__( '%1$s %2$s activated theme: %3$s, version %4$s', 'wpdef' ),
441 $blog_name,
442 $this->get_source_of_action(),
443 $new_name,
444 $version
445 ),
446 self::CONTEXT_THEME,
447 );
448 }
449
450 /**
451 * Fires immediately after a plugin deletion.
452 * There is no way to get the plugin data (name, version) because it has been removed. Only slug.
453 *
454 * @return array
455 */
456 public function process_delete_plugin() {
457 $args = func_get_args();
458 // If 'deleted'-arg is false then the plugin deletion wasn't successful.
459 if ( empty( $args[1]['deleted'] ) ) {
460 // Todo: extend Audit table with a new column for the result of the process and divide it into success and failure.
461 $failed_result = true;
462 } else {
463 $failed_result = false;
464 }
465 $plugin_file = $args[1]['plugin_file'];
466 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
467
468 return array(
469 sprintf(
470 /* translators: */
471 esc_html__( '%1$s %2$s %3$s plugin: %4$s', 'wpdef' ),
472 $blog_name,
473 $this->get_source_of_action(),
474 $failed_result ? __( 'was unable to delete', 'wpdef' ) : __( 'deleted', 'wpdef' ),
475 $plugin_file
476 ),
477 self::CONTEXT_PLUGIN,
478 );
479 }
480
481 public function dictionary() {
482
483 return array(
484 self::ACTION_DEACTIVATED => esc_html__( 'deactivated', 'wpdef' ),
485 self::ACTION_UPGRADED => esc_html__( 'upgraded', 'wpdef' ),
486 self::ACTION_ACTIVATED => esc_html__( 'activated', 'wpdef' ),
487 self::ACTION_INSTALLED => esc_html__( 'installed', 'wpdef' ),
488 self::CONTEXT_THEME => esc_html__( 'theme', 'wpdef' ),
489 self::CONTEXT_PLUGIN => esc_html__( 'plugin', 'wpdef' ),
490 self::CONTEXT_CORE => esc_html__( 'WordPress', 'wpdef' ),
491 self::FILE_ADDED => esc_html__( 'File Added', 'wpdef' ),
492 self::FILE_MODIFIED => esc_html__( 'File Modified', 'wpdef' ),
493 );
494 }
495
496 public function get_plugin_abs_path( $slug ) {
497 if ( ! is_file( $slug ) ) {
498 $slug = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $slug;
499 }
500
501 return $slug;
502 }
503 }
504