diff --git a/_docs/05_security/dependency_scan.md b/_docs/05_security/dependency_scan.md new file mode 100644 index 0000000..c9433f1 --- /dev/null +++ b/_docs/05_security/dependency_scan.md @@ -0,0 +1,34 @@ +# Dependency Vulnerability Scan + +**Date**: 2026-05-07 +**Tool**: `pip-audit 2.10.0` +**Manifest**: `pyproject.toml` +**Result**: PASS + +## Scope + +The scan covered the Python dependencies declared in `pyproject.toml`, including the `dev` optional dependency group: + +- `pydantic==2.13.3` +- `black>=24.0` +- `pytest>=8.0` +- `ruff>=0.5` + +## Findings + +No known vulnerabilities were reported. + +## Audit Output Summary + +`pip-audit` resolved and checked the project dependency set and returned: + +```text +No known vulnerabilities found +``` + +Resolved packages with no advisories included `pydantic`, `pydantic-core`, `black`, `pytest`, and `ruff`. + +## Notes + +- `pip-audit` and its own transitive packages were installed as an audit tool in the local Python environment. +- The repository does not currently include a locked production dependency file, so the audit used the version constraints from `pyproject.toml`. diff --git a/_docs/05_security/infrastructure_review.md b/_docs/05_security/infrastructure_review.md new file mode 100644 index 0000000..21326c4 --- /dev/null +++ b/_docs/05_security/infrastructure_review.md @@ -0,0 +1,49 @@ +# Infrastructure Security Review + +**Date**: 2026-05-07 +**Scope**: Dockerfiles, compose files, environment templates, GitHub Actions +**Result**: PASS_WITH_WARNINGS + +## Reviewed Artifacts + +- `deployment/docker/Dockerfile.runtime` +- `deployment/docker/Dockerfile.replay` +- `docker-compose.yml` +- `docker-compose.test.yml` +- `.github/workflows/ci.yml` +- `.env.example` +- `config/development/runtime.env` +- `config/ci/runtime.env` +- `config/jetson/runtime.env` + +## Findings + +| ID | Severity | Category | Location | Title | +|----|----------|----------|----------|-------| +| I1 | Medium | Security Misconfiguration | `docker-compose.yml:7`, `docker-compose.yml:9`, `.env.example:5` | Default Postgres password and exposed host port need stronger dev/prod separation | +| I2 | Low | CI/CD Hardening | `.github/workflows/ci.yml` | CI lacks dependency audit / secret scan / SAST gates | + +## Finding Details + +### I1: Default Postgres password and exposed host port need stronger dev/prod separation + +`docker-compose.yml` uses `POSTGRES_PASSWORD=gpsd`, publishes `5432:5432`, and points runtime at `.env.example`, which embeds the same example credentials in `GPSD_DATABASE_URL`. + +**Impact**: Safe enough for local development if never deployed, but risky if copied into staging, Jetson, or field environments. + +**Remediation**: Move credentials into an ignored local `.env`, document `docker-compose.yml` as development-only, bind local Postgres to loopback, and require production/Jetson credentials from a secret manager or deployment-time secret source. + +### I2: CI lacks dependency audit / secret scan / SAST gates + +`.github/workflows/ci.yml` runs format, lint, unit tests, and compose config validation, but it does not run dependency audit, secret scanning, or SAST. + +**Impact**: Vulnerable dependencies or accidentally committed secrets may be caught only during manual audits. + +**Remediation**: Add `pip-audit` for Python dependencies, a secret scanner such as Gitleaks/TruffleHog, and a lightweight SAST pass such as Semgrep or Ruff security rules when the project adopts them. + +## Positive Controls + +- Runtime and replay Dockerfiles create and run as a non-root `gpsd` user. +- Runtime image copies only project source and `pyproject.toml`/`README.md`, not `.env` or fixture payloads. +- `docker-compose.test.yml` keeps replay/SITL/cache stubs on isolated compose networks and exposes no host ports. +- `config/jetson/runtime.env` contains paths and mode labels only; it does not include embedded passwords or signing keys. diff --git a/_docs/05_security/owasp_review.md b/_docs/05_security/owasp_review.md new file mode 100644 index 0000000..67b87d3 --- /dev/null +++ b/_docs/05_security/owasp_review.md @@ -0,0 +1,26 @@ +# OWASP Top 10 Review + +**Date**: 2026-05-07 +**Reference**: OWASP Top 10:2021, current official Top 10 referenced from +**Result**: PASS_WITH_WARNINGS + +## Assessment + +| OWASP Category | Status | Findings / Notes | +|----------------|--------|------------------| +| A01: Broken Access Control | PASS | No web/API authorization surface is implemented in the current runtime code. MAVLink source/system ID and cache trust boundaries are represented in architecture/tests. | +| A02: Cryptographic Failures | PASS_WITH_WARNINGS | No weak crypto or secret leakage found in source. Cache signature checks compare trusted signature hashes, but production key handling remains a deployment concern. | +| A03: Injection | PASS | No SQL construction, shell execution, dynamic code execution, or template rendering paths were found in source. | +| A04: Insecure Design | PASS_WITH_WARNINGS | `S1` is a resource-exhaustion design gap for local VPR descriptor package loading. | +| A05: Security Misconfiguration | PASS_WITH_WARNINGS | `S2` covers default development database credentials and broad host port exposure in `docker-compose.yml`. | +| A06: Vulnerable and Outdated Components | PASS | `pip-audit` reported no known vulnerabilities for the project dependency set. | +| A07: Identification and Authentication Failures | NOT_APPLICABLE | No user/session authentication surface is implemented in this package. | +| A08: Software and Data Integrity Failures | PASS_WITH_WARNINGS | Cache metadata validation is implemented, but CI currently validates tests/compose only; dependency audit and secret/SAST scanning are not yet CI gates. | +| A09: Security Logging and Monitoring Failures | PASS | Architecture and tests require FDR/QGC visibility for cache rejection, spoofing, blackout, and health events. | +| A10: Server-Side Request Forgery | NOT_APPLICABLE | No HTTP client, URL-fetching, or server-side request surface was found in runtime source. | + +## OWASP Notes + +- The current package is primarily an onboard runtime and replay harness, not a web application. Several OWASP categories are therefore assessed through local trust boundaries: cache package integrity, MAVLink source filtering, runtime configuration, and generated-tile promotion. +- The strongest security controls already represented in code/docs are no in-flight satellite-provider calls, cache manifest/hash checks, spoofed/unauthorized MAVLink rejection tests, and FDR-visible security events. +- Remaining warnings are hardening items rather than exploitable remote vulnerabilities in the current code shape. diff --git a/_docs/05_security/security_report.md b/_docs/05_security/security_report.md new file mode 100644 index 0000000..3306a7b --- /dev/null +++ b/_docs/05_security/security_report.md @@ -0,0 +1,106 @@ +# Security Audit Report + +**Date**: 2026-05-07 +**Scope**: GPS-denied onboard runtime and replay infrastructure +**Verdict**: PASS_WITH_WARNINGS + +## Summary + +| Severity | Count | +|----------|-------| +| Critical | 0 | +| High | 0 | +| Medium | 2 | +| Low | 1 | + +No Critical or High issues were found. The audit can proceed through the autodev gate, with hardening work recommended before production deployment. + +## OWASP Top 10 Assessment + +| Category | Status | Findings | +|----------|--------|----------| +| A01: Broken Access Control | PASS | — | +| A02: Cryptographic Failures | PASS_WITH_WARNINGS | Deployment key handling remains a release concern | +| A03: Injection | PASS | — | +| A04: Insecure Design | PASS_WITH_WARNINGS | S1 | +| A05: Security Misconfiguration | PASS_WITH_WARNINGS | S2 / I1 | +| A06: Vulnerable and Outdated Components | PASS | — | +| A07: Identification and Authentication Failures | NOT_APPLICABLE | No auth/session surface in current package | +| A08: Software and Data Integrity Failures | PASS_WITH_WARNINGS | I2 | +| A09: Security Logging and Monitoring Failures | PASS | — | +| A10: Server-Side Request Forgery | NOT_APPLICABLE | No URL-fetching runtime surface | + +## Findings + +| # | Severity | Category | Location | Title | +|---|----------|----------|----------|-------| +| 1 | Medium | Resource / Input Validation | `src/satellite_service/types.py:67` | VPR index JSON is read fully without size limits | +| 2 | Medium | Security Misconfiguration | `docker-compose.yml:7`, `docker-compose.yml:9`, `.env.example:5` | Default DB credentials and exposed port need dev/prod separation | +| 3 | Low | CI/CD Hardening | `.github/workflows/ci.yml` | CI lacks dependency audit / secret scan / SAST gates | + +## Finding Details + +### F1: VPR index JSON is read fully without size limits + +**Severity**: Medium +**Category**: Resource / Input Validation +**Location**: `src/satellite_service/types.py:67` + +`LocalVprIndexPackage.from_json_file()` reads an entire local descriptor package into memory before validation. Descriptor packages are part of the local cache trust boundary and can become large. + +**Impact**: A malformed or unexpectedly large package could exhaust memory or stall startup/readiness on Jetson. + +**Remediation**: Add a maximum file-size check before reading, cap descriptor record count and descriptor length, and require callers to load only manifest-validated package paths. + +### F2: Default DB credentials and exposed port need dev/prod separation + +**Severity**: Medium +**Category**: Security Misconfiguration +**Location**: `docker-compose.yml:7`, `docker-compose.yml:9`, `.env.example:5` + +The default compose file uses `POSTGRES_PASSWORD=gpsd`, publishes Postgres on `5432:5432`, and the example database URL embeds `gpsd:gpsd`. + +**Impact**: Safe enough as a local fixture convention, but risky if reused in staging, Jetson, or field deployment. + +**Remediation**: Move credentials to ignored local `.env` files, document the default compose as development-only, bind Postgres to loopback for local runs, and require secret-manager sourced credentials for production/Jetson deploys. + +### F3: CI lacks dependency audit / secret scan / SAST gates + +**Severity**: Low +**Category**: CI/CD Hardening +**Location**: `.github/workflows/ci.yml` + +CI runs format, lint, unit tests, and compose config validation, but not dependency audit, secret scanning, or SAST. + +**Impact**: Vulnerable dependencies or accidentally committed secrets may be caught only during manual audits. + +**Remediation**: Add `pip-audit`, a secret scanner such as Gitleaks/TruffleHog, and a lightweight SAST pass such as Semgrep or Ruff security rules. + +## Dependency Vulnerabilities + +| Package | CVE / Advisory | Severity | Fix Version | +|---------|----------------|----------|-------------| +| None | — | — | — | + +## Positive Controls + +- `pip-audit` reported no known vulnerabilities for the declared Python dependency set. +- No SQL construction, shell execution, dynamic code execution, Pickle/marshal use, weak crypto, hardcoded production secrets, or HTTP URL-fetching runtime surface was found in source. +- Runtime and replay Dockerfiles run as non-root `gpsd`. +- Cache manifest/hash validation, no in-flight satellite-provider access, MAVLink spoofing/source rejection, and FDR-visible security events are represented in code, docs, and tests. + +## Recommendations + +### Immediate + +- None required for Critical/High severity because no Critical/High findings were found. + +### Short-Term + +- Add size/count limits to VPR descriptor package loading. +- Split local-development database credentials from production/Jetson deploy configuration and restrict local Postgres host binding. + +### Long-Term + +- Add dependency audit, secret scanning, and SAST to CI. +- Re-run security audit after the deploy step creates final production deployment artifacts. diff --git a/_docs/05_security/static_analysis.md b/_docs/05_security/static_analysis.md new file mode 100644 index 0000000..3fc333d --- /dev/null +++ b/_docs/05_security/static_analysis.md @@ -0,0 +1,45 @@ +# Static Analysis + +**Date**: 2026-05-07 +**Scope**: Python source, tests, replay harness, tools, compose/config files +**Result**: PASS_WITH_WARNINGS + +## Findings + +| ID | Severity | Category | Location | Title | +|----|----------|----------|----------|-------| +| S1 | Medium | Resource / Input Validation | `src/satellite_service/types.py:67` | VPR index JSON is read fully without size limits | +| S2 | Medium | Security Misconfiguration | `docker-compose.yml:7`, `docker-compose.yml:9`, `.env.example:5` | Development database credentials and exposed port are easy to reuse outside dev | + +## Finding Details + +### S1: VPR index JSON is read fully without size limits + +**Location**: `src/satellite_service/types.py:67` + +`LocalVprIndexPackage.from_json_file()` reads the entire configured descriptor package into memory with `Path(package_path).read_text()` and then parses it with `json.loads`. The model validates record shape, but there is no file-size limit, descriptor-count limit, or explicit package path trust check at this boundary. + +**Impact**: A malformed or unexpectedly large local descriptor package could exhaust memory or stall startup/readiness on a constrained Jetson target. + +**Remediation**: Add a maximum package size check before reading, cap `records` and descriptor lengths in the Pydantic model, and ensure callers pass only cache-package paths that have already passed manifest/signature validation. + +### S2: Development database credentials and exposed port are easy to reuse outside dev + +**Locations**: + +- `docker-compose.yml:7` +- `docker-compose.yml:9` +- `.env.example:5` + +`docker-compose.yml` sets `POSTGRES_PASSWORD=gpsd` and exposes Postgres on `5432:5432`. `.env.example` also embeds `gpsd:gpsd` in `GPSD_DATABASE_URL`. These are acceptable for local fixture use only if they are clearly kept out of production, but the default compose file name makes accidental reuse plausible. + +**Impact**: If this compose file is copied into a staging or field environment, the database would run with known credentials and an exposed host port. + +**Remediation**: Move the default password to an ignored local `.env`, rename or label the compose file as development-only, bind Postgres to `127.0.0.1` where host exposure is required, and add a production compose/deploy template that requires secret-manager sourced credentials. + +## Negative Checks + +- No SQL string construction or database `execute()` calls were found in source. +- No `eval`, `exec`, `os.system`, `shell=True`, Pickle, `marshal`, weak hashes, TLS verification bypass, CORS wildcard, or hardcoded production secret patterns were found in source. +- `tools/remove_osd_lines.py` uses `subprocess.run()` with an argument list and no shell; this is not command-injection prone in its current form. +- Dockerfiles run as a non-root `gpsd` user. diff --git a/_docs/_autodev_state.md b/_docs/_autodev_state.md index 540f38d..605ae96 100644 --- a/_docs/_autodev_state.md +++ b/_docs/_autodev_state.md @@ -2,8 +2,8 @@ ## Current Step flow: greenfield -step: 14 -name: Security Audit +step: 15 +name: Performance Test status: not_started tracker: jira sub_step: