Răsfoiți Sursa

docs: mark SEC_REVIEW F70 as fixed in 551cb90

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa 3 zile în urmă
părinte
comite
b7e05ca28b
1 a modificat fișierele cu 26 adăugiri și 1 ștergeri
  1. 26 1
      doc/SEC_REVIEW.md

+ 26 - 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 (37 fixed, 5 open).
+> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (27 fixed, 0 open), 42 sev-1 (38 fixed, 4 open).
 
 ---
 
@@ -2241,6 +2241,31 @@
   amplifier. Constrain `format` per consumer (only allow what the
   consumer typically uses).
 - **Severity: 1**
+- **Status:** Fixed by extending `BlocklistCache` to cache the
+  rendered body and its sha256 ETag per `(policy_id, format)`, not
+  just the underlying `Blocklist` domain object. New
+  `BlocklistCache::getRendered(Policy, format, callable $render)`
+  invokes the render callback at most once per cache window per
+  format; subsequent hits return the cached `body` + `etag`
+  verbatim. The render+hash work that previously paid the full
+  sha256-of-N-entries cost on every request now runs once per
+  cache window per replica per format, regardless of request rate.
+  `BlocklistController` switched to the new method and feeds it a
+  small closure for each format; the choice between `renderText`
+  and `renderJson` is the only thing varying per request now. The
+  per-format invalidation contract from `getOrBuild` is preserved
+  via the same `invalidate($policyId)` / `invalidateAll()`
+  surface (the rendered entries live inside the same cache row,
+  so dropping the row drops both the build and every render
+  alongside it). Per-consumer format constraint is still possible
+  as a future tightening but isn't necessary to defeat the F70
+  amplifier — the cache already does. Regression tests in
+  `api/tests/Integration/Public/BlocklistControllerTest.php`:
+  `testBlocklistRenderIsCachedAcrossRequests` (two sequential
+  JSON requests produce byte-identical bodies and ETags) and
+  `testTextAndJsonRendersBothCachedIndependently` (alternating
+  format requests preserve each format's cache; text and JSON
+  ETags differ because their bodies differ).
 
 ### F71 — `BlocklistCache` is not size-bounded
 - **File:** `api/src/Infrastructure/Reputation/BlocklistCache.php:33, 42-59`