Files
satellite-provider/_docs/05_security/owasp_review.md
T
Oleksandr Bezdieniezhnykh 5214a4a647 [AZ-487] [AZ-488] security: cycle 2 delta audit (PASS_WITH_WARNINGS)
Step 14 (Security Audit) for cycle 2 — delta scan against the cycle-1
baseline. Verdict remains PASS_WITH_WARNINGS; no Critical/High.

Scope: JWT auth boundary (AZ-487) and UAV multipart upload + ImageSharp
decode of attacker-controlled bytes (AZ-488). Both new packages
(JwtBearer 8.0.21, ImageSharp 3.1.11 in Services.TileDownloader)
checked.

Cycle-2 delta:
* 0 Critical / 0 High
* 2 Medium: F-AUTH-2 (iss/aud not validated — by design until admin
  team publishes values, AZ-487 § Constraints), F-UAV-1 (ImageSharp
  decode now runs on attacker-controlled bytes — mitigations
  sufficient; pin to GHSA subscribe-and-bump policy).
* 4 Low: F-AUTH-1 (DEV-ONLY secret in appsettings.Development.json —
  accepted), F-AUTH-3 (rate-limit gap extends to 401 floods — folds
  into cycle-1 I3), F-UAV-2 (JsonDocument.Parse on signature-validated
  claims — bounded by Kestrel header cap), D3 (JwtBearer shares D1
  patch line).
* 1 Informational: F-UAV-3 (reject reasons disclose gate structure —
  accepted UX trade-off; documented in contract).

OWASP refresh: A01 / A07 move from N/A (with caveat) to
PASS_WITH_WARNINGS (per-tenant authz absent; iss/aud + revocation
gaps tracked).

Pre-deploy operational gate added: deploy pipeline must verify
JWT_SECRET != DEV-ONLY placeholder before promoting api.

Artifacts: dependency_scan.md, static_analysis.md, owasp_review.md,
infrastructure_review.md, security_report.md — all appended with a
"Cycle 2 Delta" section preserving cycle-1 finding IDs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 00:13:58 +03:00

9.8 KiB

Phase 3 — OWASP Top 10:2025 Review

Date: 2026-05-11 OWASP version: OWASP Top 10:2025 (verified at audit start) Project context: Self-hosted .NET 8 backend service. Documented as an "internal/trusted network service — no auth layer" (_docs/02_document/architecture.md §7). Deployed via Docker behind another network boundary (per _docs/02_document/deployment/). The audit is scoped to the codebase as it stands; categories whose findings depend on a missing trust-boundary control are flagged accordingly.

# Category Status Findings Notes
A01 Broken Access Control N/A (with caveat) The service intentionally exposes ALL endpoints without authentication or authorization — documented design (architecture.md §7). No IDOR analysis applies because there is no user concept. Caveat: this is only safe if the deployment puts the API behind a network-level gatekeeper (VPN, mTLS, internal-only LB). If the deploy ever moves to a public network, this category becomes the #1 risk and EVERY endpoint becomes an unauthenticated execution surface.
A02 Security Misconfiguration FAIL S1, S2, I1, I2 Default Postgres credentials in both appsettings.json and docker-compose.yml; Postgres port bound to 0.0.0.0; container runs as root; no security headers middleware.
A03 Software Supply Chain Failures PASS_WITH_WARNINGS D1, D2 Two known transitive CVEs (D1 — ASP.NET Core 8.0.21 SignalR DoS, not exploitable here; D2 — Microsoft.NET.Test.Sdk 17.8.0 → NuGet.Frameworks info disclosure, test-only). No use of unsigned NuGet packages; no auto-update of dependencies in production.
A04 Cryptographic Failures N/A No password storage (no users), no encryption at rest, no in-app crypto. The Google Maps integration uses HTTPS (default Npgsql/HttpClient stacks). At-rest tile storage is plain JPEG by design — these are public satellite images, not confidential data.
A05 Injection PASS All Dapper queries use parameter objects (new { Id = id } etc.); no string-built or interpolated user input flows into SQL. No Process.Start, no shell exec, no eval. JSON deserialization uses System.Text.Json defaults (no type-name handling). XSS / template injection N/A — JSON-only API.
A06 Insecure Design FAIL S3, I3 No rate limiting on any endpoint despite the existence of an outbound rate-limited dependency (Google Maps). Latitude / longitude inputs are not range-validated at the API boundary (S3). No quota / throttling on region-request creation, which can multiply outbound calls and disk writes.
A07 Authentication Failures N/A (with caveat) Same caveat as A01 — there is no authentication system to fail.
A08 Software or Data Integrity Failures PASS DbUp migrations are idempotent and tracked in schemaversions; rollback is forward-only by design. No auto-update path. CI artifacts go through .woodpecker/02-build-push.yml with from_secret: registry_token (not in plaintext). No unsigned external scripts executed at build/deploy.
A09 Security Logging and Alerting Failures PASS_WITH_WARNINGS I4 Serilog writes structured logs with file rotation; GlobalExceptionHandler correlates server logs to client responses via correlationId (good). However: no security-event logging (e.g., bad-input bursts, repeated 4xx from same source) and no alerting on log patterns. Acceptable for an internal service; would need attention if exposed publicly.
A10 Mishandling of Exceptional Conditions PASS GlobalExceptionHandler returns RFC 7807 ProblemDetails with a generic body and a correlationId — no exception text leaks to clients. GlobalExceptionHandlerTests.cs includes a positive control that confirms a "leakySecret"-shaped exception message is NOT echoed.

Cross-reference to Phase 1 / Phase 2 findings

OWASP Cat Tied finding Severity Source phase
A02 S1 — default password in appsettings.json Medium Phase 2
A02 S2 — weak Postgres creds + 0.0.0.0 binding in compose Medium Phase 2
A02 I1 — Dockerfile runs as root Low Phase 4 (next)
A02 I2 — no security headers middleware Low Phase 4 (next)
A03 D1 — CVE-2026-26130 in ASP.NET Core 8.0.21 (SignalR; not reachable) Medium (paper) / Low (real) Phase 1
A03 D2 — CVE-2022-30184 transitive via test SDK Low (test-only) Phase 1
A06 S3 — lat/lon not range-validated at API boundary Low Phase 2
A06 I3 — no rate limiting on any endpoint Medium Phase 4 (next)
A06 S4 — Google Maps API key handling (no .env.example, no rotation hygiene) Medium Phase 2
A09 I4 — no security-event logs, no alerting Low Phase 4 (next)

Self-verification

  • All current OWASP Top 10:2025 categories assessed
  • Each FAIL has at least one specific finding with evidence
  • N/A categories have justification + caveat
  • No security_approach.md exists in _docs/00_problem/ to cross-reference (project has not declared explicit security requirements; this audit treats the architecture-vision statement "internal/trusted network service" as the de-facto requirement)

Cycle 2 Refresh (AZ-487 + AZ-488)

Cycle 1's A01 / A07 verdicts were N/A (with caveat) because the service shipped without authentication. AZ-487 (JWT validation baseline) and AZ-488 (UAV upload permission policy) materially change those verdicts. The table below supersedes the cycle-1 row for A01 and A07; all other rows remain as cycle 1 left them, with cycle-2 findings appended where applicable.

# Category Cycle 1 Status Cycle 2 Status Cycle-2 evidence
A01 Broken Access Control N/A (with caveat) PASS_WITH_WARNINGS Every endpoint requires RequireAuthorization() (AZ-487); POST /api/satellite/upload requires the GPS permission via PermissionsRequirement (AZ-488). No IDOR analysis is needed because the service has no per-user data partitioning — every authenticated principal can read every tile. Warning: per-tenant authorization (e.g. "this UAV may only upload over its assigned region") is not enforced. If a future contract demands it, A01 immediately re-opens.
A02 Security Misconfiguration FAIL (S1, S2, I1, I2) FAIL (unchanged + F-AUTH-1) Cycle-2 ships a clearly-labelled DEV-ONLY JWT secret in appsettings.Development.json. Production override path is correct (env-var wins); deploy gate must check JWT_SECRET. No new cycle-1 findings resolved.
A03 Supply Chain Failures PASS_WITH_WARNINGS (D1, D2) PASS_WITH_WARNINGS (+ D3, F-DEPS-UAV) New JwtBearer 8.0.21 package shares the D1 patch line; new ImageSharp call site widens decoder exposure (mitigations sufficient — see static_analysis.md F-UAV-1).
A04 Cryptographic Failures N/A PASS HS256 token validation uses Microsoft.IdentityModel's SymmetricSecurityKey with RequireSignedTokens = true and RequireExpirationTime = true. The alg=none bypass is blocked by RequireSignedTokens; algorithm-confusion is bounded because only one signing key is registered. Secret length ≥ 32 bytes enforced at startup.
A05 Injection PASS PASS No new SQL / shell / template surfaces. The new JSON parse (PermissionsAuthorizationHandler) runs on signature-validated token bytes — see F-UAV-2 disposition.
A06 Insecure Design FAIL (S3, S4, I3) FAIL (+ F-AUTH-3, F-UAV-3) Rate limiting still absent (now also a 401-flood vector). UAV reject reasons disclose gate structure — accepted UX trade-off, flagged for operator awareness.
A07 Identification & Authentication Failures N/A (with caveat) PASS_WITH_WARNINGS (+ F-AUTH-2) HS256 with secret ≥ 32 bytes; lifetime + signature validation; ClockSkew = 30 s. Warning: ValidateIssuer = false, ValidateAudience = false per the suite contract — any service that holds JWT_SECRET can mint tokens accepted here. Track until admin team defines iss/aud. No token revocation list — leaked tokens stay valid until exp.
A08 Software or Data Integrity Failures PASS PASS AZ-488 file-first-then-row write order documented; same migration / CI discipline as cycle 1.
A09 Security Logging Failures PASS_WITH_WARNINGS (I4) PASS_WITH_WARNINGS (unchanged) No new logging changes; 401 responses are not currently aggregated for alerting (out of scope for internal service).
A10 Mishandling of Exceptional Conditions PASS PASS UAV decode failures wrapped in scoped try/catch for UnknownImageFormatException / InvalidImageContentException — produce structured INVALID_FORMAT rejects, no stack-trace leak. SEC-11 test verifies reject details have no path / exception-type leakage.

Cycle-2 cross-reference

OWASP Cat Cycle-2 finding Severity Source phase
A01 A01 status now PASS_WITH_WARNINGS (per-tenant authz absent) — (status note) Phase 3
A02 F-AUTH-1 — DEV-ONLY secret in appsettings.Development.json Low (accepted) Phase 2
A03 D3 — JwtBearer 8.0.21 shares D1 patch line Low Phase 1
A03 F-DEPS-UAV — ImageSharp decode exposure widened Medium Phase 1
A06 F-AUTH-3 — rate-limit gap now also covers 401 floods Low (recurrence of I3) Phase 2
A06 F-UAV-3 — reject reasons disclose gate structure Informational (accepted) Phase 2
A07 F-AUTH-2 — iss/aud not validated; no revocation list Medium Phase 2
(claim handler) F-UAV-2 — JsonDocument.Parse on token claim values Low Phase 2