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

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

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