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

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

198 lines 6.1 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( $tokens );
87 if ( empty( $tokens ) ) {
88 // This mean the catch is comment, do nothing.
89 return false;
90 }
91 unset( $catch['text'] );
92 }
93 }
94
95 return $quick_found;
96 } catch ( TokenizerException $e ) {
97 $this->log( $e->getMessage(), \WP_Defender\Controller\Scan::SCAN_LOG );
98 }
99
100 return false;
101 }
102
103 /**
104 * Load PHP CodeSniffer components if needed.
105 */
106 private function load_phpcodesniffer_needed() {
107 $ds = DIRECTORY_SEPARATOR;
108 $vendor_path = defender_path( 'src/extra/php-codesniffer-php-token' );
109 if ( ! class_exists( \PHP_CodeSniffer\Util\Tokens::class ) ) {
110 require_once $vendor_path . $ds . 'Util' . $ds . 'Tokens.php';
111 }
112 if ( ! class_exists( Common::class ) ) {
113 require_once $vendor_path . $ds . 'Util' . $ds . 'Common.php';
114 }
115
116 if ( ! class_exists( Tokenizer::class ) ) {
117 require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'Tokenizer.php';
118 }
119
120 if ( ! class_exists( PHP::class ) ) {
121 require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'PHP.php';
122 }
123
124 if ( ! class_exists( Comment::class ) ) {
125 require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'Comment.php';
126 }
127
128 if ( ! class_exists( Standards::class ) ) {
129 require_once $vendor_path . $ds . 'Util' . $ds . 'Standards.php';
130 }
131
132 if ( ! class_exists( RuntimeException::class ) ) {
133 require_once $vendor_path . $ds . 'Exceptions' . $ds . 'RuntimeException.php';
134 }
135
136 if ( ! class_exists( DeepExitException::class ) ) {
137 require_once $vendor_path . $ds . 'Exceptions' . $ds . 'DeepExitException.php';
138 }
139
140 if ( ! class_exists( Config::class ) ) {
141 require_once $vendor_path . $ds . 'Config.php';
142 }
143 }
144
145 /**
146 * Create a new PHP CodeSniffer configuration object with default values.
147 *
148 * @return stdClass Returns the PHP CodeSniffer configuration object with default values set.
149 */
150 private function make_phpsniff_config() {
151 // This config will be used by PHP_CodeSniffer. So we can ignore snake_case naming convention.
152 // @codingStandardsIgnoreStart
153 $config = new stdClass();
154 $config->files = array();
155 $config->standards = array( 'PEAR' );
156 $config->verbosity = 0;
157 $config->interactive = false;
158 $config->cache = false;
159 $config->cacheFile = null;
160 $config->colors = false;
161 $config->explain = false;
162 $config->local = false;
163 $config->showSources = true;
164 $config->showProgress = false;
165 $config->quiet = false;
166 $config->annotations = true;
167 $config->parallel = 1;
168 $config->tabWidth = 0;
169 $config->encoding = 'utf-8';
170 $config->extensions = array(
171 'php' => 'PHP',
172 'inc' => 'PHP',
173 'js' => 'JS',
174 'css' => 'CSS',
175 );
176 $config->sniffs = array();
177 $config->exclude = array();
178 $config->ignored = array();
179 $config->reportFile = null;
180 $config->generator = null;
181 $config->filter = null;
182 $config->bootstrap = array();
183 $config->basepath = null;
184 $config->reports = array( 'full' => null );
185 $config->reportWidth = 'auto';
186 $config->errorSeverity = 5;
187 $config->warningSeverity = 5;
188 $config->recordErrors = true;
189 $config->suffix = '';
190 $config->stdin = true;
191 $config->stdinContent = null;
192 $config->stdinPath = null;
193 $config->unknown = array();
194 // @codingStandardsIgnoreEnd
195 return $config;
196 }
197 }
198