[uploads] FileProcessingService ctor does filesystem I/O → 500s blog-upload endpoints on unwritable path #423

Closed
opened 2026-07-05 22:37:10 +00:00 by spikerj · 1 comment
Owner

Verified finding (same anti-pattern as PR #103, sibling service)

FileProcessingService (blog media uploads) calls Directory.CreateDirectory(_uploadPath) in its constructor (FileProcessingService.cs:49). The service is DI-injected into blog media command handlers (AddMediaFilesToPost, CreateBlogPostWithFiles), so a constructor that performs filesystem I/O fails construction — and 500s every blog-upload endpoint that depends on it — if Blogs:StagingPath is unwritable/misconfigured (e.g. a container path like /app/uploads/blogs on a read-only host).

This is the same defect fixed for LessonVideoStagingService in spikersoft-backend#103, and it's the last remaining instance of the ctor-filesystem-I/O pattern (swept SpikerSoft.Business / SpikerSoft.Api: only these two services did I/O in a constructor; the rest do it inside command handlers, which is fine).

Redundant anyway

ProcessUploadedFileAsync already creates the directory lazily at the point of use:

if (!Directory.Exists(_uploadPath)) { Directory.CreateDirectory(_uploadPath); }

So the constructor call adds no value and only broadens the blast radius of a bad path.

Fix (in flight)

Remove the constructor's Directory.CreateDirectory. Construction now performs no filesystem I/O; only an actual upload fails if the path is genuinely unwritable. Adds a regression test asserting the ctor neither throws nor creates the directory. Build-clean + test green.

## Verified finding (same anti-pattern as PR #103, sibling service) `FileProcessingService` (blog media uploads) calls `Directory.CreateDirectory(_uploadPath)` in its **constructor** (`FileProcessingService.cs:49`). The service is DI-injected into blog media command handlers (`AddMediaFilesToPost`, `CreateBlogPostWithFiles`), so a constructor that performs filesystem I/O fails construction — and **500s every blog-upload endpoint that depends on it** — if `Blogs:StagingPath` is unwritable/misconfigured (e.g. a container path like `/app/uploads/blogs` on a read-only host). This is the same defect fixed for `LessonVideoStagingService` in spikersoft-backend#103, and it's **the last remaining instance** of the ctor-filesystem-I/O pattern (swept `SpikerSoft.Business` / `SpikerSoft.Api`: only these two services did I/O in a constructor; the rest do it inside command handlers, which is fine). ### Redundant anyway `ProcessUploadedFileAsync` already creates the directory lazily at the point of use: ```csharp if (!Directory.Exists(_uploadPath)) { Directory.CreateDirectory(_uploadPath); } ``` So the constructor call adds no value and only broadens the blast radius of a bad path. ## Fix (in flight) Remove the constructor's `Directory.CreateDirectory`. Construction now performs no filesystem I/O; only an actual upload fails if the path is genuinely unwritable. Adds a regression test asserting the ctor neither throws nor creates the directory. Build-clean + test green.
Author
Owner

Resolved in spikersoft-backend PR #105 (merged to master). Removed the redundant Directory.CreateDirectory from FileProcessingService's constructor (ProcessUploadedFileAsync already creates it lazily at point-of-use), so construction no longer performs filesystem I/O and a bad Blogs:StagingPath can't 500 blog-upload endpoints. Added FileProcessingServiceTests regression guard. This clears the last ctor-filesystem-I/O instance in the codebase. Closing.

Resolved in spikersoft-backend PR #105 (merged to `master`). Removed the redundant `Directory.CreateDirectory` from `FileProcessingService`'s constructor (`ProcessUploadedFileAsync` already creates it lazily at point-of-use), so construction no longer performs filesystem I/O and a bad `Blogs:StagingPath` can't 500 blog-upload endpoints. Added `FileProcessingServiceTests` regression guard. This clears the last ctor-filesystem-I/O instance in the codebase. Closing.
Sign in to join this conversation.