From aedc383fd86d94e213512b0a5911c5daf76fe1b1 Mon Sep 17 00:00:00 2001 From: keremy321 Date: Fri, 14 Aug 2026 15:55:32 +0300 Subject: [PATCH 1/2] chore: finalize release readiness --- docs/PROJECT_PLAN.md | 38 ++++++++++--------- docs/ai/CURRENT_TASK.md | 75 ++++++++++++++++---------------------- docs/ai/PROJECT_CONTEXT.md | 8 ++-- 3 files changed, 56 insertions(+), 65 deletions(-) diff --git a/docs/PROJECT_PLAN.md b/docs/PROJECT_PLAN.md index 61f9a21..002f031 100644 --- a/docs/PROJECT_PLAN.md +++ b/docs/PROJECT_PLAN.md @@ -3,9 +3,9 @@ FileSentry is a secure file-ingestion and malware-scanning API built with ASP.NET Core, PostgreSQL, ClamAV, and Docker. -**Status:** MVP behavior, security hardening, automated tests, and GitHub Actions CI -are implemented. Final local verification is complete; release blockers are recorded -in `docs/ai/CURRENT_TASK.md`. +**Status:** The MVP, local release verification, and hosted default-branch CI are +complete. The remaining pre-tag repository check is recorded in +`docs/ai/CURRENT_TASK.md`. **Architecture:** .NET 10 modular monolith with a database-backed scanner worker. @@ -151,12 +151,12 @@ grows. 4. Owner-authorized metadata, download, and delete operations. 5. Audit, correlation, rate limiting, operational hardening, and CI. -### Current phase: final verification +### Current phase: release readiness - repeat the full release checklist from a clean worktree/clone; - verify migrations and the documented manual API flow; - inspect dependency, secret, runtime-artifact, and configuration hygiene; -- confirm the default-branch hosted CI result and record remaining limitations; +- record the successful default-branch hosted CI result and remaining limitations; - decide separately whether to tag or publish a release. Local verification results and release blockers are recorded in @@ -169,8 +169,8 @@ Local verification results and release blockers are recorded in - [x] Clean and infected content follows the correct storage lifecycle. - [x] Metadata, download, and delete enforce owner predicates. - [x] Audit, correlation, and focused rate limits are implemented. -- [ ] The current default-branch GitHub Actions run is independently observed green; - the secret-free workflow is present and the complete suite passes locally. +- [x] The default-branch GitHub Actions run is observed green; the secret-free + workflow and complete local suite pass. - [x] Storage, `.env`, User Secrets, build output, and malware samples are excluded from source control. - [x] MIT license is present. @@ -182,21 +182,23 @@ Local verification results and release blockers are recorded in These requirements are retained for final verification; an item is not complete merely because an implementation or document claims it is. -- [ ] Core acceptance criteria pass. -- [ ] Release build succeeds without warnings selected as errors. -- [ ] Unit and integration test results are recorded. -- [ ] GitHub Actions is green on the default branch. -- [ ] Docker images run as non-root where feasible. -- [ ] ClamAV is internal-only. -- [ ] Database migrations are reproducible. -- [ ] `.env.example` and configuration documentation are complete. -- [ ] Threat model reflects the implemented behavior. -- [ ] README includes architecture, setup, API examples, security controls, and +- [x] Core acceptance criteria pass. +- [x] Release build succeeds without warnings selected as errors. +- [x] Unit and integration test results are recorded. +- [x] GitHub Actions is green on the default branch. +- [x] Docker images run as non-root where feasible. +- [x] ClamAV is internal-only. +- [x] Database migrations are reproducible. +- [x] `.env.example` and configuration documentation are complete. +- [x] Threat model reflects the implemented behavior. +- [x] README includes architecture, setup, API examples, security controls, and limitations. - [ ] Repository has a license, description, and relevant GitHub topics. -- [ ] Demo video is linked. - [ ] Tagged release is created. +A recorded demo video is intentionally outside the release scope. The written +[`demo.md`](demo.md) guide remains the repeatable demonstration artifact. + ## Limitations - A ClamAV clean result is not proof that content is harmless. diff --git a/docs/ai/CURRENT_TASK.md b/docs/ai/CURRENT_TASK.md index 3ca6e7a..44b118f 100644 --- a/docs/ai/CURRENT_TASK.md +++ b/docs/ai/CURRENT_TASK.md @@ -1,15 +1,15 @@ -# Current Task: Final MVP Verification +# Current Task: Final Release Readiness ## Outcome -Final local verification completed on 2026-08-14 from an isolated clone of commit -`8bf1218`. FileSentry is **not yet ready to tag as `v1.0.0`**: the demo video -required by the Definition of Done is not linked, and the current default-branch -GitHub Actions result plus remote repository description/topics were not observable -from this environment. +The FileSentry MVP's technical acceptance criteria are complete. Local final +verification passed, and GitHub Actions has now passed on the merged default branch. +A demo video will deliberately not be created and is no longer a release +requirement; [`docs/demo.md`](../demo.md) remains the written repeatable guide. -No application feature, commit, push, tag, release, or remote setting was created -or changed. +FileSentry is **not yet ready to tag as `v1.0.0`** because the remote repository +description and relevant GitHub topics have not been verified. No application +feature, commit, push, tag, release, or remote setting was created or changed. ## Acceptance criteria @@ -20,57 +20,46 @@ or changed. | 3 | PASS | A live clean PDF was promoted and downloaded byte-for-byte; a valid PDF stream containing runtime-assembled canonical EICAR became `Infected`, storage state `Deleted`, with no bytes under clean or quarantine. | | 4 | PASS | Owner metadata/download/delete worked; a second authenticated user received identical `404` results for metadata, download, and delete. PostgreSQL-backed endpoint tests passed. | | 5 | PASS | Live audit rows recorded upload/scan/clean/malware/failure/download/delete with file, actor where applicable, and persisted correlation IDs. Deterministic limits returned `AUTH_RATE_LIMITED` and `UPLOAD_RATE_LIMITED`. | -| 6 | NOT VERIFIED | The least-privilege, secret-free workflow is present and all 106 tests pass locally, but the current hosted GitHub Actions result was not observable (`gh` unavailable and the unauthenticated repository API returned `404`). | +| 6 | PASS | The least-privilege, secret-free workflow passed successfully on merged `main`; the same complete 106-test suite passes locally. | | 7 | PASS | Tracked-file inspection found no `deploy/.env`, runtime `data`, build/test output, private keys, JWT-like values, or contiguous raw EICAR sample; ignore checks cover `.env` and runtime storage. | | 8 | PASS | `LICENSE` contains the MIT license. | | 9 | PASS | The documented setup was exercised in an isolated clone without inherited `.env`, storage, package cache, or developer configuration; restore/build/test/format/migrations/Compose and API startup succeeded. | -| 10 | NOT APPLICABLE | A tag or GitHub release is a later explicitly approved action and was prohibited for this milestone. | +| 10 | INCOMPLETE | No tag or GitHub release has been created; that remains a separate explicitly approved action. | ## Definition of Done | Item | Status | Evidence | |---|---|---| -| Core acceptance criteria pass | NOT VERIFIED | All locally testable core behavior passed; the hosted-CI acceptance criterion remains unverified. | -| Release build succeeds without warnings selected as errors | PASS | Isolated `Release` build completed with 0 warnings and 0 errors. | +| Core acceptance criteria pass | PASS | All technical acceptance criteria passed locally and hosted CI is green on the default branch. | +| Release build succeeds without warnings selected as errors | PASS | Release verification completed with 0 warnings and 0 errors. | | Unit and integration test results are recorded | PASS | 31 unit and 75 integration tests passed; 106 total, 0 failed, 0 skipped. | -| GitHub Actions is green on the default branch | NOT VERIFIED | Current hosted status was not accessible from this environment. | -| Docker images run as non-root where feasible | PASS | Runtime inspection showed PostgreSQL as UID 999 and `clamd`/`freshclam` as the unprivileged `_rpc` account; the upstream ClamAV init/tail helpers remain root. | +| GitHub Actions is green on the default branch | PASS | The hosted workflow passed successfully on merged `main`. | +| Docker images run as non-root where feasible | PASS | PostgreSQL ran as UID 999 and `clamd`/`freshclam` as the unprivileged `_rpc` account; upstream ClamAV init/tail helpers remain root. | | ClamAV is internal-only | PASS | Compose binds ClamAV only to `127.0.0.1`; live inspection confirmed the loopback binding. | | Database migrations are reproducible | PASS | All five migrations applied to fresh PostgreSQL; a repeat update reported the database current. | | `.env.example` and configuration documentation are complete | PASS | The example contains local placeholders only; README documents `.env`, User Secrets, validated settings, migrations, and startup. | | Threat model reflects implemented behavior | PASS | Threat, control, and residual-risk statements match inspected code and live results. | | README includes architecture, setup, API examples, security controls, and limitations | PASS | All required sections are present, including that a clean result is not proof of harmlessness. | -| Repository has a license, description, and relevant GitHub topics | NOT VERIFIED | MIT license is present; remote description and topics were not observable. | -| Demo video is linked | FAIL | `docs/demo.md` contains a repeatable script but no video link. | -| Tagged release is created | NOT APPLICABLE | Tagging/releasing was explicitly prohibited and belongs after release approval. | +| Repository has a license, description, and relevant GitHub topics | NOT VERIFIED | MIT license is present; remote description and topics remain unverified. | +| Tagged release is created | INCOMPLETE | No tag or release exists yet, as required for this pre-tag milestone. | -## Executed verification +The former demo-video item was intentionally removed from the Definition of Done. +No nonexistent video is claimed as complete or required for release. -- `dotnet tool restore`: restored `dotnet-ef` 10.0.11. -- `dotnet restore FileSentry.slnx`: passed from an empty isolated package cache. -- `dotnet build FileSentry.slnx --configuration Release`: passed, 0 warnings and - 0 errors. -- `dotnet test FileSentry.slnx --configuration Release`: passed 31 unit and 75 - integration tests (106 total), including real PostgreSQL and ClamAV containers. -- `dotnet format FileSentry.slnx --verify-no-changes`: passed. -- `dotnet ef migrations list --project src/FileSentry.Api`: listed all five - migrations; `dotnet ef database update --project src/FileSentry.Api` applied them - to fresh PostgreSQL and a repeat run was a successful no-op. -- `docker compose --env-file deploy/.env -f deploy/docker-compose.yml config - --quiet`: passed with an isolated ignored environment file. -- NuGet direct/transitive vulnerability audit: no vulnerable packages reported by - the configured sources. -- Fresh PostgreSQL and ClamAV containers became healthy; the API started from the - isolated clone and returned live/ready `200` under normal conditions. -- `git diff --check`, local Markdown-link validation, and final status/diff - inspection passed. +## Verification record -## Release blockers +- Isolated restore, zero-warning Release build, all 106 tests, formatting, + migrations, Compose validation, dependency health, and the NuGet vulnerability + audit passed during final verification. +- Clean, infected, retry-exhausted ClamAV-outage, ownership, deletion, + audit/correlation, and authentication/upload rate-limit flows passed. +- GitHub Actions passed on the merged default branch without repository secrets or + developer User Secrets. +- The release-readiness Release build passed with 0 warnings and 0 errors; all 106 + tests passed again. `git diff --check` passed after the documentation update. -1. Record an observable green GitHub Actions run for current `main`/release commit. -2. Add the required demo video link, or explicitly remove that requirement through - a deliberate project-plan decision. -3. Verify or configure the remote repository description and relevant topics. +## Remaining release blocker -After those items are resolved, repeat only the affected remote/documentation checks -and approve the separate `v1.0.0` tag/release action. +Verify or configure the GitHub repository description and relevant topics. Once +that repository-level item is complete, FileSentry is ready for a separately +approved `v1.0.0` tag/release action. diff --git a/docs/ai/PROJECT_CONTEXT.md b/docs/ai/PROJECT_CONTEXT.md index 84367ae..21b1ba4 100644 --- a/docs/ai/PROJECT_CONTEXT.md +++ b/docs/ai/PROJECT_CONTEXT.md @@ -188,10 +188,10 @@ User Secrets (or equivalent external configuration) for the database connection string and JWT signing key. Release documentation now lives in the README plus focused architecture, threat -model, demo, and project-plan files. The current hosted CI result and remote -description/topics were not observable during final verification, and the required -demo video is not linked. These are release blockers; no tag or GitHub release was -created. +model, demo, and project-plan files. GitHub Actions has passed on merged `main`. +A recorded demo video is intentionally outside project scope; `docs/demo.md` remains +the repeatable written guide. Remote repository description/topics remain +unverified, and no tag or GitHub release has been created. ## Limitations and explicit non-goals From da2420c8fd36cb522f251bd72c6e0954818887d9 Mon Sep 17 00:00:00 2001 From: keremy321 Date: Fri, 14 Aug 2026 16:18:23 +0300 Subject: [PATCH 2/2] fix: ClamAvClient test fix --- .../FileSentry.UnitTests/ClamAvClientTests.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/FileSentry.UnitTests/ClamAvClientTests.cs b/tests/FileSentry.UnitTests/ClamAvClientTests.cs index 486ccf5..b762577 100644 --- a/tests/FileSentry.UnitTests/ClamAvClientTests.cs +++ b/tests/FileSentry.UnitTests/ClamAvClientTests.cs @@ -199,26 +199,36 @@ private static async Task RunWithServerAsync( var listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); int port = ((IPEndPoint)listener.LocalEndpoint).Port; - Task serverTask = Task.Run(async () => + Task serverTask = RunServerAsync(); + + async Task RunServerAsync() { + Task? versionAcceptTask = null; using (TcpClient scanClient = await listener.AcceptTcpClientAsync()) await using (NetworkStream stream = scanClient.GetStream()) { byte[] received = await ReadInStreamContentAsync(stream); onContent?.Invoke(received); + if (versionResponse is not null) + { + // The client opens VERSION immediately after reading the scan response. + versionAcceptTask = listener.AcceptTcpClientAsync(); + } + await stream.WriteAsync(Encoding.ASCII.GetBytes(scanResponse)); } - if (versionResponse is not null) + if (versionResponse is not null && versionAcceptTask is not null) { - using TcpClient versionClient = await listener.AcceptTcpClientAsync(); + using TcpClient versionClient = await versionAcceptTask; await using NetworkStream stream = versionClient.GetStream(); byte[] command = new byte[VersionCommand.Length]; await stream.ReadExactlyAsync(command); Assert.Equal(VersionCommand, command); await stream.WriteAsync(Encoding.ASCII.GetBytes(versionResponse)); } - }); + } + var client = new ClamAvClient(Options.Create(CreateOptions(port))); ClamAvScanResult result = await client.ScanAsync(