fix: prevent caching protected share thumbnails

This commit is contained in:
webadderall
2026-09-21 14:55:48 +10:00
parent 510ed4b57d
commit c8c162ccef
2 changed files with 5 additions and 5 deletions
+2 -3
View File
@@ -783,9 +783,8 @@ async function handleRequest(request, env) {
const thumb = await env.VIDEOS_BUCKET.get(`thumbnails/${thumbMatch[1]}.jpg`);
if (thumb) {
return new Response(thumb.body, {
// private: the unlock cookie gates access — a shared cache must not
// serve a protected poster to other clients.
headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': video.password_hash ? 'private, max-age=3600' : 'public, max-age=86400' },
// Recheck the unlock cookie on every protected poster request.
headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': video.password_hash ? 'private, no-store' : 'public, max-age=86400' },
});
}
return new Response('Not found', { status: 404 });
@@ -467,13 +467,14 @@ describe('review security fixes', () => {
} finally { lookup.mockRestore(); }
});
it('never publicly caches protected video or transcripts', async () => {
it('never caches protected video, transcripts, or thumbnails', async () => {
const password = 'cache-test';
const { shareCode } = await createShare({ password_hash: await sha256Hex(password) });
await completeUpload(shareCode);
await env.VIDEOS_BUCKET.put(`thumbnails/${shareCode}.jpg`, new Uint8Array([0xff, 0xd8, 0xff, 0xd9]));
const unlocked = await SELF.fetch(`${BASE}/s/${shareCode}/verify-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) });
const Cookie = unlocked.headers.get('Set-Cookie').split(';')[0];
for (const [url, extra] of [[`/v/${shareCode}`, {}], [`/v/${shareCode}`, { Range: 'bytes=0-3' }], [`/vtt/${shareCode}`, {}]]) {
for (const [url, extra] of [[`/v/${shareCode}`, {}], [`/v/${shareCode}`, { Range: 'bytes=0-3' }], [`/vtt/${shareCode}`, {}], [`/thumb/${shareCode}`, {}]]) {
const response = await SELF.fetch(`${BASE}${url}`, { headers: { Cookie, ...extra } });
expect([200, 206]).toContain(response.status);
expect(response.headers.get('Cache-Control')).toBe('private, no-store');