mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-26 06:21:13 +00:00
Sanitize 400 error messages in GlobalExceptionHandler and validation filters to use static strings. This change improves consistency and prevents leaking internal exception details. Updated tests to reflect new error messages for JSON parsing and bad request scenarios.
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
**Component**: WebApi (`SatelliteProvider.Api`) — applies to every public HTTP endpoint
|
||||
**Producer task**: AZ-795 — `_docs/02_tasks/done/AZ-795_strict_validation_epic.md`
|
||||
**Consumer tasks**: every per-endpoint child of AZ-795 (first: AZ-796) plus every `gps-denied-onboard` HTTP client and every future browser/CLI consumer
|
||||
**Version**: 1.0.0
|
||||
**Version**: 1.0.1
|
||||
**Status**: frozen
|
||||
**Last Updated**: 2026-05-22
|
||||
**Last Updated**: 2026-06-25
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -29,7 +29,7 @@ Both paths produce `Content-Type: application/problem+json`. Both populate the s
|
||||
"status": 400,
|
||||
"errors": {
|
||||
"tiles[0].z": ["The z field is required."],
|
||||
"tiles[1]": ["The JSON property 'tileZoom' could not be mapped to any .NET member contained in type 'TileCoord'."]
|
||||
"tiles[1].foo": ["The field value is invalid."]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -99,9 +99,19 @@ Server errors emit the simpler ProblemDetails shape with a `correlationId` exten
|
||||
- **Inv-2**: Validation failures (HTTP 400 from FluentValidation OR from JSON deserialization with a JsonException inner exception) always include an `errors` object.
|
||||
- **Inv-3**: Each `errors` entry has at least one message. Empty arrays are forbidden.
|
||||
- **Inv-4**: Field-path keys in `errors` use the same casing as the request body (camelCase root, dotted/indexed access for nested types).
|
||||
- **Inv-5**: 5xx responses include a `correlationId` extension property; 4xx responses do not. No 4xx response leaks server-internal state (DB connection strings, secrets, internal stack frames).
|
||||
- **Inv-6**: Unknown fields at root or in any nested object are rejected with HTTP 400 — not silently dropped. The error key names the offending field path.
|
||||
- **Inv-7**: Type mismatches (e.g. string where integer expected) are rejected with HTTP 400 and the error key names the offending field path.
|
||||
- **Inv-5**: 5xx responses include a `correlationId` extension property; 4xx responses do not. No 4xx response leaks server-internal state (DB connection strings, secrets, internal stack frames, or raw framework exception messages).
|
||||
- **Inv-6**: Unknown fields at root or in any nested object are rejected with HTTP 400 — not silently dropped. The error key names the offending field path; the message is the static string `"The field value is invalid."` (deserializer path) or a FluentValidation rule message (validator path).
|
||||
- **Inv-7**: Type mismatches (e.g. string where integer expected) are rejected with HTTP 400 and the error key names the offending field path; deserializer failures use `"The field value is invalid."`.
|
||||
|
||||
## Information disclosure (4xx messages)
|
||||
|
||||
| Source | Client-visible message | Server-side detail |
|
||||
|--------|------------------------|-------------------|
|
||||
| `GlobalExceptionHandler` + inner `JsonException` | `errors[<path>]`: `"The field value is invalid."` | Full `JsonException` in server logs when logged |
|
||||
| `GlobalExceptionHandler` + `BadHttpRequestException` (no `JsonException`) | `detail`: `"The request could not be processed."` | Framework message not echoed |
|
||||
| `UavUploadValidationFilter` metadata parse | `errors["metadata"]`: `"`metadata` could not be parsed as JSON."` | No `ex.Message` echo |
|
||||
| `UavTileUploadHandler` metadata parse (defense-in-depth) | Envelope error: same static string as filter | No `ex.Message` echo |
|
||||
| FluentValidation rules | Rule-specific consumer-oriented strings | Unchanged |
|
||||
|
||||
## Non-Goals
|
||||
|
||||
@@ -122,7 +132,7 @@ Server errors emit the simpler ProblemDetails shape with a `correlationId` exten
|
||||
|------|-------|----------|-------|
|
||||
| validation-missing-field | Inventory request with `tiles: [{ "z": 18 }]` (x, y missing) | HTTP 400 + `errors["tiles[0].x"]` and `errors["tiles[0].y"]` populated | Inv-2, Inv-4 |
|
||||
| validation-out-of-range | Inventory request with `tiles: [{ "z": 30, "x": 1, "y": 1 }]` | HTTP 400 + `errors["tiles[0].z"]` mentioning supported zoom range | Inv-2 |
|
||||
| validation-unknown-root-field | Body `{ "unknownField": 42, "tiles": [...] }` | HTTP 400 + `errors["unknownField"]` populated with "could not be mapped" | Inv-6 |
|
||||
| validation-unknown-root-field | Body `{ "unknownField": 42, "tiles": [...] }` | HTTP 400 + `errors["unknownField"]` = `"The field value is invalid."` | Inv-6 |
|
||||
| validation-unknown-nested-field | Body `{ "tiles": [{ "z": 18, "x": 1, "y": 1, "foo": 42 }] }` | HTTP 400 + `errors["tiles[0].foo"]` populated | Inv-6 |
|
||||
| validation-type-mismatch | Body `{ "tiles": [{ "z": "eighteen" }] }` | HTTP 400 + `errors["tiles[0].z"]` populated | Inv-7 |
|
||||
| validation-xor-both-populated | Body with both `tiles` and `locationHashes` populated | HTTP 400 + `errors["$"]` (or root key) populated | Inv-2 |
|
||||
@@ -133,4 +143,5 @@ Server errors emit the simpler ProblemDetails shape with a `correlationId` exten
|
||||
|
||||
| Version | Date | Change | Author |
|
||||
|---------|------|--------|--------|
|
||||
| 1.0.1 | 2026-06-25 | Sanitize deserializer/binding 400 messages — static strings replace raw `JsonException` / `BadHttpRequestException` text (AZ-1113). Adds Information Disclosure section. | autodev (Step 10, cycle 10) |
|
||||
| 1.0.0 | 2026-05-22 | Initial contract — uniform RFC 7807 ValidationProblemDetails shape for FluentValidation business-rule failures + JSON deserialization failures, including unknown-field rejection (`UnmappedMemberHandling.Disallow`). Sanitized ProblemDetails for 5xx (preserves AZ-353). Produced by AZ-795. | autodev (Step 10, cycle 7) |
|
||||
|
||||
Reference in New Issue
Block a user