PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.1
Defender Security – Malware Scanner, Login Security & Firewall v5.8.1
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.8.1, at src/behavior/scan/class-malware-deep-scan.php

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