Skip to content

ADR 015 Malware Scanning on Intake

Daniel Truong edited this page Oct 14, 2026 · 3 revisions

ADR-015: Malware scanning on intake

Status

Accepted 2026-10-14. The pipeline is in Document-Intake.

Context

Uploads today go to eagle-api on OpenShift, which scans them with ClamAV. The documents themselves live in the NRS object store, an external S3-compatible service. DEMI has no intake of its own, and the briefs ask for malware scanning on upload.

The BC Gov landing zone sets Defender for Storage on every subscription, with on-upload scanning on (BC-Gov-Azure-Landing-Zone). Each storage account can opt out. Defender scans blobs in Azure Storage only. It cannot scan the NRS object store.

Decision

DEMI scans uploads with Defender for Storage on-upload scanning, on one staging account per environment.

  • One scanned account. Browsers upload to demiin<env>. Defender scans each blob and writes the result to a blob index tag. A timer reads the tag and only a clean file moves on to the NRS object store. See Document-Intake.
  • Every other account opts out. Each DEMI storage module sets overrideSubscriptionLevelSettings: true and isEnabled: false, so those accounts are not scanned and not billed for it.
  • Defender is the only scanner. A file in the object store passed Defender. Nothing scans it again, and stored files are assumed safe.
  • Fail closed. Anything other than No threats found is rejected. A file with no result after 6 hours is rejected.

Consequences

  • Each environment pays about 10 USD a month for the scanned account, plus 0.15 USD per GB scanned.
  • A monthly cap limits scan spend: 5 GB on test, 25 GB on prod. At the cap, scanning stops and uploads are rejected until the month ends or the cap is raised.
  • Files over 2 GB are not scanned and are rejected.
  • Files already in the NRS object store, and anything written to it by another route, are not scanned.
  • The intake account needs a public blob endpoint, because the browser uploads to it. Access is limited by Entra auth, a 15-minute write-only SAS and the access rule in Document-Intake.
  • On 2026-10-14 the test deploy of an Event Grid topic demi-intake-test for scan results was denied. The landing-zone policy assignment Deny-PublicPaaSEndpoints (definition "Azure Event Grid topics should disable public network access", effect Deny) blocks a topic with public network access. Microsoft documents: "Event Grid topics that don't have public network access enabled, for example, those using private endpoints, aren't supported for scan-result delivery." The sweep reads the result from the blob index tag instead. A result reaches the row up to about 5 minutes after the scan.
  • The pipeline is built and parked as of 2026-10-14. The first test deploy failed on a full private endpoint subnet and on role conditions, and nothing is enabled on test or prod. See Status in Document-Intake.
  • A role assignment condition may only name actions its role grants. Storage Blob Data Contributor does not grant blobs/tags/write, so its conditions deny only the Blob.Write.WithTagHeaders sub-operations. Only the Owner grant on incoming also denies blobs/tags/write.
  • ClamAV on OpenShift stays in place for eagle-api. This decision does not change it.

Alternatives rejected

  • ClamAV self-hosted in Azure. It runs always on and costs about 31 to 53 USD a month per environment. A cold start takes minutes. DEMI would own the signature updates and the host.
  • Defender on every storage account. It scans files that never come from an upload. Each account costs about 0.63 CAD a day (Azure-Environments).

See also

DEMI Wiki

Home

Current system

Azure

Domain

ADRs

History

Related

Clone this wiki locally