mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-27 15:26:38 +00:00
Update autodev state documentation to reflect completion of Plan Step 1, including detailed progress on phases and next steps. Revised phase details to clarify user-level blocking gates and hardware assessment outcomes.
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
# Security Tests
|
||||
|
||||
> Black-box security scenarios at the public interfaces. Code-level vulnerability scanning is out of scope here (handled by Phase 4 security audit / `security/SKILL.md`).
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-01: MAVLink2 signing — invalid signature rejected (S-T1)
|
||||
|
||||
**Summary**: A GPS_INPUT or other companion-bound MAVLink frame with invalid signing tag is rejected by the FC; SUT and FC both log the rejection.
|
||||
**Traces to**: M-7, R10, restrictions §Sensors (MAVLink2 signing mandatory), S-T1, F-T9. Tier: T3 (`deferred-sitl`).
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | Runner injects a GPS_INPUT with valid schema but signing tag computed against a wrong key | FC discards frame; STATUSTEXT WARN visible at GCS |
|
||||
| 2 | Inspect FC log | rejection event recorded |
|
||||
| 3 | Subsequent valid GPS_INPUT | accepted normally |
|
||||
|
||||
**Pass criteria**: invalid frame discarded; FC continues on prior valid source; valid frames still accepted.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-02: HTTPS unauthenticated requests are rejected
|
||||
|
||||
**Summary**: All HTTPS API endpoints require valid JWT.
|
||||
**Traces to**: results_report row 33, restriction "JWT auth on the HTTP boundary". Tier: T1.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Endpoint | Auth | Expected Response |
|
||||
|------|---------|------|-------------------|
|
||||
| 1 | `POST /sessions` | none | HTTP 401 |
|
||||
| 2 | `POST /objects/locate` | none | HTTP 401 |
|
||||
| 3 | `GET /sessions/{id}/stream` | none | HTTP 401 |
|
||||
| 4 | `GET /health` | none | HTTP 200 (health is intentionally unauthenticated for liveness probes — confirm via S-T2) OR 401 if it requires auth |
|
||||
|
||||
**Pass criteria**: 1–3 return 401; 4's behaviour matches the documented contract (test asserts whichever the contract states). If `/health` is unauthenticated, body still must NOT leak sensitive state (no flight data, no key fingerprints).
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-03: HTTPS — malformed / expired / wrong-issuer JWT
|
||||
|
||||
**Summary**: JWTs that fail validation are rejected.
|
||||
**Traces to**: derived from results_report row 33. Tier: T1.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Token | Expected Response |
|
||||
|------|-------|-------------------|
|
||||
| 1 | malformed (`.foo.bar`) | HTTP 401 |
|
||||
| 2 | expired (`exp` in the past) | HTTP 401 |
|
||||
| 3 | wrong issuer | HTTP 401 |
|
||||
| 4 | wrong signing algorithm (`none` algorithm) | HTTP 401 |
|
||||
| 5 | missing required claim (e.g., `sub`) | HTTP 401 |
|
||||
|
||||
**Pass criteria**: all return 401 with no leaked state in the body.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-04: TLS — minimum version + downgrade rejection
|
||||
|
||||
**Summary**: TLS ≥1.2; weaker / downgrade attempts rejected.
|
||||
**Traces to**: S-T2, derived from restriction "telemetry plumbing uses MAVSDK + HTTPS API". Tier: T1.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | Connect with TLSv1.0 / TLSv1.1 | refused |
|
||||
| 2 | Connect with cipher suite from a known weak set (e.g., RC4) | refused |
|
||||
| 3 | Valid TLSv1.2+ + modern cipher | accepted |
|
||||
|
||||
**Pass criteria**: all weak attempts refused; modern accepted.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-05: Tile-cache write attempt by unauthorized API path
|
||||
|
||||
**Summary**: SUT does not expose any HTTP path that allows external clients to write to the tile cache.
|
||||
**Traces to**: AC-8.5 (storage policy), AC-NEW-7 (cache integrity), restriction §Satellite. Tier: T1.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | `POST /tiles` (or any guess) with valid JWT | 404 or 405 (no such endpoint) |
|
||||
| 2 | Try `PUT /var/lib/gpsdenied/tiles/...` via any exposed API | 404 / 405 |
|
||||
| 3 | Inspect the documented OpenAPI contract | no tile-write endpoints |
|
||||
|
||||
**Pass criteria**: no successful tile-write paths exist via HTTP; only the post-flight uploader (out-bound to `service-stub`) writes outside the SUT.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-06: Spoofed sysid / sysid collision (M-31)
|
||||
|
||||
**Summary**: A second device claiming sysid 11 (the SUT's sysid) — FC handles per ArduPilot routing rules.
|
||||
**Traces to**: M-31, F-T9. Tier: T3.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | Runner publishes a fake GPS_INPUT from a sysid-collision sender | FC routing handles per documented behaviour (latest-talker wins or rejects) |
|
||||
| 2 | Confirm FC parameter audit prints the actual sysid configured | matches deployment runbook (M-31 sysid collision-check) |
|
||||
|
||||
**Pass criteria**: behaviour matches documented FC routing rule; STATUSTEXT WARN observable; test verifies the deploy runbook's collision-check (M-31) catches this in pre-flight.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-07: Operator-hint injection — only signed STATUSTEXT consumed
|
||||
|
||||
**Summary**: Unsigned operator hints (or hints from a non-allowed sender) are not consumed.
|
||||
**Traces to**: AC-6.2, M-7. Tier: T3.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | Send `RELOC_HINT` STATUSTEXT with invalid MAVLink2 signing | SUT discards; emits WARN |
|
||||
| 2 | Send from a sysid not on the allowed-list | SUT discards |
|
||||
| 3 | Send signed by allowed sender | SUT consumes (NFT-RES-05 covers happy path) |
|
||||
|
||||
**Pass criteria**: only authenticated, allowed-sender hints are consumed.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-08: GPS_RAW_INT spoofing chain — SUT promotion is the safety boundary
|
||||
|
||||
**Summary**: A spoofed `GPS_RAW_INT` cannot influence SUT's GPS_INPUT directly; SUT only uses GPS_RAW_INT for source-promotion logic, not for fusing.
|
||||
**Traces to**: AC-NEW-2, restriction §Failsafe. Tier: T3.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | Inject GPS_RAW_INT with high-quality false fix | SUT does NOT use it as a position seed; only uses it for the "real-GPS health" rolling average |
|
||||
| 2 | After scripted spoofing-pattern, SUT promotes its own estimate per AC-NEW-2 | promotion event observable |
|
||||
|
||||
**Pass criteria**: SUT GPS_INPUT positions never influenced by spoofed GPS_RAW_INT lat/lon (compare SUT GPS_INPUT vs ground truth from `coordinates.csv` during the spoof window).
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-09: USB bypass surface — bench-only
|
||||
|
||||
**Summary**: USB bypasses MAVLink2 signing per restriction; this must be **disabled in production** runtime config.
|
||||
**Traces to**: M-7, restrictions §Onboard Hardware. Tier: T1 (config audit).
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | At SUT boot, inspect runtime config | USB MAVLink endpoint disabled in production profile (env var `MAVLINK_USB_ALLOWED=false` or absent) |
|
||||
| 2 | Attempt to connect via USB | refused |
|
||||
|
||||
**Pass criteria**: production config refuses USB MAVLink; bench config (env var explicitly enabled) accepts.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-10: FDR — no sensitive-data leak
|
||||
|
||||
**Summary**: FDR contains the documented payload classes only — no private keys, no plaintext JWTs, no MAVLink2 signing keys, no raw frames (AC-8.5).
|
||||
**Traces to**: AC-8.5, AC-NEW-3, S-T3 (data-at-rest). Tier: T1.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | After a 30 min replay, scan FDR for known-sensitive byte patterns (test-only signing key bytes; test JWT) | none found |
|
||||
| 2 | Scan for raw JPEG headers in non-thumbnail-log payload classes | none |
|
||||
| 3 | Verify failure-thumbnail log is ≤ 0.1 Hz and within FDR cap | as spec'd |
|
||||
|
||||
**Pass criteria**: no leaks; raw-frame storage policy enforced.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-11: External-host network policy
|
||||
|
||||
**Summary**: SUT does not call external commercial satellite providers at runtime.
|
||||
**Traces to**: AC-8.1, restrictions §Satellite. Tier: T1.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | Run a 5-min replay with `iptables` / Docker network policy capturing all out-bound connections | none of the captured destinations resolves to Maxar / Airbus / Planet / Sentinel-2 / Esri / etc. |
|
||||
| 2 | The only allowed out-bound is to `service-stub` (the Suite Satellite Service candidate-pool endpoint, post-flight) | matches |
|
||||
|
||||
**Pass criteria**: no out-bound to commercial / public ortho providers at runtime.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-12: HTTPS — payload size + path-traversal hardening
|
||||
|
||||
**Summary**: Pathological HTTP requests do not crash the SUT or leak filesystem content.
|
||||
**Traces to**: AC-3.x (resilience), restrictions (security defaults). Tier: T1.
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | `POST /objects/locate` with a 100 MB body | HTTP 413 (payload too large) |
|
||||
| 2 | Path-traversal `GET /sessions/../../etc/passwd` | HTTP 404 / 400; no filesystem leak |
|
||||
| 3 | Header-injection (`X-Forwarded-For: \r\nSet-Cookie: …`) | sanitised; no echo back |
|
||||
|
||||
**Pass criteria**: as above; SUT alive; no leak.
|
||||
|
||||
---
|
||||
|
||||
### NFT-SEC-13: AC-NEW-7 over-confidence injection — gate rejects
|
||||
|
||||
**Summary**: Synthetic over-confidence injection (1.5×–3× covariance deflation) does not let bad tiles into the trusted basemap.
|
||||
**Traces to**: AC-NEW-7. Tier: T2 (`deferred-corpus`).
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Expected Response |
|
||||
|------|----------------|-------------------|
|
||||
| 1 | Replay AerialVL + Mavic + AerialExtreMatch with synthetic deflation | per-tile geo-misalignment computed |
|
||||
| 2 | At the σ_xy boundary (3 m, 5 m, 10 m), assert hard-gate behaviour | tiles outside σ_xy ≤ 5 m never written; tiles in (3, 5] m marked `trust_level=soft`; tiles ≤ 3 m `trust_level=candidate` |
|
||||
|
||||
**Pass criteria**: P(misalign > 30 m) < 1 %, P(misalign > 100 m) < 0.1 %; voting layer prevents single-flight promotion in non-active sectors.
|
||||
Reference in New Issue
Block a user