# Security Tests

These tests cover the security-relevant AC and the Mode B revisions that introduced explicit security gates: D-CROSS-CVE-1 (OpenCV CVE pin), D-C8-9 (MAVLink 2.0 message signing), AC-NEW-7 (cache poisoning), and RESTRICT-SAT-1 / AC-8.1 (no in-flight Service calls).

### NFT-SEC-01: Cache-poisoning safety budget

**Summary**: Validates AC-NEW-7 — across all onboard tiles written, `P(geo-misalign > 30 m) < 1%` and `P(> 100 m) < 0.1%`. Multi-flight statistics constrained — PARTIAL with current single-flight fixture (see traceability matrix).
**Traces to**: AC-NEW-7, Mode B Fact #105 (Service voting layer external dependency), D-PROJ-2

**Steps**:

| Step | Consumer Action | Expected Response |
|------|----------------|------------------|
| 1 | Run 3 trial flights against `derkachi-fixture` with synthetic over-confidence injection (deflate covariance ×1.5, ×2, ×3) | Each flight produces mid-flight tiles uploaded to `mock-suite-sat-service` |
| 2 | After each flight, the mock service records each received tile's quality metadata + onboard-asserted geo-alignment vs the GT-derived geo-alignment | Per-tile mis-alignment captured |
| 3 | Across all uploaded tiles, compute `P(misalign > 30 m)` and `P(misalign > 100 m)` | Statistic computed |
| 4 | Independently observe Suite Sat Service voting-layer behavior (mock) — verify mock-side gate refuses `trusted basemap` promotion when ingest votes don't agree | Voting contract assertion (per D-PROJ-2) |

**Pass criteria** (PARTIAL):
- `P(misalign > 30 m) < 1%`, `P(misalign > 100 m) < 0.1%` across the available trial flights.
- PARTIAL annotation: AC text expects ≥100 flights — escalates D-PROJ-3 fixture acquisition + D-PROJ-2 contract verification.

---

### NFT-SEC-02: No in-flight Service calls (network egress isolation)

**Summary**: Validates RESTRICT-SAT-1 / AC-8.1 — the SUT MUST NOT reach an external satellite provider during a flight. All cache reads come from the local cache.
**Traces to**: RESTRICT-SAT-1, AC-8.1

**Steps**:

| Step | Consumer Action | Expected Response |
|------|----------------|------------------|
| 1 | Start the SUT with `e2e-net` configured `internal: true` (no external connectivity at the network layer) | SUT comes up; tile cache reads succeed |
| 2 | Run 5 min of Derkachi replay | All tile lookups served from local cache |
| 3 | Read SUT egress counter (Docker network stats) | 0 packets out to non-`e2e-net` destinations |
| 4 | Inspect SUT log for any "external Service call attempted" event | 0 events (proving the SUT didn't even try) |
| 5 | Defense-in-depth: temporarily flip `internal: false` AND blackhole DNS, re-run | Same — 0 egress attempts; no failed-DNS errors |

**Pass criteria**: 0 packets to non-`e2e-net` destinations; no "Service call attempted" log entry.

---

### NFT-SEC-03: MAVLink 2.0 message signing on AP wired channel

**Summary**: Validates D-C8-9 — AP-side rejects unsigned MAVLink GPS_INPUT messages on the signed channel; SUT-emitted (signed) messages pass; SBOM dump confirms passkey configuration.
**Traces to**: D-C8-9 (Plan-phase decision), Mode B Fact #109 (CVE-2026-1579 mitigation)

**Steps**:

| Step | Consumer Action | Expected Response |
|------|----------------|------------------|
| 1 | Start `ardupilot-plane-sitl` with signing enabled and the test passkey loaded | Signing enabled |
| 2 | Inject an UNSIGNED `GPS_INPUT` from `mavproxy-listener` (i.e. a non-SUT origin) | AP rejects the message; rejection logged in AP STATUSTEXT |
| 3 | Inject a SIGNED `GPS_INPUT` with the SUT's signing key | AP accepts |
| 4 | Inject a SIGNED `GPS_INPUT` with a DIFFERENT key | AP rejects |
| 5 | Run the SUT's SBOM-dump CI step | SBOM declares the MAVLink signing module + passkey configuration entry present |

**Pass criteria**: AP rejection of unsigned + wrong-key; AP acceptance of correct-signed; SBOM declares signing.

**Note**: iNav-side is NOT subject to this test — Mode B Fact #109 documents the asymmetry as accepted residual risk (no MAVLink signing in iNav firmware per Source #129).

---

### NFT-SEC-04: OpenCV CVE-2025-53644 mitigation (≥4.12.0 pin)

**Summary**: Validates D-CROSS-CVE-1 — the pinned OpenCV ≥4.12.0 either decodes the CVE-2025-53644 PoC JPEG safely or rejects it; no crash, no buffer overflow.
**Traces to**: D-CROSS-CVE-1, Mode B Fact #112

**Steps**:

| Step | Consumer Action | Expected Response |
|------|----------------|------------------|
| 1 | Build the SUT image with AddressSanitizer (ASan) instrumentation enabled (separate CI build) | Instrumented binary |
| 2 | Push `cve-jpeg-fixture` to every code path that uses OpenCV imread/imdecode: nav-camera frame source (C1), satellite tile thumbnail re-load (C4), tile cache import (C6) | Each path either decodes cleanly OR returns a graceful error |
| 3 | Observe ASan output | 0 buffer-overflow / use-after-free / uninitialized-read reports |
| 4 | Observe SUT process exit code | Process does NOT crash; if rejection path taken, exit code is 0 + error logged |
| 5 | CI step: lint the lockfile / pyproject.toml / requirements.txt for the OpenCV version pin | Pin asserts `opencv-python >= 4.12.0` (or platform-equivalent) |

**Pass criteria**: ASan clean; no crash; pinned version ≥ 4.12.0 in dependency manifest.

---

### NFT-SEC-05: Egress-blocked + DNS-blackholed defense-in-depth

**Summary**: Defense-in-depth complement to NFT-SEC-02 — verifies that even if the network policy were misconfigured, the SUT does not call out to public DNS / known satellite-provider hosts.
**Traces to**: RESTRICT-SAT-1 (defense-in-depth)

**Steps**:

| Step | Consumer Action | Expected Response |
|------|----------------|------------------|
| 1 | Configure SUT container with iptables OUTPUT DROP except `e2e-net` AND DNS blackholed via `--dns 0.0.0.0` | SUT comes up |
| 2 | Run Derkachi replay | All operations succeed; 0 outbound DNS queries (verified via tcpdump on egress) |
| 3 | Inspect SUT for hardcoded provider hostnames (e.g. `*.googleapis.com`, `*.maxar.com`, `*.mapbox.com`, `*.azaion.com` for the runtime path) | grep finds zero references in compiled binary's strings table for runtime-path code |

**Pass criteria**: 0 DNS queries during replay; 0 provider hostname references in runtime path.
