mirror of
https://github.com/scr34m/php-malware-scanner.git
synced 2026-06-16 12:30:35 +00:00
Fix multiple bugs and improve robustness
- Fix date format in report(): H:m:s -> H:i:s (m=month, i=minutes) - Fix loadWhitelists(): skip lines shorter than 32 chars to avoid empty hash entries - Fix updateCombinedWhitelist(): return false on hash mismatch after download - Fix scan(): handle file_get_contents() failure for unreadable files - Fix scanFunc_RE(): suppress and handle invalid regex patterns gracefully - Fix addWordpressChecksums(): validate version format, handle network/JSON errors, support both API response formats - Add 30s HTTP timeout to updateCombinedWhitelist() network requests - Fix composer.json minimum PHP version: 5.2 -> 5.3 (anonymous functions require 5.3) - Add .gitignore for .idea/, whitelist.dat, vendor/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.idea/
|
||||||
|
whitelist.dat
|
||||||
|
vendor/
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"homepage": "https://github.com/scr34m/php-malware-scanner",
|
"homepage": "https://github.com/scr34m/php-malware-scanner",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.2.0"
|
"php": ">=5.3.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
},
|
},
|
||||||
|
|||||||
53
scan.php
53
scan.php
@@ -203,7 +203,10 @@ class MalwareScanner
|
|||||||
$fp = fopen($file, 'r');
|
$fp = fopen($file, 'r');
|
||||||
while (!feof($fp)) {
|
while (!feof($fp)) {
|
||||||
$line = fgets($fp);
|
$line = fgets($fp);
|
||||||
$this->whitelist[] = substr($line, 0, 32);
|
$hash = substr($line, 0, 32);
|
||||||
|
if (strlen($hash) === 32) {
|
||||||
|
$this->whitelist[] = $hash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
}
|
}
|
||||||
@@ -212,16 +215,32 @@ class MalwareScanner
|
|||||||
|
|
||||||
public function addWordpressChecksums($wp_version)
|
public function addWordpressChecksums($wp_version)
|
||||||
{
|
{
|
||||||
$apiurl = 'https://api.wordpress.org/core/checksums/1.0/?version=' . $wp_version;
|
if (!preg_match('/^\d+\.\d+(\.\d+)?$/', $wp_version)) {
|
||||||
$json = json_decode(file_get_contents($apiurl));
|
$this->error('Invalid WordPress version format: ' . $wp_version);
|
||||||
$checksums = $json->checksums;
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
if ($checksums->$wp_version == false) { #no checksum returned
|
$apiurl = 'https://api.wordpress.org/core/checksums/1.0/?version=' . $wp_version;
|
||||||
|
$raw = file_get_contents($apiurl);
|
||||||
|
if ($raw === false) {
|
||||||
$this->error('Cannot load wordpress checksums from: ' . $apiurl);
|
$this->error('Cannot load wordpress checksums from: ' . $apiurl);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($checksums->$wp_version as $file => $checksum) {
|
$json = json_decode($raw);
|
||||||
|
if ($json === null || !isset($json->checksums)) {
|
||||||
|
$this->error('Invalid response from WordPress checksums API');
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$checksums = $json->checksums;
|
||||||
|
if ($checksums === false || empty((array)$checksums)) {
|
||||||
|
$this->error('No checksums returned for WordPress version: ' . $wp_version);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries = isset($checksums->$wp_version) ? $checksums->$wp_version : $checksums;
|
||||||
|
foreach ($entries as $file => $checksum) {
|
||||||
$this->whitelist[] = $checksum;
|
$this->whitelist[] = $checksum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -623,8 +642,8 @@ class MalwareScanner
|
|||||||
private function report($start, $dir)
|
private function report($start, $dir)
|
||||||
{
|
{
|
||||||
$end = time();
|
$end = time();
|
||||||
echo 'Start time: ' . date('Y-m-d H:m:s', $start) . PHP_EOL;
|
echo 'Start time: ' . date('Y-m-d H:i:s', $start) . PHP_EOL;
|
||||||
echo 'End time: ' . date('Y-m-d H:m:s', $end) . PHP_EOL;
|
echo 'End time: ' . date('Y-m-d H:i:s', $end) . PHP_EOL;
|
||||||
echo 'Total execution time: ' . ($end - $start) . PHP_EOL;
|
echo 'Total execution time: ' . ($end - $start) . PHP_EOL;
|
||||||
echo 'Base directory: ' . $dir . PHP_EOL;
|
echo 'Base directory: ' . $dir . PHP_EOL;
|
||||||
echo 'Total directories scanned: ' . $this->stat['directories'] . PHP_EOL;
|
echo 'Total directories scanned: ' . $this->stat['directories'] . PHP_EOL;
|
||||||
@@ -680,6 +699,12 @@ class MalwareScanner
|
|||||||
{
|
{
|
||||||
$this->stat['files_scanned']++;
|
$this->stat['files_scanned']++;
|
||||||
$fileContent = file_get_contents($path);
|
$fileContent = file_get_contents($path);
|
||||||
|
if ($fileContent === false) {
|
||||||
|
if (!$this->flagHideErr) {
|
||||||
|
echo $this->ANSI_RED . '# ER' . $this->ANSI_OFF . ' # {' . $path . '} (unreadable)' . PHP_EOL;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$found = false;
|
$found = false;
|
||||||
$inWhitelist = false;
|
$inWhitelist = false;
|
||||||
$hash = md5($fileContent);
|
$hash = md5($fileContent);
|
||||||
@@ -725,7 +750,10 @@ class MalwareScanner
|
|||||||
//Patterns will match multiple lines, though you can use ^$ to match the beginning and end of a line.
|
//Patterns will match multiple lines, though you can use ^$ to match the beginning and end of a line.
|
||||||
private function scanFunc_RE(&$pattern, &$content)
|
private function scanFunc_RE(&$pattern, &$content)
|
||||||
{
|
{
|
||||||
$ret = preg_match('/' . $pattern . '/im', $content, $match, PREG_OFFSET_CAPTURE);
|
$ret = @preg_match('/' . $pattern . '/im', $content, $match, PREG_OFFSET_CAPTURE);
|
||||||
|
if ($ret === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ($ret) {
|
if ($ret) {
|
||||||
return $match[0][1];
|
return $match[0][1];
|
||||||
}
|
}
|
||||||
@@ -790,7 +818,9 @@ class MalwareScanner
|
|||||||
|
|
||||||
private function updateCombinedWhitelist($url = 'https://scr34m.github.io/php-malware-scanner')
|
private function updateCombinedWhitelist($url = 'https://scr34m.github.io/php-malware-scanner')
|
||||||
{
|
{
|
||||||
$latest_hash = trim(file_get_contents($url . '/database/compressed.sha256'));
|
$ctx = stream_context_create(array('http' => array('timeout' => 30)));
|
||||||
|
|
||||||
|
$latest_hash = trim(file_get_contents($url . '/database/compressed.sha256', false, $ctx));
|
||||||
if ($latest_hash === false) {
|
if ($latest_hash === false) {
|
||||||
$this->error('Unable to download database checksum');
|
$this->error('Unable to download database checksum');
|
||||||
return false;
|
return false;
|
||||||
@@ -809,7 +839,7 @@ class MalwareScanner
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($download) {
|
if ($download) {
|
||||||
$data = file_get_contents($url . '/database/compressed.dat');
|
$data = file_get_contents($url . '/database/compressed.dat', false, $ctx);
|
||||||
if ($data === false) {
|
if ($data === false) {
|
||||||
$this->error('Unable to download database');
|
$this->error('Unable to download database');
|
||||||
return false;
|
return false;
|
||||||
@@ -819,6 +849,7 @@ class MalwareScanner
|
|||||||
$hash = hash_file('sha256', $file);
|
$hash = hash_file('sha256', $file);
|
||||||
if ($hash != $latest_hash) {
|
if ($hash != $latest_hash) {
|
||||||
$this->error('Downloaded database hash mismatch');
|
$this->error('Downloaded database hash mismatch');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user