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 / class-malware-deep-scan.php

class-malware-deep-scan.php in Defender Security – Malware Scanner, Login Security & Firewall 6.2.4, at src/behavior/scan/class-malware-deep-scan.php

201 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles malware deep scan.
4 *
5 * @package WP_Defender\Behavior\Scan
6 */
7
8 namespace WP_Defender\Behavior\Scan;
9
10 use stdClass;
11 use WP_Defender\Traits\IO;
12 use PHP_CodeSniffer\Config;
13 use Calotes\Component\Behavior;
14 use PHP_CodeSniffer\Util\Common;
15 use PHP_CodeSniffer\Tokenizers\PHP;
16 use PHP_CodeSniffer\Util\Standards;
17 use WP_Defender\Component\Scan\Tokens;
18 use PHP_CodeSniffer\Tokenizers\Comment;
19 use PHP_CodeSniffer\Tokenizers\Tokenizer;
20 use PHP_CodeSniffer\Exceptions\RuntimeException;
21 use PHP_CodeSniffer\Exceptions\DeepExitException;
22 use PHP_CodeSniffer\Exceptions\TokenizerException;
23 use WP_Filesystem_Base;
24
25 /**
26 * We need to verify the result to avoid false positive.
27 */
28 class Malware_Deep_Scan extends Behavior {
29
30 use IO;
31
32 /**
33 * Perform a deep scan on the provided file content to identify specific tokens.
34 *
35 * @param mixed $file The file content to be scanned.
36 * @param array $quick_found Array containing quick found items.
37 *
38 * @return array|bool Returns the array of quick found items after scanning or false if no tokens are found.
39 */
40 public function do_deep_scan( $file, $quick_found ) {
41 global $wp_filesystem;
42 // Initialize the WP filesystem, no more using 'file-put-contents' function.
43 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
44 require_once ABSPATH . '/wp-admin/includes/file.php';
45 WP_Filesystem();
46 }
47 $this->load_phpcodesniffer_needed();
48 if ( ! defined( 'PHP_CODESNIFFER_VERBOSITY' ) ) {
49 define( 'PHP_CODESNIFFER_VERBOSITY', 0 );
50 }
51 if ( ! defined( 'PHP_CODESNIFFER_CBF' ) ) {
52 define( 'PHP_CODESNIFFER_CBF', false );
53 }
54 $content = $wp_filesystem->get_contents( $file );
55 try {
56 $parser = new PHP( $content, $this->make_phpsniff_config(), PHP_EOL );
57 Tokens::$tokens = $parser->getTokens();
58 foreach ( $quick_found as &$item ) {
59 if ( ! isset( $item['catches'] ) || ! is_array( $item['catches'] ) ) {
60 continue;
61 }
62 foreach ( $item['catches'] as &$catch ) {
63 $offset = $catch['offset'];
64 $catch['line'] = Tokens::get_line_from_offset( $content, $offset );
65 $catch['mapper'] = Tokens::get_offsets_map( Tokens::get_tokens_by_line( $catch['line'] ) );
66 /**
67 * Todo: uncomment the following lines if you need to get details of the code.
68 * $code = Tokens::formatter( Tokens::get_tokens_by_line( $catch['line'] ) );
69 * $catch['code'] = $code;
70 */
71 $tokens = Tokens::get_tokens_by_line( $catch['line'] );
72 foreach ( $tokens as $k => $token ) {
73 if ( in_array(
74 $token['code'],
75 array(
76 T_DOC_COMMENT_WHITESPACE,
77 T_DOC_COMMENT_STAR,
78 T_DOC_COMMENT_WHITESPACE,
79 T_DOC_COMMENT_STRING,
80 ),
81 true
82 ) ) {
83 unset( $tokens[ $k ] );
84 }
85 }
86 $tokens = array_filter(
87 $tokens,
88 static fn( array $token ): bool => array() !== $token
89 );
90 if ( array() === $tokens ) {
91 // This mean the catch is comment, do nothing.
92 return false;
93 }
94 unset( $catch['text'] );
95 }
96 }
97
98 return $quick_found;
99 } catch ( TokenizerException $e ) {
100 $this->log( $e->getMessage(), \WP_Defender\Controller\Scan::SCAN_LOG );
101 }
102
103 return false;
104 }
105
106 /**
107 * Load PHP CodeSniffer components if needed.
108 */
109 private function load_phpcodesniffer_needed() {
110 $ds = DIRECTORY_SEPARATOR;
111 $vendor_path = defender_path( 'src/extra/php-codesniffer-php-token' );
112 if ( ! class_exists( \PHP_CodeSniffer\Util\Tokens::class ) ) {
113 require_once $vendor_path . $ds . 'Util' . $ds . 'Tokens.php';
114 }
115 if ( ! class_exists( Common::class ) ) {
116 require_once $vendor_path . $ds . 'Util' . $ds . 'Common.php';
117 }
118
119 if ( ! class_exists( Tokenizer::class ) ) {
120 require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'Tokenizer.php';
121 }
122
123 if ( ! class_exists( PHP::class ) ) {
124 require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'PHP.php';
125 }
126
127 if ( ! class_exists( Comment::class ) ) {
128 require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'Comment.php';
129 }
130
131 if ( ! class_exists( Standards::class ) ) {
132 require_once $vendor_path . $ds . 'Util' . $ds . 'Standards.php';
133 }
134
135 if ( ! class_exists( RuntimeException::class ) ) {
136 require_once $vendor_path . $ds . 'Exceptions' . $ds . 'RuntimeException.php';
137 }
138
139 if ( ! class_exists( DeepExitException::class ) ) {
140 require_once $vendor_path . $ds . 'Exceptions' . $ds . 'DeepExitException.php';
141 }
142
143 if ( ! class_exists( Config::class ) ) {
144 require_once $vendor_path . $ds . 'Config.php';
145 }
146 }
147
148 /**
149 * Create a new PHP CodeSniffer configuration object with default values.
150 *
151 * @return stdClass Returns the PHP CodeSniffer configuration object with default values set.
152 */
153 private function make_phpsniff_config() {
154 // This config will be used by PHP_CodeSniffer. So we can ignore snake_case naming convention.
155 // @codingStandardsIgnoreStart
156 $config = new stdClass();
157 $config->files = array();
158 $config->standards = array( 'PEAR' );
159 $config->verbosity = 0;
160 $config->interactive = false;
161 $config->cache = false;
162 $config->cacheFile = null;
163 $config->colors = false;
164 $config->explain = false;
165 $config->local = false;
166 $config->showSources = true;
167 $config->showProgress = false;
168 $config->quiet = false;
169 $config->annotations = true;
170 $config->parallel = 1;
171 $config->tabWidth = 0;
172 $config->encoding = 'utf-8';
173 $config->extensions = array(
174 'php' => 'PHP',
175 'inc' => 'PHP',
176 'js' => 'JS',
177 'css' => 'CSS',
178 );
179 $config->sniffs = array();
180 $config->exclude = array();
181 $config->ignored = array();
182 $config->reportFile = null;
183 $config->generator = null;
184 $config->filter = null;
185 $config->bootstrap = array();
186 $config->basepath = null;
187 $config->reports = array( 'full' => null );
188 $config->reportWidth = 'auto';
189 $config->errorSeverity = 5;
190 $config->warningSeverity = 5;
191 $config->recordErrors = true;
192 $config->suffix = '';
193 $config->stdin = true;
194 $config->stdinContent = null;
195 $config->stdinPath = null;
196 $config->unknown = array();
197 // @codingStandardsIgnoreEnd
198 return $config;
199 }
200 }
201