Describe the bug
When downloading a folder as ZIP that contains at least one Office file subject to Secure View watermarking, the resulting archive is silently corrupted. The server responds 200 OK and begins streaming the ZIP, but SecureViewWrapper::checkFileAccess() throws a ForbiddenException mid-stream once a secured file is reached. Because the HTTP response is already committed at that point, no error is surfaced to the user — they receive a broken, unextractable archive.
This bug was discovered while working on #5577 and #5587.
To Reproduce
- Enable Secure View watermarking (any rule:
watermark_shareAll, tag-based, or group-based)
- Place at least one eligible Office file (
.docx, .odt, etc.) inside a folder alongside other files
- Select the folder in the Files UI and choose "Download"
- Attempt to extract the downloaded ZIP → extraction fails with a corrupt archive error
Expected behavior
Either the download is blocked before it starts with a clear user-visible error, or secured files are excluded from the archive with a human-readable explanation.
Screenshots
N/A
Server details
Operating system: any
Web server: any
Database: any
PHP version: any
Nextcloud version: any
Version of the richdocuments app: any
Version of Collabora Online: any
Configuration of the richdocuments app
Any configuration with watermark_enabled = yes and at least one watermark rule active.
Notes for implementors
Three fix strategies:
A — Block before ZIP creation (BeforeZipCreatedEvent listener): scan the folder tree and reject the download if any watermarked file is found. Main concern: fully recursive tree walk with per-file shouldWatermark() calls degenerates into one getTagIdsForObjects() DB query per file when tag-based rules are active — O(n) queries before a single byte is written. Requires batched tag lookups and a rule-level pre-flight check to be safe for large folders.
B — Graceful degradation during streaming: catch the ForbiddenException at the ZIP-assembly layer in core, skip the secured file, and inject a DOWNLOAD_INCOMPLETE.txt into the archive. User gets a usable partial ZIP with a clear explanation. Requires a Nextcloud core change.
C — Verify existing error surfacing: confirm whether Nextcloud core already checks BeforeZipCreatedEvent::getErrorMessage() and aborts with a user-visible response before any ZIP data is written. If so, strategy A (with the performance fixes above) may be sufficient.
Describe the bug
When downloading a folder as ZIP that contains at least one Office file subject to Secure View watermarking, the resulting archive is silently corrupted. The server responds
200 OKand begins streaming the ZIP, butSecureViewWrapper::checkFileAccess()throws aForbiddenExceptionmid-stream once a secured file is reached. Because the HTTP response is already committed at that point, no error is surfaced to the user — they receive a broken, unextractable archive.This bug was discovered while working on #5577 and #5587.
To Reproduce
watermark_shareAll, tag-based, or group-based).docx,.odt, etc.) inside a folder alongside other filesExpected behavior
Either the download is blocked before it starts with a clear user-visible error, or secured files are excluded from the archive with a human-readable explanation.
Screenshots
N/A
Server details
Operating system: any
Web server: any
Database: any
PHP version: any
Nextcloud version: any
Version of the richdocuments app: any
Version of Collabora Online: any
Configuration of the richdocuments app
Any configuration with
watermark_enabled = yesand at least one watermark rule active.Notes for implementors
Three fix strategies:
A — Block before ZIP creation (
BeforeZipCreatedEventlistener): scan the folder tree and reject the download if any watermarked file is found. Main concern: fully recursive tree walk with per-fileshouldWatermark()calls degenerates into onegetTagIdsForObjects()DB query per file when tag-based rules are active — O(n) queries before a single byte is written. Requires batched tag lookups and a rule-level pre-flight check to be safe for large folders.B — Graceful degradation during streaming: catch the
ForbiddenExceptionat the ZIP-assembly layer in core, skip the secured file, and inject aDOWNLOAD_INCOMPLETE.txtinto the archive. User gets a usable partial ZIP with a clear explanation. Requires a Nextcloud core change.C — Verify existing error surfacing: confirm whether Nextcloud core already checks
BeforeZipCreatedEvent::getErrorMessage()and aborts with a user-visible response before any ZIP data is written. If so, strategy A (with the performance fixes above) may be sufficient.