mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 08:41:12 +00:00
[AZ-243] Record security audit
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,26 @@
|
||||
# OWASP Top 10 Review
|
||||
|
||||
**Date**: 2026-05-07
|
||||
**Reference**: OWASP Top 10:2021, current official Top 10 referenced from <https://owasp.org/www-project-top-ten/>
|
||||
**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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user