Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add sha256 guid and detection when scan is malicious #35

Merged
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ test.sh
gdata-antivirus.zip
test.php
scoped-code/
/svn/
/svn/
eicar*
7 changes: 7 additions & 0 deletions Infrastructure/Database/DetectedFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Gdatacyberdefenseag\GdataAntivirus\Infrastructure\Database;

class DetectedFile {
public function __construct(public string $path, public string $detection, public string $sha256) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the Guid?

}
27 changes: 23 additions & 4 deletions Infrastructure/Database/FindingsQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Gdatacyberdefenseag\GdataAntivirus\Infrastructure\Database;

use Psr\Log\LoggerInterface;
use wpdb;

class FindingsQuery implements IFindingsQuery {
private LoggerInterface $logger;
Expand All @@ -25,13 +26,15 @@ public function create(): void {
$charset_collate = $wpdb->get_charset_collate();
$sql = 'CREATE TABLE ' . $this->get_table_name() . ' (
file_path VARCHAR(512) NOT NULL,
detection VARCHAR(128) NOT NULL,
sha256 VARCHAR(64) NOT NULL,
UNIQUE KEY file_path (file_path)
)' . $charset_collate . ';';

require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
wp_cache_set($this->get_table_name(), 'true', 'GdataAntivirus');
}
}

public function remove(): void {
global $wpdb;
Expand Down Expand Up @@ -64,7 +67,7 @@ public function table_exists(): bool {
return false;
}

public function add( string $file ): void {
public function add( DetectedFile $detected_file ): void {
global $wpdb;

if (! $this->table_exists()) {
Expand All @@ -74,7 +77,11 @@ public function add( string $file ): void {
try {
$wpdb->insert(
$this->get_table_name(),
array( 'file_path' => $file )
array(
'file_path' => $detected_file->path,
'detection' => $detected_file->detection,
'sha256' => $detected_file->sha256
)
);
} catch (\Exception $e) {
$this->logger->debug($e->getMessage());
Expand All @@ -93,14 +100,26 @@ public function delete( string $file ): void {
);
}

public function delete_all(): void {
global $wpdb;
assert($wpdb instanceof wpdb);

if (! $this->table_exists()) {
return;
}
$wpdb->query(
$wpdb->prepare('TRUNCATE TABLE %i', $this->get_table_name())
);
}

public function get_all(): array {
global $wpdb;

if (! $this->table_exists()) {
return array();
}
return $wpdb->get_results(
$wpdb->prepare('SELECT file_path FROM %i', $this->get_table_name()),
$wpdb->prepare('SELECT file_path, detection, sha256 FROM %i', $this->get_table_name()),
ARRAY_A
);
}
Expand Down
3 changes: 2 additions & 1 deletion Infrastructure/Database/IFindingsQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace Gdatacyberdefenseag\GdataAntivirus\Infrastructure\Database;

interface IFindingsQuery extends IDatabase {
public function add( string $file ): void;
public function add( DetectedFile $file ): void;
public function delete( string $file ): void;
public function delete_all(): void;
public function get_all(): array;
public function table_exists(): bool;
public function count(): int;
Expand Down
52 changes: 49 additions & 3 deletions PluginPage/Findings/FindingsMenuPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function __construct(

add_action('admin_menu', array( $this, 'setup_menu' ));
add_action('admin_post_delete_findings', array( $this, 'delete_findings' ));
add_action('admin_post_reset_findings', array( $this, 'reset_findings' ));
}

public function setup_menu(): void {
Expand All @@ -55,6 +56,30 @@ public function validate_findings(): void {
$this->findings->validate();
}

public function reset_findings(): void {
$this->logger->debug('FindingsMenuPage::reset_findings');
if (! isset($_POST['gdata-antivirus-reset-findings-nonce'])) {
wp_die(
esc_html__('Invalid nonce specified', 'gdata-antivirus'),
esc_html__('Error', 'gdata-antivirus'),
array(
'response' => intval(403),
)
);
}
if (! wp_verify_nonce(sanitize_key($_POST['gdata-antivirus-reset-findings-nonce']), 'gdata-antivirus-reset-findings')) {
wp_die(
esc_html__('Invalid nonce specified', 'gdata-antivirus'),
esc_html__('Error', 'gdata-antivirus'),
array(
'response' => intval(403),
)
);
}
$this->findings->delete_all();
wp_redirect(admin_url());
}

public function delete_findings(): void {
$this->logger->debug('FindingsMenuPage::delete_findings');
if (! isset($_POST['gdata-antivirus-delete-findings-nonce'])) {
Expand Down Expand Up @@ -107,9 +132,15 @@ public function findings_list(): void {
<thead>
<tr>
<td id="cb" class="manage-column column-cb check-column"><label class="screen-reader-text" for="cb-select-all-1">Select All</label><input id="cb-select-all-1" type="checkbox"></td>
<th scope="col" id="title" class="manage-column column-title column-primary">
<th scope="col" id="title_file" class="manage-column column-title column-primary">
File
</th>
<th scope="col" id="title_detection" class="manage-column column-title column-primary">
Detection
</th>
<th scope="col" id="title_sha256" class="manage-column column-title column-primary">
Sha256
</th>
</tr>
</thead>

Expand All @@ -134,6 +165,16 @@ public function findings_list(): void {
echo esc_html($finding['file_path']);
?>
</td>
<td>
<?php
echo esc_html($finding['detection']);
?>
</td>
<td>
<?php
echo esc_html($finding['sha256']);
?>
</td>
</tr>
<?php
}
Expand All @@ -143,9 +184,14 @@ public function findings_list(): void {
</tbody>
</table>

<input type="hidden" name="action" value="delete_findings">
<?php wp_nonce_field('gdata-antivirus-delete-findings', 'gdata-antivirus-delete-findings-nonce'); ?>
<?php submit_button(__('Remove Files', 'gdata-antivirus')); ?>
<?php submit_button(__('Remove Files', 'gdata-antivirus'), 'primary', 'delete_findings', true, Array(
'formaction' => 'admin-post.php?action=delete_findings'
)); ?>
<?php wp_nonce_field('gdata-antivirus-reset-findings', 'gdata-antivirus-reset-findings-nonce'); ?>
<?php submit_button(__('Reset', 'gdata-antivirus'), 'primary', 'reset_findings', true, Array(
'formaction' => 'admin-post.php?action=reset_findings'
)); ?>
</form>

<?php
Expand Down
10 changes: 8 additions & 2 deletions PluginPage/FullScan/FullScanMenuPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Gdatacyberdefenseag\GdataAntivirus\PluginPage\FullScan;

use Gdatacyberdefenseag\GdataAntivirus\Infrastructure\Database\DetectedFile;
use Gdatacyberdefenseag\GdataAntivirus\Infrastructure\Database\IFindingsQuery;
use Gdatacyberdefenseag\GdataAntivirus\Infrastructure\Database\IScansQuery;
use Gdatacyberdefenseag\GdataAntivirus\PluginPage\AdminNotices;
Expand Down Expand Up @@ -245,6 +246,10 @@ public function full_scan(): void {
if ($file_path->isDir()) {
continue;
}
// For testing purposes, we only scan files with eicar in the name
// if (str_contains($file_path->getPathname(), "eicar") === false) {
// continue;
// }
$this->logger->debug($file_path->getPathname());
array_push($files, $file_path->getPathname());
if (count($files) >= $batch_size) {
Expand Down Expand Up @@ -274,9 +279,10 @@ public function scan_batch( array $files ): void {
continue;
}
$scan_client = $this->scan_client;
if ($scan_client->scan_file($file) === \VaasSdk\Message\Verdict::MALICIOUS) {
unglaublicherdude marked this conversation as resolved.
Show resolved Hide resolved
$vaas_verdict = $scan_client->scan_file($file);
if ($vaas_verdict->Verdict === \VaasSdk\Message\Verdict::MALICIOUS) {
$this->logger->debug('add to findings ' . $file);
$this->findings->add($file);
$this->findings->add(new DetectedFile($file, $vaas_verdict->Detection, $vaas_verdict->Sha256));
}
}
} finally {
Expand Down
23 changes: 23 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ The devcontainer is configured to mount the source code into the containers. Thi

Within the devcontainer it starts a wordpress-environment with `docker composer` within that you can even debug the code running directly in wordpress.

### How to rebuild within the container

When you change code, it is not instantly put into the container because the directory that is actually mounted is the scoped-code directory.
When starting something bigger you can just set the mount-point in the ./compose.yml and the `/var/www/html/wp-content/plugins/gdata-antivirus` in the ./.vscode/launch.json to the working directory meaning `.` or full path `/workspaces/wordpress-gdata-antivirus`.

Doing this, you still have to test your changes with the scoped code, so basically reset your changes and rebuild the container.

To avoid rebuilding the container on every change you can also just run the ./.devcontainer/configureWordPress.sh script with the simple `source .devcontainer/configureWordPress.sh` command. This will run the scoper and restart the composed containers.

### Switch to live development mode

To switch to a mode, where your changes are directly affecting the running wordpress container we provide the switch-live-develop-mode.sh script. Running this the first time will change the .vscode/launch.json and compose.yml files, so that the code in the root folder is directly mounted into the container and the debugger also points to this code (if you have a debugger running you have to restart it once).

When running this script within a running container, you have to run `source .devcontainer/configureWordPress.sh` once to start live mode and once when you switch back to scoped mode.

Please do not commit the code while in live mode. Just run the script again and it will reset these changes.

### Running the cron

If you want to run the cron event directly user this command.

`docker exec --user www-data -it gdata-antivirus-app-1 bash -c "XDEBUG_CONFIG='client_port=9080 client_host=172.19.0.1' wp --debug cron event run gdatacyberdefenseag_antivirus_scan_batch"`

## Disclaimer

While this plugin enhances the security of your WordPress installation, no security measure is foolproof. Regular backups and other security best practices are still recommended to ensure the safety of your website.
Expand Down
5 changes: 5 additions & 0 deletions Readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ While the released code is hosted on the WordPress svn, we develop the plugin on

== Changelog ==

= 2.0.9 =
* bugfix: reconnect on long running scans
* add detection and sha256 name to upload detection
* add detection and sha256 to the findings page

= 2.0.8 =
* bugfix: posts could not be saved

Expand Down
57 changes: 24 additions & 33 deletions Vaas/ScanClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
use VaasSdk\Vaas;
use VaasSdk\Authentication\ClientCredentialsGrantAuthenticator;
use VaasSdk\Authentication\ResourceOwnerPasswordGrantAuthenticator;
use VaasSdk\Exceptions\VaasInvalidStateException;
use VaasSdk\Message\Verdict;
use VaasSdk\Message\VaasVerdict;
use VaasSdk\Message\VerdictResponse;
use VaasSdk\VaasOptions as VaasParameters;

if (! class_exists('ScanClient')) {
Expand Down Expand Up @@ -102,26 +102,22 @@ public function scan_post( $data ) {

$this->connect();
try {
$verdict = $this->vaas->ForStream($stream);
} catch (VaasInvalidStateException $e) {
$vaas_verdict = $this->vaas->ForStream($stream);
} catch (\Exception $e) {
try {
$this->reconnect();
$verdict = $this->vaas->ForStream($stream);
$vaas_verdict = $this->vaas->ForStream($stream);
} catch (\Exception $e) {
$this->admin_notices->add_notice(esc_html__('virus scan failed', 'gdata-antivirus'));
$this->logger->debug($e->getMessage());
return $data;
}
} catch (\Exception $e) {
$this->admin_notices->add_notice(esc_html__('virus scan failed', 'gdata-antivirus'));
$this->logger->debug($e->getMessage());
return $data;
}
$this->logger->debug(var_export($verdict, true));
$this->logger->debug(var_export($vaas_verdict->Verdict, true));
// phpcs:ignore
if (\VaasSdk\Message\Verdict::MALICIOUS === $verdict->Verdict) {
if (\VaasSdk\Message\Verdict::MALICIOUS === $vaas_verdict->Verdict) {
$this->logger->debug('gdata-antivirus: virus found in post');
wp_die(esc_html__('virus found', 'gdata-antivirus'));
wp_die(esc_html__("Virus found! - Detection: $vaas_verdict->Detection - SHA256: $vaas_verdict->Sha256 - Guid: $vaas_verdict->Guid", 'gdata-antivirus'));
}
return $data;
}
Expand Down Expand Up @@ -155,24 +151,21 @@ public function scan_comment( $commentdata ) {
$stream = $this->file_system->get_resource_stream_from_string($commend_content);
$this->connect();
try {
$verdict = $this->vaas->ForStream($stream);
} catch (VaasInvalidStateException $e) {
$vaas_verdict = $this->vaas->ForStream($stream);
} catch (\Exception $e) {
try {
$this->reconnect();
$verdict = $this->vaas->ForStream($stream);
$vaas_verdict = $this->vaas->ForStream($stream);
} catch (\Exception $e) {
$this->admin_notices->add_notice(esc_html__('virus scan failed', 'gdata-antivirus'));
$this->logger->debug($e->getMessage());
}
} catch (\Exception $e) {
$this->admin_notices->add_notice(esc_html__('virus scan failed', 'gdata-antivirus'));
$this->logger->debug($e->getMessage());
}
$this->logger->debug(var_export($verdict, true));
$this->logger->debug(var_export($vaas_verdict->Verdict, true));
// phpcs:ignore
if (\VaasSdk\Message\Verdict::MALICIOUS === $verdict->Verdict) {
if (\VaasSdk\Message\Verdict::MALICIOUS === $vaas_verdict->Verdict) {
$this->logger->debug('gdata-antivirus: virus found in comment');
wp_die(esc_html__('virus found', 'gdata-antivirus'));
wp_die(esc_html__("Virus found! - Detection: $vaas_verdict->Detection - SHA256: $vaas_verdict->Sha256 - Guid: $vaas_verdict->Guid", 'gdata-antivirus'));
}
return $commentdata;
}
Expand Down Expand Up @@ -211,33 +204,31 @@ public function scan_single_upload( $file ) {
}
}

$verdict = $this->scan_file($file['tmp_name']);
$vaas_verdict = $this->scan_file($file['tmp_name']);
$verdict = $vaas_verdict->Verdict;
if (\VaasSdk\Message\Verdict::MALICIOUS === $verdict) {
$file['error'] = __('virus found', 'gdata-antivirus');
$file['error'] = __("Virus found! - Detection: $vaas_verdict->Detection - SHA256: $vaas_verdict->Sha256 - Guid: $vaas_verdict->Guid", 'gdata-antivirus');
}
return $file;
}

public function scan_file( $file_path ): Verdict {
public function scan_file( $file_path ): VaasVerdict {
$this->connect();
try {
$verdict = $this->vaas->ForFile($file_path)->Verdict;
} catch (VaasInvalidStateException $e) {
$vaas_verdict = $this->vaas->ForFile($file_path);
} catch (\Exception $e) {
try {
$this->reconnect();
$verdict = $this->vaas->ForFile($file_path)->Verdict;
$vaas_verdict = $this->vaas->ForFile($file_path);
} catch (\Exception $e) {
$this->logger->debug($e->getMessage());
return Verdict::UNKNOWN;
return new VaasVerdict(new VerdictResponse);
}
} catch (\Exception $e) {
$this->logger->debug($e->getMessage());
return Verdict::UNKNOWN;
}
$this->logger->debug(
'gdata-antivirus: verdict for file ' . $file_path . ': ' . var_export($verdict, true)
'gdata-antivirus: verdict for file ' . $file_path . ': ' . var_export($vaas_verdict, true)
);
return $verdict;
return $vaas_verdict;
}
}
}
1 change: 1 addition & 0 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_TEST_DB_NAME: wordpress_test
WORDPRESS_DEBUG: true
volumes:
- ./scoped-code/:/var/www/html/wp-content/plugins/gdata-antivirus:ro,cached

Expand Down
Loading