Files
gps-denied-onboard/_docs/05_security/README.md
T
Oleksandr Bezdieniezhnykh bf13549b32
ci/woodpecker/push/02-build-push Pipeline failed
[autodev] Update configuration and documentation for cycle-1
- Enhanced `.env.example` with detailed CMake build flags and replay-mode strategy flags for development and CI environments.
- Updated `.gitignore` to include a new deploy rollback bookmark.
- Revised `_docs/_autodev_state.md` to reflect the current task status and steps.
- Added new lessons to `_docs/LESSONS.md` regarding testing and architectural improvements.
- Documented changes in `_docs/02_document/deployment/ci_cd_pipeline.md` to reflect the relaxed OpenCV version pin.
- Updated test data documentation in `_docs/02_document/tests/test-data.md` to clarify fixture usage and paths.

This commit continues the cycle-1 documentation sync and addresses various configuration updates for improved clarity and functionality.
2026-05-20 08:05:35 +03:00

10 KiB
Raw Blame History

Security Audit — Consolidated Report

Audit window: 2026-05-19 Auditor: autodev Step 14 (greenfield flow) — .cursor/skills/security/SKILL.md § Phases 15 Scope: gps-denied-onboard SUT — production code path (src/gps_denied_onboard/**), pinned dependencies, container build artefacts, docker-compose topology, committed secret hygiene. Out of scope: parent-suite components (D-PROJ-1 deploy/observability stack, D-PROJ-2 ingest service, satellite-provider, operator GCS UI); third-party SITL images beyond their tagging hygiene.

Executive Summary

The SUT's security posture is built around a single defining design property: the airborne process has no inbound or outbound network surface. That property is enforced at three independent layers (architectural, operational iptables + DNS blackhole, test-harness verification via NFT-SEC-02 / NFT-SEC-05), which keeps the externally-reachable attack surface at zero in flight. The pre-flight (operator-workstation) phases DO touch the network — over TLS, with pinned hosts, parameterized SQL, redacted logs, and Ed25519-signed manifests / uploads.

Result: 0 Critical, 0 High, 5 Medium, 17 Low. No release-blocking finding. The Medium findings are container-hardening gaps (root user in the production image, dev extras in the production image, moving-tag base images for SITL) that would be raised to High under a multi-tenant or internet-exposed threat model but are bounded to Medium here by the closed-system invariant.

Severity Count Phase 1 (Deps) Phase 2 (SAST) Phase 3 (OWASP) Phase 4 (Infra)
Critical 0 0 0 0 0
High 0 0 0 0 0
Medium 5 1 0 0 4
Low 17 11 1 (informational) 0 5
Total 22 12 1 0 9

Release-blocking findings: 0. Remediation expected before next major dependency refresh: F14 (non-root container), F15 (production [dev] extras), F16 (mavproxy:latest). Remediation deferred to a future hardening pass: F13 (plugin-loader assertion), F17 (digest-pin production base images), F18-F22 (housekeeping). Cross-component leftover already tracked: D-CROSS-CVE-1 — opencv-python pin coupling with gtsam numpy<2 ABI block (_docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md).

Threat Model Snapshot

Property Status Phase Evidence
Airborne process has no network listener enforced A04 compose_root_airborne wires no HTTP server; NFT-SEC-05 enforces at runtime
Airborne process has no network egress enforced A04 iptables OUTPUT REJECT + DNS blackhole (RESTRICT-OPS-1); NFT-SEC-02 verifies in CI
Operator-workstation HTTP traffic is TLS-validated enforced A02 0 verify=False matches; pinned base URLs only
Tile / Manifest integrity end-to-end enforced A08 SHA-256 sidecar + Ed25519 detached signatures
MAVLink ↔ FC channel is signed enforced (AP) / documented residual (iNav) A07 Per-flight key rotation logged to FDR (NFT-SEC-03)
In-container runtime executes as non-root GAP — F14 A05, F14 Production Dockerfiles missing USER directive
Production runtime image excludes dev tools GAP — F15 A05, F15 pip install -e ".[dev]" ships pytest, ruff, mypy into prod
All container images are digest-pinned GAP — F16/F17 A05, F16, F17 Mixed: tile-cache-builder digest-pinned, production images use floating tags
Single-tenant binary (no per-user RBAC) by design A01 One-OS-user container, FC arm/disarm IS the privilege gate
Secrets never logged enforced A09 _REDACTED convention across 13 files
Test secrets are synthetic, marked TEST ONLY enforced P6 Both committed passkey files contain 0123…ef-repeated bytes

Per-Phase Outcomes

Phase 1 — Dependency Scan (_docs/05_security/dependency_scan.md)

pip-audit against the pinned environment surfaced 12 advisories across 5 packages.

Package Advisories Project-specific finding
onnx==1.18.0 1 (GHSA-rcgw-4cgp-9q7q) Reachable only via developer-mode model loading; SUT serves prebuilt engines on the production path. Medium.
cryptography==42.0.8 4 None reachable from SUT usage (Ed25519 sign/verify + httpx default X.509). Low.
numpy==1.26.4 4 None reachable from SUT usage (no untrusted-input numerical ingestion). Low.
protobuf==4.25.3 2 None reachable; protobuf usage is internal serialization to the FDR layer. Low.
setuptools 1 Dev-time only. Low.

D-CROSS-CVE-1 outcome: re-running pip-audit against the relaxed pin (opencv-python>=4.11.0.86,<4.12) confirmed that CVE-2025-53644 is NOT flagged for opencv-python==4.11.0.86. The deferred-bump leftover was updated to record this; the residual risk window is effectively closed at the current pin. Bump to 4.12.x remains coupled to the gtsam numpy-2 ABI work.

Phase 2 — Static Analysis (SAST) (_docs/05_security/static_analysis.md)

Pattern-driven ripgrep scan across 259 Python files in src/.

Category Findings
SQL injection 0 — all 19 cur.execute(...) sites are parameterized
Command injection 0 — 1 subprocess.run (TensorRT trtexec), list args, no untrusted input
Hardcoded secrets / weak crypto 0 — Ed25519, SHA-256, secrets.token_bytes
Insecure deserialization 0 — no pickle.loads / eval / exec on untrusted data; json.loads only on local SUT-managed files
TLS / verification bypass 0 — all httpx.Client defaults preserved
F13 (informational) — Dynamic __import__ via plugin registry 1 — bounded by closed KNOWN_*_STRATEGIES whitelist; hardening recommended

Defense-in-depth observations: per-flight ephemeral Ed25519 signing in C11; secrets.token_bytes for MAVLink passkey generation; paramiko.RejectPolicy (NOT AutoAddPolicy) for SSH; _REDACTED convention enforced across 13 files; secret-buffer zeroising on session end (AZ-318).

Phase 3 — OWASP Top 10 Review (_docs/05_security/owasp_top_10_review.md)

All 10 categories assessed against the SUT's actual surface. A01 (Broken Access Control) is N/A by design (single-tenant binary, no per-user RBAC). A02 through A10 each cleared with 0 findings beyond those already reported in Phases 1, 2, and 4. The closed-system threat model is itself the strongest A04 (Insecure Design) property and is enforced at three layers.

Phase 4 — Configuration & Infrastructure (_docs/05_security/config_infra_review.md)

9 findings (4 Medium, 5 Low) — all container-hardening gaps in the production images. The test stack is well-secured (internal: true network, digest-pinned tile-cache-builder, public-boundary discipline on the runner). The gap is consistency: the project already KNOWS how to do this (see e2e/fixtures/tile-cache-builder/Dockerfile) — production images need to match that bar.

Remediation Plan

ID Title Effort Coupling
F14 Add non-root USER directive to docker/companion-tier1.Dockerfile + docker/operator-orchestrator.Dockerfile 2 pts Couples with F22 (chown WORKDIR)
F15 Replace pip install -e ".[dev]" with runtime-only install in both production Dockerfiles 2 pts Standalone
F16 Pin ardupilot/mavproxy:latest and ardupilot/ardupilot-sitl:plane-stable to explicit versions or SHA256 digests 1 pt Standalone
F1 Bump onnx from 1.18.0 → 1.19+ when next dep cycle lands (GHSA-rcgw-4cgp-9q7q) 2 pts Couples with C7 model-loader review
ID Title Effort
F13 Add assert strategy in KNOWN_*_STRATEGIES at each __import__ call site (defense-in-depth, redundant today) 2 pts
F17 Pin production base images by SHA256 digest, with explicit refresh cadence 1 pt
F18 Delete or fix the orphan docker/mock-suite-sat-service.Dockerfile 1 pt
F19 Remove unused curl from docker/operator-orchestrator.Dockerfile 1 pt
F20 Add upper bound to runner's opencv-python pin 1 pt
F21 Update .env.example to current secret paths + env var names 1 pt
F22 Coupled with F14 — chown WORKDIR after adding USER directive included in F14

Deferred / external

  • D-CROSS-CVE-1opencv-python 4.12.x bump deferred until gtsam ships a numpy-2 compatible release. Tracked at _docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md. Residual exposure window is currently closed (CVE-2025-53644 no longer flagged at 4.11.0.86).
  • Per-flight rekeying audit cadence — out of scope for this audit; will be revisited when D-PROJ-2 ingest endpoint lands and the upload contract is finalised.
  • Operator-workstation host hardening (no-swap RESTRICT-OPS-1, etc.) — parent-suite responsibility; cross-referenced in _docs/02_document/architecture.md § Operating envelopes.

Audit Method Footnote

  • Phase 1 used pip-audit against a freshly resolved pip freeze of the SUT venv (the project's editable install was filtered out per the project-package exclusion rule).
  • Phase 2 used ripgrep with the patterns enumerated in .cursor/skills/security/SKILL.md § Phase 2; every match was cross-validated by reading the call site.
  • Phase 3 was a desk review against the OWASP 2021 list, with each category mapped to the SUT's actual production surface (see static_analysis.md "Surface Map") rather than a generic web-app threat model.
  • Phase 4 was a desk review of all 6 Dockerfiles, 2 docker-compose files, all committed secrets, .env.example, and .gitignore.

Self-Verification

  • Each phase has its own dedicated artefact and is cross-referenced here
  • Severity counts in the Executive Summary sum to the per-phase counts in the Outcomes section (22 = 12 + 1 + 0 + 9)
  • No finding is silently downgraded — every project-specific severity calibration is justified at the finding site
  • D-CROSS-CVE-1 leftover updated and cross-referenced; no stale or missing tracker entry
  • Remediation effort estimates use the user's complexity-points rule (2-3 pts default, 5 pts max for big items, no 13-point monolith)