Source of file ClamAVExtension.php
Size: 5,395 Bytes - Last Modified: 2021-12-23T10:21:01+00:00
/var/www/docs.ssmods.com/process/src/src/Extension/ClamAVExtension.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 | <?php namespace Symbiote\SteamedClams\Extension; use SilverStripe\Assets\File; use SilverStripe\Assets\Flysystem\ProtectedAssetAdapter; use SilverStripe\Assets\Folder; use SilverStripe\Core\Injector\Injector; use SilverStripe\ORM\DataExtension; use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\ValidationResult; use Symbiote\SteamedClams\ClamAV; use Symbiote\SteamedClams\Model\ClamAVScan; use SilverStripe\Core\Config\Config; use Silverstripe\SiteConfig\SiteConfig; /** * Class Symbiote\SteamedClams\ClamAVExtension * * @property File|ClamAVExtension $owner * @method DataList|ClamAVScan[] ClamAVScans() */ class ClamAVExtension extends DataExtension { /** * @var array */ private static $has_many = [ 'ClamAVScans' => ClamAVScan::class, ]; /** * @var ClamAVScan */ protected $_cache_scanForVirus = 0; /** * */ //public function updateCMSFields(FieldList $fields) { // todo(Jake): Show 'ClamAVScans' on AssetAdmin/File level. //} /** * This is called within `File::write()` but before `File::onBeforeWrite()`. * * @param ValidationResult $validationResult * * @return null * @throws \SilverStripe\ORM\ValidationException */ public function validate(ValidationResult $validationResult) { // If its a new file, scan it. $doVirusScan = ($this->owner->ID == 0); // Support VersionedFiles module // ie. If file has been replaced, scan it. $changedFields = $this->owner->getChangedFields(true, DataObject::CHANGE_VALUE); $currentVersionIDChanged = (isset($changedFields['CurrentVersionID'])) ? $changedFields['CurrentVersionID'] : []; if ($currentVersionIDChanged && $currentVersionIDChanged['before'] != $currentVersionIDChanged['after']) { $doVirusScan = true; } // NOTE(Jake): Perhaps add $this->extend('updateDoVirusScan'); so other modules can support this. // Skip scanning unless the *physical* file on disk/CDN/etc has changed if (!$doVirusScan) { return; } $record = $this->owner->scanForVirus(); if (!$record) { return; } $denyOnFailure = ClamAV::config()->get('deny_on_failure'); $denyUpload = ($record->IsInfected || ($denyOnFailure && !$record->IsScanned)); // todo(Jake): Allow for custom deny rules with virus scan and TEST. //$this->owner->extend('updateDeny', $denyUpload, $record, $validationResult); if (!$denyUpload) { // Add the scan/log if the file is clean / allowed $this->owner->ClamAVScans()->add($record); return; } $config = SiteConfig::current_site_config(); $validationMessage = ($config->ValidationMessage) ? $config->ValidationMessage : 'A virus was detected.'; $validationResult->addError( _t( 'ClamAV.VIRUS_DETECTED', $validationMessage ), 'VIRUS' ); // Delete infected file // (If file hasn't been written to DB yet) if ($this->owner->ID == 0) { $filepath = $this->owner->getFullPath(); if (file_exists($filepath)) { @unlink($filepath); } $record->Action = ClamAVScan::ACTION_DELETED; } // Write log of infection to DB // (as this File record will never be written due to failing // validation) if ($record && !$record->exists()) { $record->write(); } } /** * Returns an unsaved `ClamAVScan` record with information regarding the virus scan * * @return ClamAVScan */ public function scanForVirus() { if (!$this->isVirusScannable()) { return null; } return Injector::inst()->get(ClamAV::class)->scanFileRecordForVirus($this->owner); } /** * Whether the file can be scanned or not. * * @return boolean */ public function isVirusScannable() { if ($this->owner instanceof Folder) { return false; } // NOTE(Jake): Perhaps add $this->owner->extend() here? Maybe you want to avoid scanning // 2GB files or similar? But maybe we want a different function that works // like ::validate(). Too early to say. return true; } /** * Returns an absolute filesystem path to the file. * Use {@link getRelativePath()} to get the same path relative to the webroot. * * @return String */ public function getFullPath() { if (!isset($this->owner->File)) { return null; } $fileMetaData = $this->owner->File->getMetadata(); if ($this->owner->isPublished()) { return PUBLIC_PATH . $this->owner->File->getURL(); } else { return ASSETS_PATH . '/' . Config::inst()->get(ProtectedAssetAdapter::class, 'secure_folder') . '/' . $fileMetaData['path']; } } /** * @throws \SilverStripe\ORM\ValidationException */ public function onAfterDelete() { foreach ($this->owner->ClamAVScans() as $scan) { $scan->processFileActionDelete(); } } } |