Drop cached dinner responses and keep link tokens out of names (PR #97 review F1, F2)
F1: bump the service worker cache version to v2 so activate deletes the v1 API cache, whose dinner responses still carried the pasted recipe_url and could be served offline. F2: nameFromURL now drops words holding a digit and ends the name at the first credential word (token, access, secret, ...), so a token in the link's last path segment never reaches the public dinner name. /recipes/beef-stew is still "Beef stew". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -28,7 +28,7 @@ declare const self: ServiceWorkerGlobalScope & {
|
||||
__WB_MANIFEST: Array<{ url: string; revision: string | null }>
|
||||
}
|
||||
|
||||
// The precache becomes mealquest-precache-v1.
|
||||
// The precache becomes mealquest-precache-v2.
|
||||
setCacheNameDetails({ prefix: 'mealquest', precache: 'precache', suffix: cacheVersion })
|
||||
|
||||
const day = 24 * 60 * 60
|
||||
|
||||
@@ -146,11 +146,20 @@ describe('cache names', () => {
|
||||
it('all start with mealquest-', () => {
|
||||
for (const name of Object.values(cacheNames)) expect(name).toMatch(/^mealquest-/)
|
||||
})
|
||||
|
||||
it('are v2, so activate deletes the v1 caches', () => {
|
||||
// v1 API responses held dinners with the pasted recipe_url, which can
|
||||
// carry a personal token; offline, the worker would still serve them.
|
||||
expect(cacheNames.api).toBe('mealquest-api-v2')
|
||||
const current = ['mealquest-precache-v2', ...Object.values(cacheNames)]
|
||||
const v1 = ['mealquest-precache-v1', 'mealquest-api-v1', 'mealquest-media-v1', 'mealquest-fonts-v1']
|
||||
expect(staleCaches([...v1, ...current], current)).toEqual(v1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('staleCaches', () => {
|
||||
it('deletes only MealQuest caches that are not current', () => {
|
||||
const current = ['mealquest-precache-v1', ...Object.values(cacheNames)]
|
||||
const current = ['mealquest-precache-v2', ...Object.values(cacheNames)]
|
||||
const names = [
|
||||
...current,
|
||||
'mealquest-precache',
|
||||
|
||||
@@ -25,7 +25,9 @@ export interface RouteRequest {
|
||||
}
|
||||
|
||||
// Bumping the version renames every cache; activate then deletes the old ones.
|
||||
export const cacheVersion = 'v1'
|
||||
// v2 drops v1 API responses, whose dinners still carried the pasted
|
||||
// recipe_url.
|
||||
export const cacheVersion = 'v2'
|
||||
export const cachePrefix = 'mealquest-'
|
||||
|
||||
export const cacheNames = {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// The service worker itself needs worker globals, so these tests read its
|
||||
// source: they pin the parts routes.ts cannot express — the API cache's
|
||||
// strategy, the font strategy's expiration, the font route's place ahead of
|
||||
// the precache route and network-only requests left to the browser.
|
||||
// the precache route, network-only requests left to the browser and stale
|
||||
// caches deleted on activate.
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import source from '../sw.ts?raw'
|
||||
|
||||
@@ -37,6 +38,12 @@ describe('service worker source', () => {
|
||||
expect(source).not.toContain('registerRoute(() => true')
|
||||
})
|
||||
|
||||
it('deletes every stale cache on activate', () => {
|
||||
expect(source).toMatch(
|
||||
/self\.addEventListener\('activate', \(event\) => \{\s*const current = \[workboxCacheNames\.precache, \.\.\.Object\.values\(cacheNames\)\][\s\S]*?staleCaches\(names, current\)\.map\(\(name\) => caches\.delete\(name\)\)/,
|
||||
)
|
||||
})
|
||||
|
||||
it('activates a waiting worker when the update prompt sends SKIP_WAITING', () => {
|
||||
expect(source).toMatch(
|
||||
/self\.addEventListener\('message', \(event\) => \{\s*if \(event\.data\?\.type === 'SKIP_WAITING'\) void self\.skipWaiting\(\)/,
|
||||
|
||||
Reference in New Issue
Block a user