Skip to content

Antivirus

Every uploaded version is scanned before its content is served. Scanning is fail-closed: content that has not been cleared is not delivered, in any form, including previews.

Settings live under the Antivirus configuration section. Administrative screens are under Antivirus in the administration section.

Antivirus:ScannerKind selects one of three backends.

Kind What it does Use for
Eicar Recognises only the EICAR test string. Everything else is reported clean. Development and testing.
ClamAv Submits content to a ClamAV daemon over TCP. Production, where you run your own scanner.
VirusTotal Looks the content hash up against the VirusTotal service. Production, where an external service is acceptable.
Setting Default Purpose
Antivirus:Host localhost ClamAV daemon host.
Antivirus:Port 3310 ClamAV daemon port.
Antivirus:TimeoutSeconds 30 Bound on a single scan.

Content is submitted to the daemon, so it never leaves your infrastructure. This is the right choice where content is sensitive.

Setting Default Purpose
Antivirus:VirusTotalBaseUrl https://www.virustotal.com/api/v3/ Service endpoint.
Antivirus:VirusTotalApiKey - Your API key.
Antivirus:VirusTotalUnknownPolicy FailClosed What to do with a hash the service has never seen.
Antivirus:VirusTotalCacheMinutes 10 How long a verdict is cached.

This backend submits a hash, not the file. Content is not uploaded, which makes it viable where uploading documents to a third party would not be.

The consequence is that a file nobody has ever submitted to VirusTotal has no verdict. VirusTotalUnknownPolicy decides what happens then:

  • FailClosed (default) - an unknown file is not cleared. Safe, but it means genuinely novel internal documents are blocked, which in a document management system is most of them.
  • TreatAsClean - an unknown file is allowed through. Usable, but it means the scanner only catches malware that is already publicly known.

Neither setting is comfortable. If most of your content is original internal material, ClamAV is the better fit.

The API key is stored encrypted, protected by the Data Protection key ring - which is one of the reasons that key ring is itself a credential. See Security hardening.

Verdicts are cached for VirusTotalCacheMinutes so that re-uploading the same content does not re-query the service.

The antivirus settings screen showing the scanner backend

Antivirus settings. The scanner kind selected here decides whether uploads are genuinely scanned.

Until a version is cleared, its content is not served. Downloads are refused and previews are not shown. The document is still listed, and its metadata is still visible - only the content is withheld.

This has a consequence worth planning for: if the scanner is unreachable, uploads stop being usable. That is the correct trade-off for a scanner, but it makes scanner availability part of the application’s availability. Do not point the configuration at a scanner and then leave it down.

Content that fails a scan is moved to the quarantine directory, resolved from Storage:QuarantineSubpath beneath the storage root. It is kept rather than deleted so it can be examined, and it is held outside the normal blob store so nothing can serve it by accident.

Antivirus > Failed lists versions whose scan did not complete. Two situations land here and they need different responses:

  • Infrastructure failures - the scanner was unreachable or timed out. Requeue them once the scanner is healthy.
  • Genuine detections - the content was found to be malicious. Do not requeue; investigate.

Requeue puts a version back in line to be scanned. Requeueing against a still-broken scanner simply refills the queue.

Individual documents can be rescanned by an administrator. This is useful after a signature database update, when content that was previously cleared may now be recognised.

Each document exposes its scan status, so a user seeing a refused download can tell whether it is pending, failed, or blocked. The status is also available through the API - see HTTP API.