Step 5 of autodev existing-code flow. Epic AZ-563 plus 11 atomic tasks covering all 67 test scenarios from _docs/02_document/tests/* exactly once: - AZ-564 test infrastructure (xUnit + Docker + mock JWKS + dataseed) - AZ-565..568 functional positive (FT-P-01..22) - AZ-569..570 functional negative (FT-N-01..16) - AZ-571 security (NFT-SEC-01..10) - AZ-572 resilience (NFT-RES-01..06) - AZ-573 resource limits (NFT-RES-LIM-01..06) - AZ-574 performance (NFT-PERF-*) _dependencies_table.md records the cross-check vs traceability matrix (22 + 16 + 29 = 67 scenarios, no overlaps, no gaps; deferred items remain deferred per matrix). All task headers carry their Jira IDs (tracker: jira). Autodev state advanced to Step 6 (Implement Tests). Co-authored-by: Cursor <cursoragent@cursor.com>
3.0 KiB
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 ownSutRestartFixture. - 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).