Address review findings on the cover-path deletion (#63)

- Move the kagane cover URL shape into the extraction module (sites.go):
  browserOnlyCoverURL + kaganeImageURLRe now own the claim; the byte-fetch
  router and BrowserFetcher.Image reference it. One shape gate for producer
  and fetcher (the id regex is folded into the full-URL match), so no Site
  name appears in a cover path outside the extraction module and the
  producer cannot emit an address the fetch would refuse.
- Restore the serving-boundary guarantee: GET /covers/{addr} re-checks the
  stored media type via store.CoverContentType and 404s a poisoned row;
  TestPublicCoverNeverEchoesNonImage now seeds one directly behind the
  write gate and pins the refusal where bytes leave.
- Restore the SSRF rationale (client-supplied stored URL, headless browser
  as a strong primitive) on the URL regex.
This commit is contained in:
2026-08-10 11:36:54 +07:00
parent cce3d61799
commit 0a79e5f3d7
6 changed files with 60 additions and 34 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ type CoverBytesFetcher interface {
// fallback onto a path that cannot succeed. One routing rule for the poll and
// the acquirer, so the two cannot drift apart.
func fetchCoverBytes(ctx context.Context, cover string, browser BrowserCoverFetcher, tls CoverBytesFetcher) ([]byte, string, error) {
if browserCoverURL(cover) {
if browserOnlyCoverURL(cover) {
if browser == nil {
return nil, "", errors.New("no cover fetcher")
}