Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: CI

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
NUGET_XMLDOC_MODE: skip

jobs:
verify:
name: Build and test
runs-on: ubuntu-24.04
timeout-minutes: 30

steps:
- name: Check out repository
uses: actions/checkout@v7.0.1
with:
persist-credentials: false

- name: Set up .NET SDK
uses: actions/setup-dotnet@v6.0.0
with:
dotnet-version: 10.0.302

- name: Show .NET information
run: dotnet --info

- name: Restore dependencies
run: dotnet restore FileSentry.slnx

- name: Validate Docker Compose configuration
env:
POSTGRES_DB: filesentry_ci
POSTGRES_USER: filesentry_ci
POSTGRES_PASSWORD: ci-only-placeholder-not-a-secret
POSTGRES_PORT: 5432
CLAMAV_PORT: 3310
run: docker compose -f deploy/docker-compose.yml config --quiet

- name: Prepare integration test images
run: |
docker pull postgres:17
docker pull clamav/clamav:1.4_base

- name: Build solution
run: dotnet build FileSentry.slnx --configuration Release --no-restore

- name: Run automated tests
run: dotnet test FileSentry.slnx --configuration Release --no-build --no-restore

- name: Verify formatting
run: dotnet format FileSentry.slnx --verify-no-changes --no-restore

- name: Audit NuGet packages
run: dotnet package list --project FileSentry.slnx --vulnerable --include-transitive
108 changes: 46 additions & 62 deletions docs/ai/CURRENT_TASK.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,68 @@
# Current Task: Audit, Correlation, and Rate-Limit Hardening
# Current Task: GitHub Actions CI Pipeline

## Objective

Add durable security auditing, request/worker correlation, focused upload rate
limiting, and small operational security improvements without changing the core
file workflow.
Add a focused GitHub Actions workflow that proves FileSentry restores, builds, and
passes its complete automated verification from a clean checkout without developer
User Secrets or repository credentials.

The completed authentication, ingestion, scanner, file authorization, and health
behavior must remain intact.
The completed application behavior and existing Testcontainers architecture must
remain unchanged.

## Audit Scope
## Workflow Scope

Persist safe `AuditEvent` records for:
- Run for pull requests and pushes to `main`, with manual dispatch available.
- Grant read-only repository contents permission.
- Cancel superseded runs for the same workflow/ref.
- Use an Ubuntu hosted runner and a pinned .NET 10 SDK.
- Restore `FileSentry.slnx`.
- Validate Docker Compose configuration with explicit ephemeral test-only values.
- Pre-pull the PostgreSQL and ClamAV images already used by integration tests.
- Build the solution in Release configuration.
- Run the full unit and integration suite using the existing Testcontainers setup.
- Verify formatting and audit direct/transitive NuGet dependencies.

- upload accepted;
- scan started, clean, malware detected, and failed;
- file downloaded;
- file deleted.
## Dependency Strategy

Audit fields are limited to event ID, actor/file identifiers where applicable,
event type, UTC timestamp, correlation ID, controlled workflow states, a bounded
failure classification, and scan duration. Audit storage must never accept file
content, credentials, tokens, secrets, arbitrary client objects, or raw malware.
Integration tests continue to create disposable PostgreSQL and ClamAV containers;
the workflow must not create parallel service containers or depend on a checked-in
`.env`. PostgreSQL test passwords and JWT keys remain generated at test runtime.
Compose validation receives only non-secret, step-scoped placeholder values.

Audit writes participate in the same PostgreSQL unit of work as the associated
state change where feasible. A download is not returned unless its audit write
succeeds. Failures propagate and fail safely rather than silently dropping the
audit event.
## Security and Maintainability

## Correlation and Logging
- Use only stable official GitHub Actions required for checkout and SDK setup.
- Pin the SDK version and keep action versions explicit.
- Do not grant write permissions, consume repository secrets, or persist test
credentials.
- Do not deploy, publish images, create releases, or introduce cloud resources.
- Keep the workflow as one readable verification job unless an actual independent
job boundary is needed.

- Accept one syntactically safe `X-Correlation-ID` or generate a new identifier.
- Return the effective correlation ID and use it as the request trace identifier.
- Persist the ingestion correlation ID on the file record so worker scan events
and structured logs retain durable correlation after restarts.
- Log only structured file IDs, correlation IDs, state transitions, bounded scan
duration, and controlled failure codes; never log filenames, content, tokens,
passwords, keys, database secrets, or malware bytes.
## Verification

## Upload Rate Limit

Apply a startup-validated built-in fixed-window policy to authenticated
`POST /api/v1/files` requests. Partition authenticated uploads by user ID, keep the
existing IP-partitioned authentication limiter unchanged, and return stable
`ProblemDetails` code `UPLOAD_RATE_LIMITED` for rejected uploads.

## Operational Boundaries

- Storage remains outside the web root and is not static content.
- Trusted storage roots and all security-critical options fail validation clearly.
- ClamAV remains loopback-bound in the host development deployment.
- Do not add distributed rate limiting, Redis, OpenTelemetry, or unrelated
refactoring.

## Tests and Verification

Cover audit fields and safe schema, all required event types, request-to-worker
correlation, normal and excessive upload traffic, authentication precedence, and
continued authentication rate limiting. Run restore, Release build/tests, EF
migration list/update, formatting, diff checks, dependency audit, container health,
and inspect logs/audit rows for sensitive data.
Locally run restore, Release build, full tests, formatting verification, NuGet
vulnerability audit, Docker Compose config validation, and diff/status checks.
Inspect the workflow YAML and report hosted GitHub runner execution as unverified
until the workflow has actually run after push.

## Out of Scope

- CI or GitHub Actions;
- admin audit APIs or UI;
- public sharing, signed URLs, retention cleanup, dashboards, or OpenTelemetry;
- cloud storage, message brokers, or frontend work.
- deployment, Docker publishing, or GitHub releases;
- CodeQL or additional security platforms;
- cloud infrastructure, frontend work, or unrelated refactoring.

Do not create a commit or push.

## Verified Completion

Implemented and verified on 2026-08-14. Durable controlled-field audit events,
request and worker correlation, structured workflow logging, and authenticated
per-user upload rate limiting passed the PostgreSQL-backed integration suite. The
existing authentication limiter, ingestion, real ClamAV scanning, authorization,
downloads, deletes, and health behavior remain green.
Implemented and locally verified on 2026-08-14. The workflow uses one read-only
Ubuntu job, an exact .NET 10 SDK, the existing PostgreSQL and ClamAV Testcontainers
architecture, and step-scoped non-secret values for Docker Compose validation.

Release restore/build/tests, migration list/application, formatting, diff checks,
NuGet audit, and local PostgreSQL/ClamAV health checks completed successfully.
Restore, Release build, all 106 tests, formatting verification, NuGet vulnerability
audit, Docker Compose validation, and diff checks completed successfully locally.
The workflow was inspected, but its first execution on a GitHub-hosted runner remains
unverified until the branch is pushed.

Recommended next milestone: `feat/ci-pipeline`.
Recommended next milestone: `docs/release-hardening`.
8 changes: 5 additions & 3 deletions docs/ai/PROJECT_CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The primary design rule is:
| Background processing | ASP.NET Core `BackgroundService` with durable database state |
| Testing | xUnit, WebApplicationFactory, and Testcontainers |
| Local infrastructure | Docker Compose |
| CI | GitHub Actions (planned) |
| CI | GitHub Actions |

## Solution Layout

Expand Down Expand Up @@ -184,8 +184,10 @@ states, durations, and failure codes without filenames or content. Storage remai
outside static web content, and the host-development ClamAV mapping remains
loopback-only.

CI remains unimplemented. The completed hardening milestone and recommended next
milestone are recorded in `CURRENT_TASK.md`.
GitHub Actions CI restores, builds, runs the full Testcontainers-backed test suite,
verifies formatting, audits NuGet dependencies, and validates Docker Compose from
a clean Ubuntu runner. The workflow uses an exact .NET 10 SDK, read-only repository
permissions, no repository secrets, and ephemeral test-only infrastructure values.

## Explicit Non-Goals for the Initial Release

Expand Down
7 changes: 7 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sdk": {
"version": "10.0.302",
"rollForward": "disable",
"allowPrerelease": false
}
}