Security: enforce postprocessing/antivirus quarantine on the storage Download path#596
Open
thistehneisen wants to merge 1 commit into
Open
Conversation
The processing/antivirus quarantine was only enforced by the ocdav GET/HEAD/PROPFIND handlers via a separate Stat call. Decomposedfs.Download and DownloadRevision did not check the node's processing state, so the datagateway /data endpoint and revision downloads could serve unscanned/in-process content and the ocdav path was racy (TOCTOU). - reject Download/DownloadRevision with errtypes.TooEarly while the node IsProcessing, enforcing the quarantine at the storage layer for every caller - map the too-early error to HTTP 425 in the data transfer handler
62c8394 to
c1cc13b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security: enforce postprocessing/antivirus quarantine in the storage Download path
Summary
The "still being processed / virus scan in progress" quarantine that protects
users from downloading unscanned or infected content is enforced only in
the ocdav HTTP handlers (
GET/HEAD/PROPFIND), and even there only via aseparate
Statround-trip performed before the data is fetched. Theauthoritative blob-serving layer —
Decomposedfs.DownloadandDecomposedfs.DownloadRevision— performs no processing-state check at all.As a result the antivirus / postprocessing gate can be bypassed:
Download/DownloadRevisionwithout going through the ocdav
GETstat-gate serves the blob with noquarantine check. This includes the
/datadatagateway endpoint (which isUnprotectedat the oCIS proxy and callsfs.Downloaddirectly) and anyrevision-style reference (
ResourceId.OpaqueIdcontaining the revisiondelimiter), which is routed straight into
DownloadRevision.Statresponse and the content is then fetched in a separateInitiateFileDownload+ data request, with noIf-Match/etag bindingbetween the two. The reva source already acknowledges this race in
internal/http/services/owncloud/ocdav/get.go(// TODO we need to send the If-Match etag in the GET to the datagateway to prevent race conditions between stating and reading the file).The correct fix is to enforce the quarantine at the resource boundary
(
Decomposedfs.Download/DownloadRevision), so every consumer (ocdav, thedatagateway
/datapath, the archiver, app-provider, OCM) is covered and theTOCTOU is closed.
Affected code
pkg/storage/utils/decomposedfs/decomposedfs.go—Download()resolves the node, checks per-user permissions, then calls
fs.tp.ReadBlob(n)with no check of the node's postprocessing/scanstatus.
pkg/storage/utils/decomposedfs/revisions.go—DownloadRevision()has the same gap.
internal/http/services/owncloud/ocdav/get.go— the only place thestatus == "processing"check exists, performed on a priorStatresult(separate request → TOCTOU; explicit
TODOacknowledging the race).services/proxy/pkg/config/defaults/defaultconfig.goroutes
/datawithUnprotected: true; the datagateway authorises with thetransfer token only and calls
fs.Downloadwith no processing gate(
pkg/rhttp/datatx/utils/download/download.go).Impact
configured antivirus / postprocessing pipeline has finished or while it is
reporting "processing", defeating the malware-quarantine guarantee. Combined
with a public link on the parent folder, distribution is unauthenticated.
/datavariants are raceless; the ocdav variant requires winning a short, known
window). No administrative privileges required.
control are affected. (Note: in a default oCIS install the antivirus and
policies services are opt-in and the postprocessing pipeline is empty — that
is a separate hardening concern tracked elsewhere; this PR fixes the control
for deployments that do enable it.)
CVSS:3.1 (proposed)
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N.Proposed fix
Enforce the postprocessing/scan quarantine in
Decomposedfs.DownloadandDecomposedfs.DownloadRevision, beforeReadBlob:postprocessing status that
node.AsResourceInfouses to annotatestatus: processingon the resource (the node scan/processing marker).(
errtypes.NotFound/ a dedicated "still processing" error consistent withthe existing ocdav
425 Too Earlybehaviour) instead of streaming the blob.Decomposedfs.Downloadto read the content under scan — the scanner streamsthe upload
.binvia the upload/TUS token path, not viaDownload, so thenew gate must not regress scanning. Add an explicit allowance only if a
legitimate internal-scanner code path is identified during review.
This makes the resource layer (not one HTTP caller) the authoritative
enforcement point and closes both the raceless and TOCTOU variants in a single
place. The ocdav
Stat-based pre-check can remain as a fast-path / nicerstatus code; the storage-layer check is the security boundary.
Test plan
Download/DownloadRevisionreturn the processing error when thenode is marked processing; succeed once the marker is cleared.
assert
GET /data..., a revision-reference download, and ocdavGETallreturn the quarantine error during processing and succeed afterwards.
unaffected); normal download after a clean verdict works.
before the verdict.
Nils Putnins / OffSeq Cybersecurity
npu@offseq.com / https://offseq.com / https://radar.offseq.com