mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-27 10:11:13 +00:00
[AZ-1126] Migrate capturedAt to DateTimeOffset
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Migrate UAV upload capturedAt to DateTimeOffset
|
||||
|
||||
**Task**: AZ-1126_captured_at_datetimeoffset
|
||||
**Name**: Migrate UavTileMetadata.capturedAt to DateTimeOffset (F-AZ810-2)
|
||||
**Description**: Close security carry-over F-AZ810-2 by typing `UavTileMetadata.CapturedAt` as `DateTimeOffset` instead of `DateTime`, eliminating ambiguous `DateTimeKind.Unspecified` handling on the UAV upload metadata input path.
|
||||
**Complexity**: 2 points
|
||||
**Dependencies**: AZ-810 (HARD — metadata validation layer); AZ-488 (original upload endpoint)
|
||||
**Component**: SatelliteProvider.Common (UavTileMetadata) + SatelliteProvider.Api (validators) + SatelliteProvider.Services.TileDownloader (quality gate + upload handler)
|
||||
**Tracker**: AZ-1126
|
||||
**Epic**: AZ-795
|
||||
|
||||
## Problem
|
||||
|
||||
Security finding F-AZ810-2 (cycle 8, open through cycle 12) flags that `UavTileMetadata.CapturedAt` is typed `DateTime` rather than `DateTimeOffset`. `DateTime` accepts `DateTimeKind.Unspecified` values that deserialize from offset-less ISO-8601 strings, forcing manual `Kind` normalization in the upload handler and quality gate. This is a time-handling correctness gap that can skew freshness-window checks in non-UTC dev environments.
|
||||
|
||||
## Outcome
|
||||
|
||||
- `capturedAt` on the UAV upload metadata input is unambiguously UTC-aware at the type level
|
||||
- Offset-less or `Unspecified` timestamps are rejected before persistence
|
||||
- F-AZ810-2 is marked resolved in the next security audit cycle
|
||||
- Wire compatibility preserved for clients sending ISO-8601 UTC with explicit offset (`Z` or `+00:00`)
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Change `UavTileMetadata.CapturedAt` from `DateTime` to `DateTimeOffset`
|
||||
- Update FluentValidation rules, quality gate, and upload handler to compare via UTC without manual `Kind` branching
|
||||
- Reject offset-less / ambiguous `capturedAt` values with HTTP 400
|
||||
- Unit tests for the new rejection path and existing freshness-window rules
|
||||
- Integration test proving offset-less `capturedAt` is rejected
|
||||
- Patch `_docs/02_document/contracts/api/uav-tile-upload.md` 1.2.0 → 1.2.1 (change log + clarify offset requirement)
|
||||
|
||||
### Excluded
|
||||
|
||||
- `TileInventoryEntry.CapturedAt` response field (remains `DateTime?` — DB read path)
|
||||
- `TileEntity.CapturedAt` persistence layer type
|
||||
- Changes to gRPC tile delivery or other non-UAV-upload surfaces
|
||||
- MAJOR contract version bump (wire JSON shape unchanged for compliant clients)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Type migration**
|
||||
Given the UAV upload metadata DTO
|
||||
When deserialized from JSON
|
||||
Then `CapturedAt` is `DateTimeOffset` and freshness comparisons use UTC without manual `DateTimeKind` normalization
|
||||
|
||||
**AC-2: Reject ambiguous timestamps**
|
||||
Given a UAV upload batch with `capturedAt` lacking an explicit UTC offset (offset-less ISO string)
|
||||
When POST `/api/satellite/upload`
|
||||
Then HTTP 400 with a validation or deserialization error referencing `capturedAt`
|
||||
|
||||
**AC-3: Backward-compatible UTC clients**
|
||||
Given a UAV upload batch with `capturedAt` as ISO-8601 UTC (`...Z` or `...+00:00`)
|
||||
When POST `/api/satellite/upload` with otherwise valid payload
|
||||
Then HTTP 200 (or the same non-timestamp rejection as before timestamp validation)
|
||||
|
||||
**AC-4: Contract patch**
|
||||
Given the implementation is complete
|
||||
When `uav-tile-upload.md` is reviewed
|
||||
Then version is 1.2.1 with a change-log entry documenting the offset requirement and F-AZ810-2 closure
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
**Compatibility**
|
||||
- Compliant clients already sending `Z`-suffixed timestamps must not break
|
||||
|
||||
**Security**
|
||||
- Closes F-AZ810-2 (Low / informational time-handling finding)
|
||||
|
||||
## Unit Tests
|
||||
|
||||
| AC Ref | What to Test | Required Outcome |
|
||||
|--------|-------------|-----------------|
|
||||
| AC-1 | `UavTileMetadataValidator` freshness window with `DateTimeOffset` | Future/too-old rules still fire |
|
||||
| AC-2 | Deserializer or validator with offset-less `capturedAt` | Rejected |
|
||||
| AC-1 | `UavTileQualityGate` captured-at rules | Still accept/reject correctly |
|
||||
|
||||
## Blackbox Tests
|
||||
|
||||
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|
||||
|--------|------------------------|-------------|-------------------|----------------|
|
||||
| AC-2 | Valid JPEG + metadata with `capturedAt: "2026-06-26T12:00:00"` (no offset) | POST `/api/satellite/upload` | HTTP 400 mentioning `capturedAt` | — |
|
||||
| AC-3 | Valid JPEG + metadata with `capturedAt` as `DateTime.UtcNow.ToString("o")` | POST `/api/satellite/upload` | HTTP 200 (happy path unchanged) | Compatibility |
|
||||
|
||||
## Constraints
|
||||
|
||||
- Must remain a child of epic AZ-795 strict-validation theme
|
||||
- No breaking wire change for offset-aware clients
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1: Client sends offset-less timestamps**
|
||||
- *Risk*: Legitimate clients using `"2026-06-26T12:00:00"` without `Z` start failing
|
||||
- *Mitigation*: Contract patch documents requirement; rejection is intentional per F-AZ810-2
|
||||
|
||||
## Contract
|
||||
|
||||
This task patches the producer contract at `_docs/02_document/contracts/api/uav-tile-upload.md` (1.2.0 → 1.2.1).
|
||||
|
||||
Consumers: `gps-denied-onboard`, mission planner UI, any UAV upload client.
|
||||
|
||||
### Document Dependencies
|
||||
|
||||
- `_docs/02_document/contracts/api/uav-tile-upload.md` v1.2.0 (patch to 1.2.1)
|
||||
- `_docs/02_document/contracts/api/error-shape.md` v1.0.0 (unchanged)
|
||||
Reference in New Issue
Block a user