Explorar el Código

docs: mark SEC_REVIEW F50 as fixed in 6cc66ef

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa hace 3 días
padre
commit
1ed9341c2c
Se han modificado 1 ficheros con 33 adiciones y 1 borrados
  1. 33 1
      doc/SEC_REVIEW.md

+ 33 - 1
doc/SEC_REVIEW.md

@@ -11,7 +11,7 @@
 >
 > Each finding is referenced as **F<N>** for later citation.
 >
-> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (27 fixed, 0 open), 42 sev-1 (17 fixed, 25 open).
+> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (27 fixed, 0 open), 42 sev-1 (18 fixed, 24 open).
 
 ---
 
@@ -1646,6 +1646,38 @@
   `allow_redirects => ['max'=>3,'protocols'=>['https'],'strict'=>true,'referer'=>false]`
   plus a private-IP guard.
 - **Severity: 1**
+- **Status:** Fixed. Two layers added to the GeoIP-downloader Guzzle
+  client in `api/src/App/Container.php`:
+  1. **Tight `allow_redirects`.** `['max' => 3, 'protocols' =>
+     ['https'], 'strict' => true, 'referer' => false,
+     'track_redirects' => false]`. Caps the chain at 3 hops, refuses
+     to follow `http://` or `file://` redirects, and never sends a
+     Referer.
+  2. **`PrivateHostGuardMiddleware`.** New handler-stack middleware
+     in `api/src/Infrastructure/Enrichment/Downloaders/`. Inspects
+     the URL's literal host on every outgoing request — including
+     each redirect target — and throws
+     `GuzzleHttp\Exception\TransferException` before opening a socket
+     to a loopback / link-local / RFC1918 / CGNAT / multicast
+     address (IPv4 + IPv6) or a known instance-metadata hostname
+     (`169.254.169.254`, `metadata.google.internal`, `localhost`,
+     `0.0.0.0`). The post-redirect
+     `http://169.254.169.254/...` / `https://localhost/...` /
+     `https://10.0.0.1/...` patterns therefore die at the request
+     layer rather than reaching the network. The guard inspects
+     literal hosts only — pinning DNS to catch a public hostname
+     pointing at a private IP is out of scope for "defence in depth";
+     the primary controls are the constant base URL plus the
+     `protocols => ['https']` redirect gate.
+  Regression tests in
+  `api/tests/Unit/Enrichment/PrivateHostGuardMiddlewareTest.php`:
+  17 blocked-host data-provider cases (IPv4 + IPv6 across loopback,
+  link-local, RFC1918, CGNAT, multicast, all-zero, metadata IP,
+  metadata hostname, `localhost`), 6 allowed-host cases (`download.
+  db-ip.com`, `download.maxmind.com`, `ipinfo.io`, just-outside-
+  172.16/12, `1.1.1.1`, public IPv6), `testFactoryProducesMiddleware
+  ThatGuardsBeforeHandler` proving the inner handler is not
+  invoked on the blocked branch, and `testEmptyHostIsRejected`.
 
 ### F51 — `RoleMappingRepository` placeholder generation does not enforce list shape
 - **File:** `api/src/Infrastructure/Auth/RoleMappingRepository.php:31-36`