nextcloud / files_antivirus

👾 Antivirus app for Nextcloud Files
https://apps.nextcloud.com/apps/files_antivirus
GNU Affero General Public License v3.0
84 stars 35 forks source link

Only scan user files #1 #201

Closed st3iny closed 3 years ago

st3iny commented 3 years ago

Second iteration (fix check if a file is inside the users home storage)

https://github.com/nextcloud/files_antivirus/blob/e9955478525509569c1a8810024a70af1193ff4b/lib/AvirWrapper.php#L77-L83

https://github.com/nextcloud/files_antivirus/blob/e888f598f93f9864c509f19e6f1a32d79186b033/lib/AvirWrapper.php#L97-L103

When AvirWrapper.shouldWrap returns true the storage wrapper for scanning a file is attached => the file will be scanned.

I assume the method should return true when a file is in the users home directory or moved into it. A chunk has a path like uploads/web-file-upload-a37afee81a3d38104ab8808b454e5590-1629448320149/0.ocTransferId741408323.part. AvirWrapper.shouldWrap return true for this path because strpos($path, '/files/') returns false and is loosely compared to 0 => evaluating to true.

Note: It sounds good to me to already scan chunks during upload and it also seems to work. Work means that the chunks are sent to clamav. However clamav does not detect a virus for chunks of an uploaded zip with a movie and eicar but in the assembled file. Also the behavior when one chunk is rejected is undefined.

First iteration (exclude certificate bundle from scanner)

Currently, the certificate bundles are scanned which can lead to an endless recursion when Kaspersky is used. I solved this by ignoring certificate bundles entirely.

  1. A new certificate is requested (e.g. by the app updater).
  2. New bundle is written and scanned.
  3. Upon finishing the scan, a post request is send to Kaspersky.
  4. The instantiated client for the request will try to load a certificate bundle (because the previous one is not yet finished due to it being stuck on scanning).
  5. A new bundle is created and then scanned.
  6. The client tries to create another bundle.
  7. To be continued ...

Feedback is highly appreciated as I'm not familiar with the code. Feel free to suggest a better solution.

Excerpt of the recursion on a live system:

#198 /var/www/html/nextcloud/apps/files_antivirus/lib/AvirWrapper.php(118): OCA\Files_Antivirus\Scanner\ScannerBase->completeAsyncScan()
#199 [internal function]: OCA\Files_Antivirus\AvirWrapper->OCA\Files_Antivirus\{closure}()
#200 /var/www/html/nextcloud/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php(121): call_user_func()
#201 [internal function]: Icewind\Streams\CallbackWrapper->stream_close()
#202 /var/www/html/nextcloud/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php(132): fclose()
#203 /var/www/html/nextcloud/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php(119): Icewind\Streams\Wrapper->stream_close()
#204 [internal function]: Icewind\Streams\CallbackWrapper->stream_close()
#205 /var/www/html/nextcloud/lib/private/Security/CertificateManager.php(157): fclose()
#206 /var/www/html/nextcloud/lib/private/Security/CertificateManager.php(235): OC\Security\CertificateManager->createCertificateBundle()
#207 /var/www/html/nextcloud/lib/private/Http/Client/Client.php(104): OC\Security\CertificateManager->getAbsoluteBundlePath()
#208 /var/www/html/nextcloud/lib/private/Http/Client/Client.php(75): OC\Http\Client\Client->getCertBundle()
#209 /var/www/html/nextcloud/lib/private/Http/Client/Client.php(299): OC\Http\Client\Client->buildRequestOptions()
#210 /var/www/html/nextcloud/apps/files_antivirus/lib/Scanner/ExternalKaspersky.php(74): OC\Http\Client\Client->post()
#211 /var/www/html/nextcloud/apps/files_antivirus/lib/Scanner/ExternalKaspersky.php(97): OCA\Files_Antivirus\Scanner\ExternalKaspersky->scanBuffer()
#212 /var/www/html/nextcloud/apps/files_antivirus/lib/Scanner/ScannerBase.php(145): OCA\Files_Antivirus\Scanner\ExternalKaspersky->shutdownScanner()
#213 /var/www/html/nextcloud/apps/files_antivirus/lib/AvirWrapper.php(118): OCA\Files_Antivirus\Scanner\ScannerBase->completeAsyncScan()
#214 [internal function]: OCA\Files_Antivirus\AvirWrapper->OCA\Files_Antivirus\{closure}()
#215 /var/www/html/nextcloud/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php(121): call_user_func()
#216 [internal function]: Icewind\Streams\CallbackWrapper->stream_close()
#217 /var/www/html/nextcloud/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php(132): fclose()
#218 /var/www/html/nextcloud/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php(119): Icewind\Streams\Wrapper->stream_close()
#219 [internal function]: Icewind\Streams\CallbackWrapper->stream_close()
#220 /var/www/html/nextcloud/lib/private/Security/CertificateManager.php(157): fclose()
#221 /var/www/html/nextcloud/lib/private/Security/CertificateManager.php(235): OC\Security\CertificateManager->createCertificateBundle()
#222 /var/www/html/nextcloud/lib/private/Http/Client/Client.php(104): OC\Security\CertificateManager->getAbsoluteBundlePath()
#223 /var/www/html/nextcloud/lib/private/Http/Client/Client.php(75): OC\Http\Client\Client->getCertBundle()
#224 /var/www/html/nextcloud/lib/private/Http/Client/Client.php(299): OC\Http\Client\Client->buildRequestOptions()
#225 /var/www/html/nextcloud/apps/files_antivirus/lib/Scanner/ExternalKaspersky.php(74): OC\Http\Client\Client->post()
#226 /var/www/html/nextcloud/apps/files_antivirus/lib/Scanner/ExternalKaspersky.php(97): OCA\Files_Antivirus\Scanner\ExternalKaspersky->scanBuffer()
#227 /var/www/html/nextcloud/apps/files_antivirus/lib/Scanner/ScannerBase.php(145): OCA\Files_Antivirus\Scanner\ExternalKaspersky->shutdownScanner()
#228 /var/www/html/nextcloud/apps/files_antivirus/lib/AvirWrapper.php(118): OCA\Files_Antivirus\Scanner\ScannerBase->completeAsyncScan()
kesselb commented 3 years ago

Investigated a case yesterday when files_antivirus (clamav via socket) slowdown the uploading of new files via web.

AvirWrapper.shouldWrap return true for uploads/web-file-upload-a37afee81a3d38104ab8808b454e5590-1629448320149/0.ocTransferId741408323.part.

strpos($path, '/files/') returns false the above path and is loosely compared to 0 => evaluating to true. I wonder if that might be related to your case. The user certificates are not stored in the users home directory afaik :shrug: :confused:

st3iny commented 3 years ago

Investigated a case yesterday when files_antivirus (clamav via socket) slowdown the uploading of new files via web.

AvirWrapper.shouldWrap return true for uploads/web-file-upload-a37afee81a3d38104ab8808b454e5590-1629448320149/0.ocTransferId741408323.part.

strpos($path, '/files/') returns false the above path and is loosely compared to 0 => evaluating to true. I wonder if that might be related to your case. The user certificates are not stored in the users home directory afaik shrug confused

Yeah we should fix that too. Replacing it with a strict comparison (===) should do it but is out of scope of this PR.

st3iny commented 3 years ago

I fixed the tests and removed redundant parentheses around the strpos(...) call.

kesselb commented 3 years ago

Yeah we should fix that too. Replacing it with a strict comparison (===) should do it but is out of scope of this PR.

If you change strpos($path, '/files/') == 0 to strpos($path, '/files/') === 0 it will not scan the certificate bundle to create because it's outside the users home directory. Sorry for not making that clear in my comment :see_no_evil:

st3iny commented 3 years ago

Yeah we should fix that too. Replacing it with a strict comparison (===) should do it but is out of scope of this PR.

If you change strpos($path, '/files/') == 0 to strpos($path, '/files/') === 0 it will not scan the certificate bundle to create because it's outside the users home directory. Sorry for not making that clear in my comment see_no_evil

Right! One single equals can have such consequences. :exploding_head:

kesselb commented 3 years ago

Thanks :+1:

I wrote down my findings about this problem and added some additional fixes: https://github.com/nextcloud/files_antivirus/pull/205