Files
annotations/_docs/00_problem/security_approach.md
T
Oleksandr Bezdieniezhnykh 03f879206e docs+src: complete Steps 1-3 outcomes + auth re-sync baseline
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>
2026-05-14 20:19:05 +03:00

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, 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 ClosedValidateIssuer/ValidateAudience enforced; JWT_ISSUER and JWT_AUDIENCE are required env vars.
SEC-02 Secrets Dev fallback for JWT_SECRET in source ClosedJWT_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.