Files
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

125 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
### Recommended before next dependency-refresh cycle
| 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 |
### Recommended at next hardening pass
| 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-1** — `opencv-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
- [x] Each phase has its own dedicated artefact and is cross-referenced here
- [x] Severity counts in the Executive Summary sum to the per-phase counts in the Outcomes section (22 = 12 + 1 + 0 + 9)
- [x] No finding is silently downgraded — every project-specific severity calibration is justified at the finding site
- [x] D-CROSS-CVE-1 leftover updated and cross-referenced; no stale or missing tracker entry
- [x] Remediation effort estimates use the user's complexity-points rule (2-3 pts default, 5 pts max for big items, no 13-point monolith)