diff --git a/Controller.php b/Controller.php index 2adc12f..d0cbbdc 100644 --- a/Controller.php +++ b/Controller.php @@ -76,21 +76,13 @@ private function scanFileForHeuristic(FileInfo $file_info, $root_path) // Processing results if ( !empty($verdict) ) { $output->weak_spots = $verdict; - $output->severity = ( - array_key_exists('CRITICAL', $verdict) || - array_key_exists('SIGNATURES', $verdict) - ) + $output->severity = array_key_exists('SIGNATURES', $verdict) ? 'CRITICAL' - : ( - array_key_exists('DANGER', $verdict) - ? 'DANGER' - : 'SUSPICIOUS' - ); + : 'SUSPICIOUS'; $output->status = ( array_key_exists('CRITICAL', $verdict) || array_key_exists('SIGNATURES', $verdict) || - array_key_exists('SUSPICIOUS', $verdict) || - array_key_exists('DANGER', $verdict) + array_key_exists('SUSPICIOUS', $verdict) ) ? 'INFECTED' : 'OK'; diff --git a/HeuristicAnalyser.php b/HeuristicAnalyser.php index 19e8a40..6fe1753 100644 --- a/HeuristicAnalyser.php +++ b/HeuristicAnalyser.php @@ -85,14 +85,13 @@ class HeuristicAnalyser public $looks_safe = false; private $bad_constructs = array( - 'CRITICAL' => array( + 'SUSPICIOUS' => array( + 'str_rot13', + 'syslog', 'eval', 'assert', 'create_function', 'shell_exec', - // 'unserialize', - ), - 'DANGER' => array( 'system', 'passthru', 'proc_open', @@ -100,10 +99,7 @@ class HeuristicAnalyser 'pcntl_exec', 'popen', '`', - ), - 'SUSPICIOUS' => array( - 'str_rot13', - 'syslog', + // 'unserialize', ), ); @@ -298,6 +294,11 @@ private function checkFileSize($file_size) */ public function processContent() { + // Skip files does not contain PHP code + if ( $this->extension !== 'php' && ! $this->code_style->hasPHPOpenTags() ) { + return; + } + // Analysing code style // Do this, only for initial code if ( ! $this->evaluations->evaluations ) { @@ -447,14 +448,14 @@ public function makeVerdict() // If common bad structures found, then check containment for superglobals if ($found_malware_key !== false && $this->checkingSuperGlobalsInTheSystemCommands($this->tokens->current)) { - $this->verdict['CRITICAL'][$this->tokens->current->line][] = 'global variables in a sys command'; + $this->verdict['SUSPICIOUS'][$this->tokens->current->line][] = 'global variables in a sys command'; break; } // If the current token is backtick, so we have to check shell command existing inside the backticks. if ( $current_token_value === '`' ) { if ( $this->checkingShellCommand($this->tokens->current) ) { - $this->verdict['CRITICAL'][$this->tokens->current->line][] = 'shell command inside the backticks'; + $this->verdict['SUSPICIOUS'][$this->tokens->current->line][] = 'shell command inside the backticks'; } break; } @@ -471,9 +472,9 @@ public function makeVerdict() $this->dangerous_decoded_values, true ); - $this->verdict['CRITICAL'][$this->tokens->current->line][] = $this->dangerous_decoded_values[$found_malware_key]; + $this->verdict['SUSPICIOUS'][$this->tokens->current->line][] = $this->dangerous_decoded_values[$found_malware_key]; } elseif ($this->checkingDecryptedToken($this->tokens->current)) { - $this->verdict['CRITICAL'][$this->tokens->current->line][] = 'the function contains suspicious arguments'; + $this->verdict['SUSPICIOUS'][$this->tokens->current->line][] = 'the function contains suspicious arguments'; } } @@ -481,7 +482,7 @@ public function makeVerdict() foreach ( $this->includes->includes as $include ) { if ( $include['status'] === false ) { if ( $include['not_url'] === false && $include['ext_good'] === false ) { - $this->verdict['CRITICAL'][$include['string']][] = substr( + $this->verdict['SUSPICIOUS'][$include['string']][] = substr( $this->tokens->glueTokens(ExtendedSplFixedArray::createFromArray($include['include'])), 0, 255 @@ -589,8 +590,8 @@ private function checkingSpecialDecryptedToken(DataStructures\Token $token) } return $token->type === 'T_CONSTANT_ENCAPSED_STRING' && - is_callable(trim((string)$token->value, '\'')) && - in_array(trim((string)$token->value, '\''), $this->dangerous_decoded_values, true); + is_callable(trim((string)$token->value, '\'')) && + in_array(trim((string)$token->value, '\''), $this->dangerous_decoded_values, true); } private function checkingDecryptedToken(DataStructures\Token $token) diff --git a/Modules/CodeStyle.php b/Modules/CodeStyle.php index b99ce6b..547ab58 100644 --- a/Modules/CodeStyle.php +++ b/Modules/CodeStyle.php @@ -139,6 +139,32 @@ public function detectBadLines() return $result; } + /** + * Check if file contains PHP open tags ("<\?php" or `<\?`). + * @return bool + */ + public function hasPHPOpenTags() + { + foreach ( $this->tokens as $_token => $content ) { + if ( isset($content[0]) && isset($this->tokens->next1[0]) ) { + if ( $content[0] === 'T_OPEN_TAG' ) { + //check if open tag is short + $is_short = isset($content[1]) && $content[1] === 'tokens->next1[0] === 'T_WHITESPACE' || + // should be whitespaces or variable after tag + !$is_short && in_array($this->tokens->next1[0], array('T_WHITESPACE', 'T_VARIABLE')) + ) { + return true; + } + } + } + } + + return false; + } + private function proportionOfSpecialSymbols() { $content = ''; diff --git a/tests/Cases/Common/NoPHPCodeTest.php b/tests/Cases/Common/NoPHPCodeTest.php new file mode 100644 index 0000000..31e15e4 --- /dev/null +++ b/tests/Cases/Common/NoPHPCodeTest.php @@ -0,0 +1,100 @@ +current_dir = __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR; + $this->heuristic_scanner = new Controller(); + $this->files_list = array( + 'testNoPHPCodeFile' => new FileInfo( + 'has_no_php.otf', + file_get_contents($this->current_dir . 'has_no_php.otf') + ), + 'testNoPHPCodeFileWithShortOpenTag' => new FileInfo( + 'has_no_php_but_has_short_open_tag.otf', + file_get_contents($this->current_dir . 'has_no_php_but_has_short_open_tag.otf') + ), + 'testHasPHPCodeBadFile' => new FileInfo( + 'has_bad_php.otf', + file_get_contents($this->current_dir . 'has_bad_php.otf') + ), + 'testHasPHPCodeBadFileShortOpenTag' => new FileInfo( + 'has_bad_php_short_tag.otf', + file_get_contents($this->current_dir . 'has_bad_php_short_tag.otf') + ), + 'testHasPHPCodeBadFileNoClosingTag' => new FileInfo( + 'has_bad_php_no_closing_tag.otf', + file_get_contents($this->current_dir . 'has_bad_php_no_closing_tag.otf') + ), + 'testHasPHPCodeBadFileOpenSomewhere' => new FileInfo( + 'has_bad_php_open_somewhere.otf', + file_get_contents($this->current_dir . 'has_bad_php_open_somewhere.otf') + ), + 'testHasPHPCodeGoodFile' => new FileInfo( + 'has_good_php.otf', + file_get_contents($this->current_dir . 'has_good_php.otf') + ), + ); + parent::setUp(); // TODO: Change the autogenerated stub + } + + public function testNoPHPCodeFile() + { + $verdict = $this->heuristic_scanner->scanFile($this->files_list[__FUNCTION__], $this->current_dir); + $this->assertEquals('OK', $verdict->status); + $this->assertEquals(null, $verdict->severity); + } + + public function testNoPHPCodeFileWithShortOpenTag() + { + $verdict = $this->heuristic_scanner->scanFile($this->files_list[__FUNCTION__], $this->current_dir); + $this->assertEquals('OK', $verdict->status); + $this->assertEquals(null, $verdict->severity); + } + public function testHasPHPCodeBadFile() + { + $verdict = $this->heuristic_scanner->scanFile($this->files_list[__FUNCTION__], $this->current_dir); + $this->assertEquals('INFECTED', $verdict->status); + $this->assertEquals('SUSPICIOUS', $verdict->severity); + } + + public function testHasPHPCodeBadFileNoClosingTag() + { + $verdict = $this->heuristic_scanner->scanFile($this->files_list[__FUNCTION__], $this->current_dir); + $this->assertEquals('INFECTED', $verdict->status); + $this->assertEquals('SUSPICIOUS', $verdict->severity); + } + + public function testHasPHPCodeBadFileOpenSomewhere() + { + $verdict = $this->heuristic_scanner->scanFile($this->files_list[__FUNCTION__], $this->current_dir); + $this->assertEquals('INFECTED', $verdict->status); + $this->assertEquals('SUSPICIOUS', $verdict->severity); + } + + public function testHasPHPCodeBadFileShortOpenTag() + { + if ( ini_get('short_open_tag') ) { + $verdict = $this->heuristic_scanner->scanFile($this->files_list[__FUNCTION__], $this->current_dir); + $this->assertEquals('INFECTED', $verdict->status); + $this->assertEquals('SUSPICIOUS', $verdict->severity); + } + } + + public function testHasPHPCodeGoodFile() + { + $verdict = $this->heuristic_scanner->scanFile($this->files_list[__FUNCTION__], $this->current_dir); + $this->assertEquals('OK', $verdict->status); + $this->assertEquals(null, $verdict->severity); + } +} diff --git a/tests/Cases/Common/files/has_bad_php.otf b/tests/Cases/Common/files/has_bad_php.otf new file mode 100644 index 0000000..28457f4 --- /dev/null +++ b/tests/Cases/Common/files/has_bad_php.otf @@ -0,0 +1,96 @@ + +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malwaresome text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + + diff --git a/tests/Cases/Common/files/has_bad_php_no_closing_tag.otf b/tests/Cases/Common/files/has_bad_php_no_closing_tag.otf new file mode 100644 index 0000000..94f2f58 --- /dev/null +++ b/tests/Cases/Common/files/has_bad_php_no_closing_tag.otf @@ -0,0 +1,95 @@ + +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malwaresome text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + + diff --git a/tests/Cases/Common/files/has_good_php.otf b/tests/Cases/Common/files/has_good_php.otf new file mode 100644 index 0000000..f1c47c3 --- /dev/null +++ b/tests/Cases/Common/files/has_good_php.otf @@ -0,0 +1,96 @@ + +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malware +some text not contains malwaresome text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + some text not contains malware + + diff --git a/tests/Cases/Common/files/has_no_php.otf b/tests/Cases/Common/files/has_no_php.otf new file mode 100644 index 0000000..401ec0f Binary files /dev/null and b/tests/Cases/Common/files/has_no_php.otf differ diff --git a/tests/Cases/Common/files/has_no_php_but_has_short_open_tag.otf b/tests/Cases/Common/files/has_no_php_but_has_short_open_tag.otf new file mode 100644 index 0000000..ee0df75 Binary files /dev/null and b/tests/Cases/Common/files/has_no_php_but_has_short_open_tag.otf differ