Blog posts stay publicly live when their media fails the security scan #475

Closed
opened 2026-07-11 06:19:49 +00:00 by spikerj · 0 comments
Owner

Found while debugging why uploaded blog images weren't loading (staging files never promoted — the blog-media-processor couldn't reach ClamAV because its stack wasn't on the clamav overlay; fixed in spikersoft-infrastructure master).

Bug

Publication and scanning are fully decoupled:

  1. Adult accounts are auto-approved: the post is Published with PublishedAt set before the scan pipeline even starts.
  2. When a scan comes back dirty, BlogMediaOrchestrator.HandleScanCompletedAsync only marks the media record (SecurityScanStatus=Quarantined, ProcessingStatus=Failed). The post stays Published.
  3. The API serializes quarantined media out to clients like any other media (empty URL → broken image).
  4. The flagged file is left in /app/uploads/blogs forever — the blog pipeline has no quarantine directory, unlike the file-movement service.
  5. Because scanning fails closed, a ClamAV outage takes the same quarantine path as a real virus — so any post-level consequence would let an infra blip unpublish posts.

Fix

  • Scanner outage ≠ verdict: BlogMediaScanConsumer now throws on connection failures so the existing DLQ/retry machinery (5s/30s/300s, then DLQ with auto-retry) re-delivers the scan request. Media stays Pending; the post is untouched. Virus detected / scan-error verdicts / missing file remain fail-closed.
  • Quarantine unpublishes: on a definitive dirty verdict the orchestrator moves the post Published → PendingReview (clears PublishedAt, IsAutoApproved=false) and appends the scan verdict to ReviewNotes. Moderation flow already exists: approve republishes, reject discards.
  • Quarantined media is never served: BlogController filters media with SecurityScanStatus=Quarantined out of all responses.
  • Dirty files leave staging: moved to BlogMedia:QuarantinePath (/app/quarantine/blogs, prefixed with the workflow id). Requires the quarantine bind mount on the blog-media-processor stack (added in spikersoft-infrastructure master).
Found while debugging why uploaded blog images weren't loading (staging files never promoted — the blog-media-processor couldn't reach ClamAV because its stack wasn't on the `clamav` overlay; fixed in spikersoft-infrastructure master). ## Bug Publication and scanning are fully decoupled: 1. Adult accounts are auto-approved: the post is `Published` with `PublishedAt` set **before** the scan pipeline even starts. 2. When a scan comes back dirty, `BlogMediaOrchestrator.HandleScanCompletedAsync` only marks the media record (`SecurityScanStatus=Quarantined`, `ProcessingStatus=Failed`). The post stays `Published`. 3. The API serializes quarantined media out to clients like any other media (empty URL → broken image). 4. The flagged file is left in `/app/uploads/blogs` forever — the blog pipeline has no quarantine directory, unlike the file-movement service. 5. Because scanning fails closed, a ClamAV *outage* takes the same quarantine path as a real virus — so any post-level consequence would let an infra blip unpublish posts. ## Fix - **Scanner outage ≠ verdict:** `BlogMediaScanConsumer` now throws on connection failures so the existing DLQ/retry machinery (5s/30s/300s, then DLQ with auto-retry) re-delivers the scan request. Media stays Pending; the post is untouched. Virus detected / scan-error verdicts / missing file remain fail-closed. - **Quarantine unpublishes:** on a definitive dirty verdict the orchestrator moves the post `Published → PendingReview` (clears `PublishedAt`, `IsAutoApproved=false`) and appends the scan verdict to `ReviewNotes`. Moderation flow already exists: approve republishes, reject discards. - **Quarantined media is never served:** `BlogController` filters media with `SecurityScanStatus=Quarantined` out of all responses. - **Dirty files leave staging:** moved to `BlogMedia:QuarantinePath` (`/app/quarantine/blogs`, prefixed with the workflow id). Requires the quarantine bind mount on the blog-media-processor stack (added in spikersoft-infrastructure master).
Sign in to join this conversation.