Files
missions/_docs/02_document/tests/security-tests.md
T
Oleksandr Bezdieniezhnykh 7025f4d075 refactor: enhance JWT authentication and CORS configuration
Updated JWT authentication to use configuration values instead of hardcoded secrets, improving security and flexibility. Enhanced CORS policy to conditionally allow origins based on configuration settings, with logging for permissive defaults. Updated README to reflect project renaming and clarify service context.
2026-05-14 19:48:25 +03:00

6.5 KiB

Security Tests

Status: produced by autodev /test-spec Phase 2 (2026-05-14). Naming: post-rename target. Security tests focus on the JWT bearer + Authz boundary defined in AC-5 and AC-9. Out-of-scope (suite-tracked): the iss / aud validation gap (AC-5.3, CMMC L2 row 3, AZ-487 / AZ-494) is documented but NOT enforced today. Tests assert today's behaviour (AC-5.3 returns 200) — when the suite-wide remediation lands, update NFT-SEC-04.


NFT-SEC-01: Missing Authorization header → 401

Summary: Verifies AC-5.4 — every protected endpoint rejects requests without an Authorization header. Traces to: AC-5.4

Steps:

Step Consumer Action Expected Response
1 GET /vehicles with no Authorization header 401
2 GET /missions with no Authorization header 401
3 GET /missions/{any}/waypoints with no Authorization header 401
4 POST /vehicles with no Authorization header + valid body 401 (no row written — verify via side-channel count unchanged)

Pass criteria: every protected endpoint returns 401; no DB side-effect.


NFT-SEC-02: Invalid signature → 401

Summary: Verifies AC-5.5 — token signed with a different secret is rejected. Traces to: AC-5.5

Steps:

Step Consumer Action Expected Response
1 Mint token T_bad with WRONG_SECRET=other-secret-32-chars-min!!!!!!, otherwise valid (exp = now + 1h, permissions=FL)
2 GET /vehicles with Authorization: Bearer T_bad 401

Pass criteria: 401.


NFT-SEC-03: Expired token outside skew → 401; inside skew → 200

Summary: Verifies AC-5.6 + AC-5.2 (1-min skew tighter than .NET's 5-min default). Traces to: AC-5.2, AC-5.6

Steps:

Step Consumer Action Expected Response
1 Mint token T_exp with exp = now - 120s (outside 60s skew); permissions=FL
2 GET /vehicles with Authorization: Bearer T_exp 401
3 Mint token T_skew with exp = now - 30s (inside 60s skew); permissions=FL
4 GET /vehicles with Authorization: Bearer T_skew 200

Pass criteria: T_exp rejected; T_skew accepted.


NFT-SEC-04: Missing iss and aud claims accepted (today's behavior, AC-5.3)

Summary: Verifies the ValidateIssuer = false and ValidateAudience = false configuration. This test will FAIL once the suite-wide remediation (AZ-487 / AZ-494) lands — that's good news; update the test then. Traces to: AC-5.3

Steps:

Step Consumer Action Expected Response
1 Mint token with NO iss and NO aud claim, valid signature + lifetime, permissions=FL
2 GET /vehicles with that token 200

Pass criteria: 200 today; will become 401 post-remediation.


NFT-SEC-05: Missing permissions claim → 403

Summary: Verifies AC-5.8 — valid signature + lifetime is not enough; the permissions=FL claim is required. Traces to: AC-5.8, AC-9.1

Steps:

Step Consumer Action Expected Response
1 Mint token with no permissions claim, valid otherwise
2 GET /vehicles 403

Pass criteria: 403.


NFT-SEC-06: Wrong permissions claim value → 403

Summary: Verifies AC-9.2 — the policy is exact-string match, hardcoded. Traces to: AC-9.2

Steps:

Step Consumer Action Expected Response
1 Mint token with permissions="ADMIN", valid otherwise
2 GET /vehicles 403
3 Mint token with permissions="fl" (lowercase), valid otherwise
4 GET /vehicles 403
5 Mint token with permissions="FLight", valid otherwise
6 GET /vehicles 403

Pass criteria: 403 for every wrong-value case.


NFT-SEC-07: Health endpoint exempt from auth

Summary: Verifies AC-7.1, AC-9.4 (contrast) — /health is anonymous. Traces to: AC-7.1, AC-9.4

Steps:

Step Consumer Action Expected Response
1 GET /health with no Authorization 200
2 GET /health with Authorization: Bearer <expired token> 200 (auth not evaluated)

Pass criteria: 200 in both cases.


NFT-SEC-08: Stack trace not leaked in 500 body

Summary: Verifies AC-8.6 + AC-10.3 — internal exception details stay in the log, not the HTTP body. Traces to: AC-8.6, AC-10.3

Steps:

Step Consumer Action Expected Response
1 Force a 500 (drop vehicles table mid-test, then GET /vehicles/{any})
2 Inspect response body body == { "statusCode":500, "message":"Internal server error" } exactly; NO key matching stack, stackTrace, exception, inner, trace; NO file path; NO type name in the body
3 docker logs missions | grep "Unhandled exception" At least one matching line; line contains the file path of the throw site OR the exception type name (the log-side info is private to operators)

Pass criteria: response body contains only statusCode, message; log contains stack info.


NFT-SEC-09: SQL injection guard via parameterised queries

Summary: Defensive — verifies linq2db's parameterised query path is in effect for filter strings. Traces to: AC-1.6 (filter), AC-2.3 (filter), defensive

Steps:

Step Consumer Action Expected Response
1 GET /vehicles?name='%20OR%20'1'%3D'1 (URL-encoded ' OR '1'='1) 200; body.length == 0 (no row matches the literal ' OR '1'='1 string against BR-01 etc.)
2 GET /missions?name=%3B%20DROP%20TABLE%20vehicles%3B%20-- (URL-encoded ; DROP TABLE vehicles; --) 200; body.TotalCount == 0; side-channel verifies vehicles table still exists

Pass criteria: filter inputs are treated as literal strings; no SQL execution; no DDL side-effect.


Notes

  • Tests that drop tables (NFT-SEC-08) run in a per-class fixture that recreates the schema before subsequent tests.
  • The CMMC L2 row 3 (iss / aud) gap is acknowledged but NOT remediated in this Epic; NFT-SEC-04 documents today's permissive behavior so a future enforcement change is detected.
  • No fuzz testing today (recommended follow-up under a separate refactor cycle).