# Security tests (NFT-SEC-01..10) **Task**: AZ-571 **Name**: Security tests **Description**: Implement xUnit tests for all 10 security scenarios: JWT signature mismatch, expired, cross-policy DATASET/ANN, anonymous-access denials, error envelope no-stack-leak, path traversal in image/thumbnail GETs, token claim tampering, CORS preflight, alg-confusion `alg=HS256` forgery. **Complexity**: 5 points **Dependencies**: AZ-564 (test infrastructure) **Component**: Blackbox Tests → Security **Tracker**: jira **Epic**: AZ-563 ## Scenarios Covered | Test ID | Source | What it asserts | |---------|--------|-----------------| | NFT-SEC-01 | `_docs/02_document/tests/security-tests.md` | JWT signed with key NOT in JWKS. HTTP 401. | | NFT-SEC-02 | same | JWT expired. HTTP 401. | | NFT-SEC-03 | same | DATASET token → `POST /annotations`. HTTP 403. | | NFT-SEC-04 | same | ANN token → `PUT /settings/*`. HTTP 403. | | NFT-SEC-05 | same | Anonymous access to non-public endpoints. Only `/health` is anonymous; everything else returns 401 without auth. | | NFT-SEC-06 | same | Error envelope under Production env mode does NOT leak stack traces. | | NFT-SEC-07 | same | Path traversal in image / thumbnail GET routes. `../etc/passwd` style payloads return 400/404, never 200 with foreign content. | | NFT-SEC-08 | same | Token claim modification (signature breaks). HTTP 401. | | NFT-SEC-09 | same | CORS preflight respects `CorsConfig:AllowedOrigins` allow-list under Production. | | NFT-SEC-10 | same | Algorithm confusion — token forged with `alg=HS256` using the published ES256 public key as the HMAC secret. HTTP 401. | ## System Under Test Boundary - HTTP only. - Token variants minted via `TokenMinter.MintToken(claim, overrides)`. - NFT-SEC-06 requires the SUT to be re-booted with `ASPNETCORE_ENVIRONMENT=Production` (and a Production-safe CORS config). This is a separate compose profile or test class with its own `SutRestartFixture`. - NFT-SEC-09 requires a second SUT boot under Production with `CorsConfig__AllowedOrigins__0: https://app.azaion.local`. Asserts ACAO is exactly that one origin. ## Acceptance Criteria **AC-1: Every scenario passes per its spec.** **AC-2: NFT-SEC-10 explicitly verifies algorithm pinning** Given a token forged with `alg=HS256` and the published ES256 public key as the HMAC secret, When the runner presents it to `POST /annotations`, Then HTTP 401 is returned and the error envelope contains "Bearer error=invalid_token" in `WWW-Authenticate`. **AC-3: NFT-SEC-06 verifies no stack leak** Given `ASPNETCORE_ENVIRONMENT=Production`, When a request triggers a 500-class error, Then the response body's error envelope contains only the safe error code and message — no `stackTrace`, no `innerException`, no file paths. ## Constraints - AAA pattern. - `[Trait("traces_to", "AC-F-50, AC-F-51, AC-F-52, SW-05, ENV-06")]` plus per-test specific traces. - Production-env tests run in a dedicated test class with its own fixture (no leak between Production and E2ETest boots).