mirror of
https://github.com/azaion/missions.git
synced 2026-06-21 11:31:08 +00:00
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.
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user