Wrap up cycle 5 verification + documentation: - Steps 10/11 wrap-up reports (implementation_completeness + implementation_report) for the AZ-503-foundation + AZ-504 batch. - Step 12 test-spec sync: AZ-503-foundation/AZ-504 ACs appended; AZ-505 deferred ACs recorded. - Step 13 update-docs: architecture, data-model, glossary, module- layout, uav-tile-upload contract (v1.1.0), DataAccess + Services + Tests module docs synced; new common_uuidv5.md module doc. - Step 14 security audit: PASS_WITH_WARNINGS; 0 new Critical/High; 2 new Low informational (F1 flightId provenance, F2 pgcrypto deploy gap). - Step 15 performance test: PASS_WITH_INFRA_WARNINGS; PT-08 passed twice (AZ-504 fix verified); PT-01/02 failed due to recurring local Docker/colima DNS cold-start (not an app regression). Cycle-3 perf-harness leftover stays OPEN with replay #5 documented. - Autodev state moved to Step 16 (Deploy). Co-authored-by: Cursor <cursoragent@cursor.com>
7.8 KiB
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.mdv1.1.0 § Request shape;SatelliteProvider.Services.TileDownloader/UavTileUploadHandler.cs:144-217 - Description: The new optional
metadata.flightIdfield is persisted totiles.flight_idand used as part of the on-disk path and the deterministictile.idderivation, 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:
- Impersonation: a compromised UAV credential can falsely attribute its uploads to a different flight (mis-attribution on the evidence chain).
- 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_idas 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 claimGPS:flight=<uuid>, or move flight_id derivation to a trusted claim). - Verification cross-reference: AZ-487/AZ-494 (JWT identity baseline) + AZ-488 (
RequiresGpsPermissionpolicy) — 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.mdis 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 thepostgressuperuser, 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 withmust be owner of databaseorpermission denied to create extensionunless 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 pgcryptohas run as a superuser before the satellite-provider migration role runs migration 014." - (b) On RDS / Cloud SQL, allow-list
pgcryptoin the provider's per-DB extension UI / parameter group.
- (a) Add a one-line entry to the deployment runbook: "ensure
- 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
pgcryptoin 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.HandleAsyncand bump the upload contract to v2.0.0. Until then, document the trust boundary clearly in any consumer-facing API docs that surfacetiles.flight_id. - F2-cy5: add the
pgcryptopre-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_WARNINGSbecause the cumulative state retains a Medium finding (D2-cy4) but no Critical or High. Cycle-5 delta in isolation would bePASS_WITH_WARNINGSdue to the Low informational notes; the cycle-5 delta-only verdict isPASSper the strict reading ("PASS_WITH_WARNINGS: only Medium or Low findings" — and we have 2 Low informational). The conservative (cumulative) verdict isPASS_WITH_WARNINGS.
Final verdict (cumulative): PASS_WITH_WARNINGS. Cycle-5 delta verdict: PASS_WITH_WARNINGS (informational notes only — gate-acceptable).