diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a095de0..3bc13e6c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,11 @@ ### Fixed +- **PDF previews show in Safari.** On macOS and iOS the preview of an + uploaded PDF stayed an empty box. The page's security policy told the + browser that nothing may frame it, and Safari applied that to the + preview the page itself opens. The policy now allows framing by the app + itself and still refuses every other site. (#1311) - **Periods from Garmin and Oura open their cycles.** Period days imported from a watch or a ring were stored as days only, so the cycle tracker saw no cycle start, no lengths and no forecast. They now open and fill their diff --git a/src/__tests__/baselines/source-file-lines.json b/src/__tests__/baselines/source-file-lines.json index 6fdd36690..649b99be4 100644 --- a/src/__tests__/baselines/source-file-lines.json +++ b/src/__tests__/baselines/source-file-lines.json @@ -1,7 +1,7 @@ { "$comment": "Production files over 800 lines and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.", "count": 84, - "total": 94069, + "total": 94064, "entries": { "app/medications/page-client.tsx": 839, "components/charts/mood-chart.tsx": 1166, @@ -86,6 +86,6 @@ "lib/validations/inbound-documents.ts": 1018, "lib/withings/sync-sleep.ts": 884, "lib/withings/sync.ts": 933, - "proxy.ts": 870 + "proxy.ts": 865 } } diff --git a/src/__tests__/baselines/source-function-lines.json b/src/__tests__/baselines/source-function-lines.json index b4776feb5..453a901c5 100644 --- a/src/__tests__/baselines/source-function-lines.json +++ b/src/__tests__/baselines/source-function-lines.json @@ -1,7 +1,7 @@ { "$comment": "Production functions over 200 lines, keyed \"file :: name\", and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.", "count": 345, - "total": 111909, + "total": 111902, "entries": { "app/achievements/page.tsx :: AchievementsPage": 206, "app/api/admin/backups/upload/route.ts :: apiHandler(arg)": 275, @@ -180,7 +180,7 @@ "components/mood/mood-tag-picker.tsx :: MoodTagPicker": 257, "components/onboarding/confirm-screen.tsx :: ConfirmScreen": 255, "components/onboarding/done-screen.tsx :: DoneScreen": 408, - "components/onboarding/getting-started-checklist.tsx :: GettingStartedChecklist": 425, + "components/onboarding/getting-started-checklist.tsx :: GettingStartedChecklist": 423, "components/onboarding/tour.tsx :: OnboardingTour": 544, "components/practitioners/practitioner-list.tsx :: PractitionerList": 230, "components/provenance/source-provenance-view.tsx :: SourceProvenanceView": 363, @@ -347,6 +347,6 @@ "lib/withings/sync-ecg.ts :: syncUserEcg": 202, "lib/withings/sync-sleep.ts :: syncUserSleep": 414, "lib/withings/sync.ts :: syncUserMeasurements": 337, - "proxy.ts :: proxy": 498 + "proxy.ts :: proxy": 493 } } diff --git a/src/__tests__/proxy-csp-blob-preview.test.ts b/src/__tests__/proxy-csp-blob-preview.test.ts index 06811925f..c312ced2c 100644 --- a/src/__tests__/proxy-csp-blob-preview.test.ts +++ b/src/__tests__/proxy-csp-blob-preview.test.ts @@ -11,7 +11,7 @@ import { NextRequest } from "next/server"; * - `frame-src` exists and admits precisely 'self' and blob: — nothing else. * - `img-src` admits blob: alongside 'self' and data:. * - The boundaries that make blob: safe stay untouched: `object-src 'none'`, - * `frame-ancestors 'none'`, `default-src 'self'`, nonce-bound script-src. + * `frame-ancestors 'self'`, `default-src 'self'`, nonce-bound script-src. * - The vault SERVE route keeps its own stricter document CSP unchanged. * * Security rationale (mirrors the comment in proxy.ts): a `blob:` URL can only @@ -73,7 +73,10 @@ describe("proxy.ts blob preview CSP (#787)", () => { const res = proxy(makeRequest("/documents")); const csp = res.headers.get("content-security-policy") ?? ""; expect(directive(csp, "object-src")).toEqual(["'none'"]); - expect(directive(csp, "frame-ancestors")).toEqual(["'none'"]); + // 'self', never 'none': the blob: preview inherits this policy, and + // WebKit applies the inherited frame-ancestors to the preview's own + // parent, so 'none' blanks every PDF preview in Safari. + expect(directive(csp, "frame-ancestors")).toEqual(["'self'"]); expect(directive(csp, "default-src")).toEqual(["'self'"]); expect(csp).toMatch(/script-src 'self' 'nonce-/); }); diff --git a/src/__tests__/proxy-document-serve-framing.test.ts b/src/__tests__/proxy-document-serve-framing.test.ts index a0353575c..cb1b7f263 100644 --- a/src/__tests__/proxy-document-serve-framing.test.ts +++ b/src/__tests__/proxy-document-serve-framing.test.ts @@ -4,8 +4,7 @@ import { NextRequest } from "next/server"; /** * The document vault's decrypt-and-serve route is framed same-origin by the * detail preview (`