mirror of
https://github.com/azaion/annotations.git
synced 2026-06-21 06:51:06 +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>
6.7 KiB
6.7 KiB
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, defaulthttps://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 legacyPOST /auth/refreshwas removed; callers refresh against admin'sPOST /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(insrc/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 requireADM; reads accept any authenticated user.
Known weakness
- No row-level / tenancy authorization is implemented. A user with policy
ANNcan 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.csboot viaConfigurationResolver.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_SECRETwas 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.EnsureSafeForEnvironmentrefuses to start inProductionwhen the allow-list is empty andCorsConfig:AllowAnyOriginis not explicitly set; aLogWarningis 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
FluentValidationor custom validator visible in code. - File upload validation: no MIME / extension whitelist visible at the
MediaControllerlayer. 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 inProgram.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 /annotationsandPOST /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
JwtBearermiddleware default 401s — not explicitly logged with extra detail. - No audit log of mutations is written today; the
annotations_queue_recordsoutbox + downstream stream IS the de-facto audit trail (post RB-01 + RB-09).
Observability boundary
/healthis 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
ADMor environment-conditional registration.
Error response surface
- All errors returned via
Middleware/ErrorHandlingMiddlewareuse 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. |