Captures the post-implementation autodev gates for AZ-484 multi-source tile storage: - Step 12 (Test-Spec Sync): added 7 AC rows (AZ-484 AC-1..AC-7) and a PT-07 NFR row to traceability-matrix.md; added PT-07 scenario to performance-tests.md. - Step 13 (Update Docs): refreshed data_model.md (tiles columns + indexes + selection rule + UPSERT contract + migrations 012/013), module-layout.md (Common/Enums section with L-001 guidance, DataAccess imports-from now lists 6 sites), 6 module / component docs to reflect the new repo signatures, source/captured_at fields, and Dapper enum bypass workaround. ripple_log_cycle1.md records zero out-of-scope ripple. - Step 14 (Security Audit): PASS_WITH_WARNINGS - 0 Critical, 0 High, 5 Medium, 5 Low. AZ-484 itself added zero new findings. Hardening items (Postgres default creds, .env in build context, GMaps key rotation, ASP.NET Core 8.0.21 -> 8.0.25, rate limiter) recorded for separate tickets. - Step 15 (Performance Test): all PT-01..PT-07 scenarios Unverified (non-blocking); PT-07 baseline-comparison harness deferred to a leftover for next cycle. - Step 16 (Deploy): cycle deploy report covering migration safety, rollback path, post-deploy verification, security caveats. Co-authored-by: Cursor <cursoragent@cursor.com>
9.4 KiB
Security Audit Report
Date: 2026-05-11
Scope: Satellite Provider — full repository (Api, Common, DataAccess, Services.*, Tests, infra)
Trigger: /autodev Step 14 (Security Audit) — feature cycle 1, post-AZ-484
Verdict: PASS_WITH_WARNINGS
Summary
| Severity | Count |
|---|---|
| Critical | 0 |
| High | 0 |
| Medium | 5 |
| Low | 5 |
No Critical or High findings. The verdict is PASS_WITH_WARNINGS driven by 5 Medium findings, all of which are well-understood configuration / hardening gaps rather than exploitable vulnerabilities in the application logic itself. AZ-484 (the cycle's only feature change) introduced zero new findings — it is a pure data-layer change with no auth surface, no untrusted-input handling, and no new external dependencies.
OWASP Top 10:2025 Assessment
| Category | Status | Findings |
|---|---|---|
| A01 Broken Access Control | N/A (with caveat) | — |
| A02 Security Misconfiguration | FAIL | S1, S2/I6, I1, I2 |
| A03 Software Supply Chain Failures | PASS_WITH_WARNINGS | D1, D2 |
| A04 Cryptographic Failures | N/A | — |
| A05 Injection | PASS | — |
| A06 Insecure Design | FAIL | S3, S4, I3 |
| A07 Authentication Failures | N/A (with caveat) | — |
| A08 Software or Data Integrity Failures | PASS | — |
| A09 Security Logging and Alerting Failures | PASS_WITH_WARNINGS | I4 |
| A10 Mishandling of Exceptional Conditions | PASS | — |
The two N/A (with caveat) entries (A01, A07) reflect the documented architectural choice (architecture.md §7) that this is an internal/trusted-network service. The audit does not endorse that choice — it merely notes that the choice has been made deliberately. If the deployment trust boundary ever changes, A01 and A07 immediately become FAIL and every endpoint becomes an unauthenticated surface; that decision must be re-examined before any internet-facing exposure.
Findings
| # | Severity | Category | Location | Title |
|---|---|---|---|---|
| S1 | Medium | A02 — Misconfiguration | SatelliteProvider.Api/appsettings.json:24 |
Default Postgres password (postgres/postgres) committed in appsettings.json |
| S2 | Medium | A02 — Misconfiguration | docker-compose.yml:6-7,30 |
Weak Postgres credentials in compose (mirrors S1) |
| S3 | Low | A06 — Insecure Design | SatelliteProvider.Api/Program.cs:169,207,237 |
Latitude/longitude inputs not range-validated at API boundary |
| S4 | Medium | A06 — Insecure Design | .env (workspace root) |
Apparent real Google Maps API key on developer filesystem; no .env.example |
| D1 | Medium | A03 — Supply Chain | SatelliteProvider.Api.csproj — Microsoft.AspNetCore.OpenApi 8.0.21 |
CVE-2026-26130 SignalR DoS (not reachable in this app — codebase has zero SignalR use) |
| D2 | Low | A03 — Supply Chain | SatelliteProvider.Tests.csproj — Microsoft.NET.Test.Sdk 17.8.0 |
CVE-2022-30184 transitive via NuGet.Frameworks <6.2.1 (test-only) |
| I1 | Low | A02 — Misconfiguration | SatelliteProvider.Api/Dockerfile |
Container runs as root (no USER directive) |
| I2 | Low | A02 — Misconfiguration | SatelliteProvider.Api/Program.cs |
No security headers middleware |
| I3 | Medium | A06 — Insecure Design | SatelliteProvider.Api/Program.cs |
No inbound rate limiting on any HTTP endpoint |
| I4 | Low | A09 — Logging | SatelliteProvider.Api/* (logging strategy) |
No security-event logs / alerting |
| I5 | Medium | A02 — Misconfiguration | .dockerignore + Dockerfile:15 (COPY . .) |
.env not in .dockerignore — risk of API key being baked into image layers |
I6 in the infra report is a duplicate of S2 (same root cause) and is not double-counted in the summary.
Finding Details
Full evidence and remediation for every finding lives in the per-phase reports. The detail tables there are the source of truth — this top-level report intentionally avoids restating multi-paragraph remediation steps.
- Phase 1:
_docs/05_security/dependency_scan.md— D1, D2, full dependency inventory + cross-version sanity check - Phase 2:
_docs/05_security/static_analysis.md— S1, S2, S3, S4, plus the categories that were checked clean (SQL injection, command injection, deserialization, path traversal, log leakage, exception leakage) - Phase 3:
_docs/05_security/owasp_review.md— OWASP Top 10:2025 per-category assessment + cross-reference table - Phase 4:
_docs/05_security/infrastructure_review.md— I1, I2, I3, I4, I5, I6, plus the items checked clean (CI secrets handling, image attribution labels)
Dependency Vulnerabilities
| Package | CVE | Severity | Reachable? | Fix Version |
|---|---|---|---|---|
| Microsoft.AspNetCore.OpenApi (→ ASP.NET Core 8 runtime) | CVE-2026-26130 | High (paper) / Low (this app) | No — codebase has zero SignalR use | 8.0.25 |
| Microsoft.NET.Test.Sdk → NuGet.Frameworks | CVE-2022-30184 | Medium (paper) / Low (this app) | Test project only — never shipped | Microsoft.NET.Test.Sdk 17.9.0+ |
All other dependencies (Newtonsoft.Json 13.0.4, SixLabors.ImageSharp 3.1.11, Npgsql 9.0.2, Dapper 2.1.35, Swashbuckle.AspNetCore 6.6.2, Serilog.AspNetCore 8.0.3, dbup-postgresql 6.0.3, Microsoft.Extensions.* 9.0.10) are at or beyond the patched line for every CVE I could find.
AZ-484 Cycle-Specific Verdict
The reason this audit was triggered (the AZ-484 multi-source tile storage cycle) is independently clean:
- Migration 013 is transactional and idempotent — no data loss / data integrity finding.
TileSourceConverterenforces a closed value space at the language layer;TileEntity.Sourceisstringonly as a Dapper-bug workaround documented in_docs/LESSONS.mdL-001.TileRepositoryqueries continue to use parameterised Dapper — no new SQL injection surface.- No new external dependencies, no new endpoints, no new untrusted-input flows.
- All findings in this report predate AZ-484 and are unchanged by it.
Recommendations
Immediate (Critical/High)
None — there are no Critical or High findings. The audit does not block the next deploy on its own merit.
Short-term (Medium — pick before next public-network exposure or any post-deploy hardening pass)
- S1 + S2 + I5 — De-default DB credentials and stop shipping the .env into the build context. One coordinated change:
- Remove
ConnectionStrings:DefaultConnectionfromappsettings.json(rely on env-var via the existing throw on null). - Add
POSTGRES_USER/POSTGRES_PASSWORDto a tracked.env.exampleand source them from a dev.env; bind5432to127.0.0.1. - Append
.envand.env.*(with!.env.exampleexception) to.dockerignore.
- Remove
- S4 — Rotate the Google Maps API key out-of-band, add
.env.example, add Google Cloud key restrictions (HTTP referrer or IP allowlist + per-API quotas). The audit deliberately did not echo the key value into any artifact. - D1 — Bump
Microsoft.AspNetCore.OpenApifrom8.0.21to the current 8.0.x patch (≥ 8.0.25) and rebuild the deployed image so the vulnerable SignalR code paths are physically absent. - I3 — Wire
Microsoft.AspNetCore.RateLimiting(built into .NET 8 — no new package). Conservative starting threshold in the per-phase report.
Long-term (Low — hardening backlog)
- I1 — Add a non-root
USERto the API Dockerfile. - I2 — Add a tiny security-headers middleware (or pull
NWebsec.AspNetCore.Middleware). - S3 — Add explicit lat/lon range guards at the API boundary (matches the existing
SizeMeters100-10000 pattern). - D2 — Bump
Microsoft.NET.Test.Sdkto ≥ 17.9.0 next time the test project's deps are touched. - I4 — Defer until the trust boundary changes; if/when the API moves toward a less-trusted network, add structured 4xx logging per IP + a basic alerting rule.
Verdict Logic
- No Critical or High findings → not FAIL
- 5 Medium + 5 Low findings exist → not PASS
- Therefore: PASS_WITH_WARNINGS
This satisfies the autodev gate to proceed to Step 15 (Performance Test). The recommendations above should be tracked as separate Jira tasks under a hardening epic before the first non-internal deployment.
Self-verification
- All findings from Phases 1–4 included
- No duplicate findings (I6 explicitly noted as a duplicate of S2 and not double-counted)
- Every finding has remediation guidance (in per-phase reports)
- Verdict matches severity logic (no Critical/High → not FAIL; >0 findings → not PASS)
- No real secret values printed in any audit artifact (S4 described without echoing the API key)