refactor(cache): generalize content-type guard and gate bypass to privileged callers

Addresses review feedback on the prior commit:

- The shared cache middleware should not hardcode `image/*`. Replace the
  inline content-type check with a generic `cache.contentType` route
  label (a prefix match). Each route that opts into caching now declares
  `->label('cache.contentType', 'image/')` alongside `->label('cache', true)`.
- A public `Cache-Control: no-cache` bypass is a DDoS vector on expensive
  pipelines (Imagick transforms, Chromium screenshots). Restrict the
  bypass to privileged callers — API keys, admin console, and other
  server-side contexts — so only trusted traffic can force-miss the cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
loks0nandClaude Opus 4.7 committed 2026-04-23 11:29:35 +01:00
1 parent eb936865dd
commit 3a5bb98b70
11 files changed
+25 -11

No files matched your search

@@ -33,6 +33,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resource', 'avatar/browser')
->label('sdk', new Method(
namespace: 'avatars',
@@ -36,6 +36,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resourceType', 'cards/cloud-back')
->label('cache.resource', 'card-back/{request.userId}')
->label('docs', false)
@@ -36,6 +36,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resourceType', 'cards/cloud')
->label('cache.resource', 'card/{request.userId}')
->label('docs', false)
@@ -36,6 +36,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resourceType', 'cards/cloud-og')
->label('cache.resource', 'card-og/{request.userId}')
->label('docs', false)
@@ -33,6 +33,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resource', 'avatar/credit-card')
->label('sdk', new Method(
namespace: 'avatars',
@@ -40,6 +40,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resource', 'avatar/favicon')
->label('sdk', new Method(
namespace: 'avatars',
@@ -33,6 +33,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resource', 'avatar/flag')
->label('sdk', new Method(
namespace: 'avatars',
@@ -36,6 +36,7 @@ class Get extends Action
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resource', 'avatar/image')
->label('sdk', new Method(
namespace: 'avatars',
@@ -46,6 +46,7 @@ class Get extends Action
->label('usage.metric', METRIC_AVATARS_SCREENSHOTS_GENERATED)
->label('abuse-limit', 60)
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resourceType', 'avatar/screenshot')
->label('cache.resource', 'screenshot/{request.url}/{request.width}/{request.height}/{request.scale}/{request.theme}/{request.userAgent}/{request.fullpage}/{request.locale}/{request.timezone}/{request.latitude}/{request.longitude}/{request.accuracy}/{request.touch}/{request.permissions}/{request.sleep}/{request.quality}/{request.output}')
->label('sdk', new Method(
@@ -52,6 +52,7 @@ class Get extends Action
->label('scope', 'files.read')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('cache', true)
->label('cache.contentType', 'image/')
->label('cache.resourceType', 'bucket/{request.bucketId}')
->label('cache.resource', 'file/{request.fileId}')
->label('cache.params', ['width', 'height', 'gravity', 'quality', 'borderWidth', 'borderColor', 'borderRadius', 'opacity', 'rotation', 'background', 'output', 'project'])