Malware scanning

Two scanners, and nothing downloadable until a file is clean.

Every file, sent or received, is scanned for malware before anyone can download it. Nothing becomes downloadable until it has a clean result, or a result the tenant's policy has explicitly chosen to allow.

When scanning happens

A file is scanned once its upload completes, after a server-side check that the transfer is still open and the size matches what was declared. The same pass also inspects the file for sensitive data (see Data loss prevention).

Each file shows its state to the member (FR-16):

StateMeaning
UploadingParts are still arriving
ScanningThe malware scan and DLP inspection are running
CleanFully scanned, no finding
InfectedThe engine found malware. Quarantined for good.
UnscannableThe engine couldn't fully inspect it, for example a password-protected archive
Scan-failedThe engine kept failing with errors, so the file stays blocked

A file is shareable when it is clean, or unscannable and allowed by tenant policy, and its DLP inspection has finished without failing. Only shareable files can be downloaded. A download grant is created only for a shareable file, and each content request checks again. Storage backs this up: the API's read permission on the files bucket requires a tag that the scan worker sets when it records the result.

The bytes scanned are the bytes served. The scan reads the exact object version recorded at completion, and downloads serve that same version, so a file can't be swapped after its scan.

Two engines

ClamAV can't scan files larger than 2 GB, and files can be up to 5 GB (FR-2). So scanning is a hybrid, chosen by the file's size (ADR-0019):

File sizeEngineWhere it runs
Up to 2,000,000,000 bytesClamAV (clamd)Scan pods in the service's cluster, with separate pools for files under and over 100 MB so large files don't delay small ones
Above 2,000,000,000 bytesAmazon GuardDuty Malware Protection for S3Inside AWS, on a dedicated files bucket that the protection plan covers whole

The boundary is set in bytes, below ClamAV's 2 GiB limit, so no file can fall between the two engines. A file's size is declared at upload start and checked at completion, and it decides the bucket the file goes to.

Why not GuardDuty for everything: at design load the capacity estimate puts the hybrid at about $956 a month against about $3,860, and ClamAV's results can be tested in CI. The cost is two engines and two result paths to maintain.

One result mapping for both engines. Only a fully inspected file with no finding is clean. A finding is infected. Encrypted content and every exceeded limit (size, nesting, file count, extracted bytes, compression ratio) is unscannable. An engine error or timeout is retried, then becomes scan-failed. A limit is never a pass.

Engine isolation

Malware scanners parse hostile files, so clamd runs in its own pods with no credentials: no service account token, no AWS identity, a read-only root file system, connections accepted only from the scan workers, and outbound access only to the signature mirror. An exploited engine has nothing to authenticate with and can't reach the database or storage. An alert fires when the engine's signatures are more than 24 hours old. Each tenant is capped on concurrent scans, so one tenant's burst of uploads can't hold up everyone else.

Unscannable files and tenant policy

Files the engine can't fully inspect, such as password-protected archives, encrypted documents and archives past the nesting, file-count or compression-ratio limits, are blocked by default (FR-18). Decompression bombs stop at the engine's limits and end up here too, never as clean.

A tenant admin can change this in tenant policy (FR-23). If unscannable files are allowed, anyone who downloads one, recipient or member, sees a warning first. The policy is applied when the file's scan result is recorded, and a later policy change applies only to files scanned after it. To stop sharing a file that was allowed, revoke its transfer.

The policy never applies to scan failures (FR-19). An engine error is retried; if it keeps failing, the file stays blocked and an operator alert fires. A worker that dies mid-scan, for example out of memory, doesn't leave the file stuck: once its scan deadline passes, it is marked scan-failed.

Infected files are quarantined and can never be downloaded by anyone, including the sender, tenant admins and platform operators (FR-17). They are deleted with the rest of their transfer.

What the sender sees

  • Per-file state on the transfer page, from uploading through to the result.
  • No early notifications. Recipients are emailed only after every file in the transfer is shareable and the DLP decision allows it (FR-5).
  • A held transfer if any file isn't shareable: it is infected, its scan keeps failing, or it is unscannable and policy blocks it. No recipient is notified. The member sees which file failed and why, and can remove that file and send the rest, or cancel the transfer.
  • For upload requests, shareable files become available to the member, who is notified. Blocked files are reported to both the member and the uploader (FR-12).

What scanning doesn't cover

ClamAV and GuardDuty both match signatures; neither detonates files in a sandbox. Signature scanning can miss new and targeted malware. The threat model records this as an accepted residual risk (R-04), with a scheduled review and ADR-0019's triggers for revisiting the engine choice. Blocking unscannable files by default is the main control around it.

Source: PRD, ADR-0019, Architecture, Data model, Threat model