mirror of
https://github.com/azaion/annotations.git
synced 2026-06-21 11:01:07 +00:00
03f879206e
This commit captures everything produced during autodev existing-code Steps 1 (Document), 2 (Architecture Baseline Scan), and 3 (Test Spec), together with the targeted auth + CORS re-sync triggered on 2026-05-14 when codebase drift was detected at Step 4 entry. None of this work was previously committed. Step 1 (Document) — 50+ _docs/02_document/ files: problem, solution, architecture, system flows, glossary, module-layout, per-component specs (01..06), modules, deployment, diagrams, data model, FINAL report, verification log, discovery. Step 2 (Architecture Baseline) — architecture_compliance_baseline.md. Verdict PASS_WITH_WARNINGS (0 Critical, 0 High, 1 Medium, 2 Low). No High/Critical findings; auto-chained to Step 3 per existing-code flow. Step 3 (Test Spec) — _docs/02_document/tests/* (67 scenarios across blackbox, security, resilience, resource-limit, performance), plus e2e/docker-compose.test.yml, e2e/seed/run.sh, scripts/run-tests.sh, scripts/run-performance-tests.sh. Coverage 88% over the active scope (40 of 45 items covered, 6 RB-deferred, 5 documented-as-uncovered). Targeted auth + CORS re-sync — replaces the deleted in-house token issuer with a JWKS-verifier model. AuthController and TokenService removed; JwtExtensions switched from HS256 symmetric to ES256 over admin's JWKS. ConfigurationResolver and CorsConfigurationValidator added under src/Infrastructure/. ADR-002 and ADR-006 retired; SEC-01, SEC-02, SEC-03 marked Closed. One new testability risk recorded in architecture.md Open Risks Section 6 (JWKS HTTPS gating). Source changes: - src/Auth/JwtExtensions.cs (modified) — ES256, JWKS, alg pinning - src/Program.cs (modified) — DI wiring for ConfigurationResolver and CorsConfigurationValidator - src/Controllers/AuthController.cs (deleted) — no in-service issuance - src/Services/TokenService.cs (deleted) — same - src/Infrastructure/ConfigurationResolver.cs (new) - src/Infrastructure/CorsConfigurationValidator.cs (new) - .env.example (new) — required env var documentation - .gitignore (updated) Cross-repo coordination: _docs/cross-repo/flights_h1_h2_h3_change_spec captures the change-spec for downstream services that consumed the now deleted /auth endpoints. Co-authored-by: Cursor <cursoragent@cursor.com>
87 lines
6.7 KiB
Markdown
87 lines
6.7 KiB
Markdown
# Azaion.Annotations — Security approach (retrospective)
|
|
|
|
> Inventory of the **security mechanisms actually present in code today** + the gaps that the autodev existing-code Step 14 (Security Audit) will close. Evidence anchored to source files. ADR references point to `_docs/02_document/architecture.md`.
|
|
|
|
## Authentication
|
|
|
|
- **Mechanism**: JWT Bearer with **ES256 asymmetric signing**, verified against admin's JWKS endpoint (`JWT_JWKS_URL`, default `https://admin.azaion.com/.well-known/jwks.json`).
|
|
- **Token validator code**: `src/Auth/JwtExtensions.cs` (verifier only — annotations does not mint tokens).
|
|
- **Validation parameters**: `ValidateIssuer = true`, `ValidateAudience = true`, `ValidateLifetime = true`, `ValidateIssuerSigningKey = true`, `ValidAlgorithms = [SecurityAlgorithms.EcdsaSha256]`, `RequireSignedTokens = true`, `RequireExpirationTime = true`, `ClockSkew = 30s`.
|
|
- **Anonymous endpoints**: only `GET /health` — every other endpoint requires authentication. The legacy `POST /auth/refresh` was removed; callers refresh against admin's `POST /token/refresh`.
|
|
- **Token storage**: stateless. Refresh tokens live in admin's DB (revocation is enforced by admin); annotations does not persist any token material.
|
|
|
|
## Authorization
|
|
|
|
- **Policies declared**: `ANN`, `DATASET`, `ADM` (in `src/Auth/JwtExtensions.cs`).
|
|
- **Policy → controller mapping** (verified):
|
|
- `ANN`: `AnnotationsController`, `MediaController` (annotation lifecycle + media upload — what humans on the annotation UI need).
|
|
- `DATASET`: `DatasetController` (dataset exploration).
|
|
- `ADM`: planned `[ADM]` writes on `/classes` (RB-06) and mutating routes on `/settings/*`.
|
|
- Mixed `[Authorize]` (any authenticated): the read endpoints under `/settings/*`.
|
|
- **Per-action overrides**: writes inside `/settings/*` typically require `ADM`; reads accept any authenticated user.
|
|
|
|
### Known weakness
|
|
|
|
- No row-level / tenancy authorization is implemented. A user with policy `ANN` can read/mutate any annotation regardless of mission ownership. This is acceptable for the current single-tenant deployment but must be documented before any multi-tenant rollout.
|
|
|
|
## Secrets handling
|
|
|
|
- **Source**: env vars resolved at `Program.cs` boot via `ConfigurationResolver.ResolveRequiredOrThrow` (env var → `IConfiguration` → throw if missing).
|
|
- **Required in any environment** (no fallback — service refuses to start without them): `DATABASE_URL`, `JWT_ISSUER`, `JWT_AUDIENCE`, `JWT_JWKS_URL`.
|
|
- **Soft defaults**: `RABBITMQ_*` keep their development defaults; operators MUST still override for production.
|
|
- **No secret manager integration** in code today — secrets land via container env (suite-level orchestrator's responsibility).
|
|
- `JWT_SECRET` was removed: annotations holds no HMAC material and no longer issues tokens.
|
|
|
|
## Transport
|
|
|
|
- Inside the container: HTTP only (`ASPNETCORE_URLS=http://+:8080`).
|
|
- TLS termination: outside the container (suite-level reverse proxy / orchestrator).
|
|
- No HSTS or HTTPS-redirect middleware in `Program.cs`.
|
|
|
|
## CORS
|
|
|
|
- Configuration: config-driven allow-list via `CorsConfig:AllowedOrigins` (`Program.cs` + `Infrastructure/CorsConfigurationValidator.cs`).
|
|
- `CorsConfigurationValidator.EnsureSafeForEnvironment` refuses to start in `Production` when the allow-list is empty and `CorsConfig:AllowAnyOrigin` is not explicitly set; a `LogWarning` is emitted in lower environments when running with the permissive fallback so the drift is visible in logs.
|
|
|
|
## Input validation & sanitization
|
|
|
|
- **REST DTO binding**: ASP.NET model binding only — no `FluentValidation` or custom validator visible in code.
|
|
- **File upload validation**: no MIME / extension whitelist visible at the `MediaController` layer. Step 14 should confirm whether the upstream pipeline restricts upload format or whether the service itself needs to.
|
|
- **SQL**: all access via Linq2DB / parameterised queries — no raw string concatenation found.
|
|
- **YOLO label file write**: trusts caller-provided detections (class id, coordinates) — clamping / range checks would be a Step 14 candidate.
|
|
|
|
## Rate limiting / DoS
|
|
|
|
- **None present** — there is no rate-limiting middleware (`AddRateLimiter`, `UseRateLimiter`) registered in `Program.cs`.
|
|
- **Implicit limits**: SSE channel is unbounded; outbox table is unbounded; RabbitMQ stream is bounded by retention config (suite-level).
|
|
- Step 14 candidate: per-IP rate limit on `POST /annotations` and `POST /media`, since they accept image bytes and write to disk.
|
|
|
|
## Auditing & logging
|
|
|
|
- Console logger configured in `Program.cs` (default ASP.NET Core logging).
|
|
- Authentication failures: rely on `JwtBearer` middleware default 401s — not explicitly logged with extra detail.
|
|
- No audit log of mutations is written today; the `annotations_queue_records` outbox + downstream stream IS the de-facto audit trail (post RB-01 + RB-09).
|
|
|
|
## Observability boundary
|
|
|
|
- `/health` is the only pre-auth endpoint that reveals state. It returns 200/non-200 only — no version or DB info — so it is safe to expose to load balancers without auth.
|
|
- Swagger UI is mounted in all environments (ADR-005). It exposes the full controller surface but no secrets. Step 14 should consider gating it behind `ADM` or environment-conditional registration.
|
|
|
|
## Error response surface
|
|
|
|
- All errors returned via `Middleware/ErrorHandlingMiddleware` use the suite-standard envelope (`_docs/02_document/common-helpers/01_http-error-envelope.md`).
|
|
- Stack traces are not echoed back to the client in prod (verify the `IsDevelopment()` branch in the middleware during Step 14).
|
|
|
|
## Summary table — known security gaps to address in Step 14
|
|
|
|
| ID | Area | Gap | Status |
|
|
|----|------|-----|--------|
|
|
| SEC-01 | Auth | JWT issuer/audience not validated | **Closed** — `ValidateIssuer`/`ValidateAudience` enforced; `JWT_ISSUER` and `JWT_AUDIENCE` are required env vars. |
|
|
| SEC-02 | Secrets | Dev fallback for `JWT_SECRET` in source | **Closed** — `JWT_SECRET` removed; remaining required vars fail fast on startup via `ConfigurationResolver`. |
|
|
| SEC-03 | CORS | `AllowAnyOrigin` default | **Closed** — config-driven allow-list; `CorsConfigurationValidator` blocks empty list in `Production`. |
|
|
| SEC-04 | Surface | Swagger UI exposed in prod | Open — gate behind `ADM` or `Development` only. |
|
|
| SEC-05 | Upload | No MIME / extension whitelist on `/media` | Open — validate at controller before disk write. |
|
|
| SEC-06 | DoS | No rate limiting on hot write endpoints | Open — per-IP / per-user limiter on `POST /annotations`, `POST /media`. |
|
|
| SEC-07 | Tenancy | No row-level authorization | Open — document constraint; add mission-scoped check before multi-tenant rollout. |
|
|
| SEC-08 | Audit | No structured audit log | Open — use post-RB-01 lifecycle stream as the audit substrate; add structured fields. |
|