ADR 015 Malware Scanning on Intake - digitalspace/eagle-demi GitHub Wiki
Accepted 2026-10-14. The pipeline is in Document-Intake.
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.
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: trueandisEnabled: 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 foundis rejected. A file with no result after 6 hours is rejected.
- 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-testfor scan results was denied. The landing-zone policy assignmentDeny-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 theBlob.Write.WithTagHeaderssub-operations. Only the Owner grant onincomingalso deniesblobs/tags/write. - ClamAV on OpenShift stays in place for eagle-api. This decision does not change it.
- 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).
- Document-Intake — pipeline, settings, how to resume
- Azure-Environments — Defender state per account