- Dikirim
- 4 Agustus 2026 pukul 03.50 UTC
- Penulis
- Kamo
- Commit
- b111065
The document vault accepted arbitrary uploads with no malware scanning anywhere on the platform, and it is multi-tenant — a file one org uploads is opened by another org's users through the viewer, the WOPI editor and the binder. ClamAV over INSTREAM, spoken directly to clamd (no client library; the protocol is a socket and a length prefix). The gate runs in ImagingUploadController on /upload and /templates/upload, before the member lookup and before any bytes reach MinIO — scanning after storage leaves the object retrievable for the width of the race. Scanner-unreachable behaviour is FAIL OPEN by default, and that is a decision rather than an accident. clamd is not deployed yet: this is the application half, and the deployment is a separate operational act (KlusterServices auto-applies on push and is not this change's to touch). Shipping fail-closed would take document upload down for every tenant the moment the image rolls, with no staging in which that would have been caught. Fail-open here is not "no protection", it is the status quo of the last several years, now logged at ERROR every time it happens. Every knob — including fail-closed — lives in kamowsconversion-config, so tightening it once clamd is stable is a configmap edit and a restart, not a release. A circuit breaker bounds the cost of an absent scanner: after 3 consecutive failures the guard stops dialling for 60s, so uploads do not each pay a connect timeout for a control that is effectively off. On detection: 422 naming the signature (not a 5xx — retrying will not help), the bytes are preserved in a dedicated quarantine bucket rather than dropped, and the rejection is recorded through PhiAccessRecorder. The audit row carries identifiers only; the uploaded filename stays out of it because filenames here routinely contain borrower names. PhiAccessKind has no UPLOAD member and adding one would alter an @Enumerated(STRING) column's constraint in the shared library, so DOWNLOAD + permitted=false is used and the reason is documented at the call site. Also switches this service's PHI trail to the durable phi_access_log writer. The matching com.kamo.z.shared.phi.audit entry is in the same commit's @EnableJpaRepositories list, and PhiAuditRepositoryScanTest fails the build if the two ever drift — that pairing is what took every login down on 2026-08-03. Retro-scan (POST **************** sweeps documents already in the vault, tenant-scoped and paged. It reports and preserves, never blocks or deletes: making a stored object unretrievable needs a persisted quarantine flag, i.e. a shared-library column applied by a hand-run initializer while service CI builds against main, and deleting on a false positive would destroy a tenant's record with no recovery. Tests: 36 new. Pre-existing **************** at_or_below_1080 still fails on a clean tree; untouched. Not covered, deliberately: RecordingIngestController (internal-auth Asterisk push) and DocsService's WOPI PutFile are second ingest paths that want the same gate, but the scanner lives in this service and WOPI has no *** token to attribute an audit row to. Both are follow-ups, not oversights.