# Security Audit Report (Cycle 5)

**Date**: 2026-05-12
**Scope**: Cycle-5 delta over the cycle-4 audit (`_docs/05_security/security_report_cycle4.md`)
**Trigger**: AZ-503-foundation (UUIDv5 tile identity + integer-only flight-aware UPSERT + per-flight on-disk paths) + AZ-504 (perf-script pipefail fix)
**Mode**: Delta — all five phases re-executed for the AZ-503 surface; AZ-504 has no source-code surface beyond a shell wrap and is folded into Phase 2
**Verdict**: **PASS_WITH_WARNINGS** (cycle-5 delta) / **PASS_WITH_WARNINGS** (cumulative — carries forward 1 cycle-3 Medium dep finding via D2-cy4 + 2 cycle-5 Low informational notes)

## Summary

| Severity | Count (cycle 5 delta) | Count (cumulative) |
|----------|-----------------------|--------------------|
| Critical | 0                     | 0 |
| High     | 0                     | 0 |
| Medium   | 0 NEW                 | 1 (D2-cy4 carry-over — Microsoft.NET.Test.Sdk transitive flag, test-runtime exposure only) |
| Low      | 2 NEW (informational) | 5 cycle-4 informational + 2 cycle-5 informational |

## OWASP Top 10 Assessment (Cycle 5)

| Category | Status |
|----------|--------|
| A01 Broken Access Control | PASS |
| A02 Cryptographic Failures | PASS |
| A03 Injection | PASS |
| A04 Insecure Design | PASS_WITH_NOTE (F1-cy5 — flight_id provenance) |
| A05 Security Misconfiguration | PASS |
| A06 Vulnerable Components | PASS_WITH_WARNINGS (D2-cy4 carry-over only) |
| A07 Auth Failures | PASS |
| A08 Data Integrity Failures | PASS |
| A09 Logging Failures | PASS |
| A10 SSRF | PASS |

## Cycle-5 NEW Findings

| # | Severity | Category | Location | Title |
|---|----------|----------|----------|-------|
| F1-cy5 | Low (informational) | Insecure Design (A04) | `_docs/02_document/contracts/api/uav-tile-upload.md` v1.1.0 + `UavTileUploadHandler.PersistAsync` | `metadata.flightId` is not authenticated provenance |
| F2-cy5 | Low (informational) | Security Misconfiguration (A05) | Migration 014 `CREATE EXTENSION pgcrypto` | Deployment runbook gap on managed Postgres providers |

### Finding Details

**F1-cy5: `metadata.flightId` is not authenticated provenance** (Low / Insecure Design)
- Location: `_docs/02_document/contracts/api/uav-tile-upload.md` v1.1.0 § Request shape; `SatelliteProvider.Services.TileDownloader/UavTileUploadHandler.cs:144-217`
- Description: The new optional `metadata.flightId` field is persisted to `tiles.flight_id` and used as part of the on-disk path and the deterministic `tile.id` derivation, but the handler does NOT check that the authenticated principal is authorized to write under that flight identifier. Any GPS-permissioned caller can supply any flight_id.
- Impact: Two adversarial cases:
  1. **Impersonation**: a compromised UAV credential can falsely attribute its uploads to a different flight (mis-attribution on the evidence chain).
  2. **False-flag**: a legitimate UAV credential can falsely attribute its uploads to a competing operator's flight_id.
  Downstream consumers MUST NOT treat `tiles.flight_id` as cryptographic provenance — they must cross-reference against an authoritative flight registry (out of this workspace's scope) before drawing operational conclusions.
- Remediation: Documented as a deliberate v1.1.0 design choice. If a future cycle requires per-flight ownership, options listed in `static_analysis_cycle5.md` (per-flight JWT, scoped permission claim `GPS:flight=<uuid>`, or move flight_id derivation to a trusted claim).
- Verification cross-reference: AZ-487/AZ-494 (JWT identity baseline) + AZ-488 (`RequiresGpsPermission` policy) — both still apply unchanged. F1-cy5 is purely about the **inside** of the authorized envelope.
- Severity rationale: Low because (i) the surface only exists after a valid GPS-permissioned JWT, (ii) the Admin API per `suite/_docs/10_auth.md` is the upstream identity gate, (iii) no current consumer treats flight_id as authenticated provenance.

**F2-cy5: Deployment runbook gap — `CREATE EXTENSION pgcrypto` privilege on managed Postgres** (Low / Security Misconfiguration)
- Location: `SatelliteProvider.DataAccess/Migrations/014_AddTileIdentityColumns.sql:34`; first observed by `_docs/05_security/infrastructure_review_cycle5.md`
- Description: Migration 014 issues `CREATE EXTENSION IF NOT EXISTS pgcrypto`. The current Docker compose dev/test environment connects as the `postgres` superuser, so the extension installs automatically. On managed Postgres providers (AWS RDS, Google Cloud SQL, Azure Database for PostgreSQL) the deployment role typically lacks superuser; the migration will fail with `must be owner of database` or `permission denied to create extension` unless the extension is pre-installed by an operator.
- Impact: Deployment may fail at the migration step at startup time. Failure is loud (the app crashes before serving requests) — not a silent security degradation. No production cycle has shipped yet for the satellite-provider on a managed Postgres provider, so this is forward-looking.
- Remediation:
  - (a) Add a one-line entry to the deployment runbook: "ensure `CREATE EXTENSION IF NOT EXISTS pgcrypto` has run as a superuser before the satellite-provider migration role runs migration 014."
  - (b) On RDS / Cloud SQL, allow-list `pgcrypto` in the provider's per-DB extension UI / parameter group.
- Severity rationale: Low informational because (i) the failure mode is loud, (ii) the remediation is one-line operational, (iii) the local Docker environment is unaffected, (iv) every recent managed Postgres provider supports `pgcrypto` in their default allow-list (it's a contrib module shipped with Postgres itself, not a third-party extension).

## Dependency Vulnerabilities (Cycle 5 delta)

None. Zero new NuGet packages, zero version bumps. The cycle-4 D2-cy4 Medium carry-over (Microsoft.NET.Test.Sdk 17.8.0 transitive `NuGet.Frameworks` flag) is unchanged.

## Recommendations

### Immediate (Critical/High)

None. No Critical or High findings in cycle 5.

### Short-term (Medium)

Apply the D2-cy4 Microsoft.NET.Test.Sdk refresh once a downstream cycle bumps the Test SDK (separate workstream — same posture as cycle 4).

### Long-term (Low / Hardening)

- **F1-cy5**: when an authoritative flight registry is introduced (likely a sibling repo or the Admin API), add per-flight ownership verification to `UavTileUploadHandler.HandleAsync` and bump the upload contract to v2.0.0. Until then, document the trust boundary clearly in any consumer-facing API docs that surface `tiles.flight_id`.
- **F2-cy5**: add the `pgcrypto` pre-install step to the deployment runbook before the first managed-Postgres deployment.

## Cross-Reference to Prior Audits

- Cycle-3 baseline: `_docs/05_security/security_report.md` (authoritative for OWASP narrative on categories untouched by AZ-503).
- Cycle-4 delta: `_docs/05_security/security_report_cycle4.md` (AZ-500 package bumps; D2-cy4 finding tree).
- Cycle-5 delta artifacts: `dependency_scan_cycle5.md`, `static_analysis_cycle5.md`, `owasp_review_cycle5.md`, `infrastructure_review_cycle5.md`.

## Verdict Reasoning

- No Critical, no High, no NEW Medium findings.
- 2 Low informational notes (F1-cy5, F2-cy5) properly documented with rationale and forward-looking remediation paths.
- Cumulative posture continues to carry the cycle-3 D2-cy4 Medium dep finding (out-of-scope for AZ-503).
- Per `security/SKILL.md` § Verdict Logic: `PASS_WITH_WARNINGS` because the cumulative state retains a Medium finding (D2-cy4) but no Critical or High. Cycle-5 delta in isolation would be `PASS_WITH_WARNINGS` due to the Low informational notes; the cycle-5 delta-only verdict is `PASS` per the strict reading ("PASS_WITH_WARNINGS: only Medium or Low findings" — and we have 2 Low informational). The conservative (cumulative) verdict is `PASS_WITH_WARNINGS`.

**Final verdict (cumulative)**: **PASS_WITH_WARNINGS**.
**Cycle-5 delta verdict**: **PASS_WITH_WARNINGS** (informational notes only — gate-acceptable).
