/app/lynxseal_pdfjs/Embedded pdf.js viewer, served from its own origin (pdfjs.lynxseal.com) so any parser/XSS bug in pdf.js is sandboxed away from portal and wrapper origins (no shared cookies, no shared localStorage, no shared scripts). Parents communicate with the viewer via postMessage.
pdfjs-5.7.284-dist.zip — upstream Mozilla pdf.js release ZIP,
committed verbatim, pinned by sha256 in sw.js.
Reproduce:
curl -L -o pdfjs-5.7.284-dist.zip \
https://github.com/mozilla/pdf.js/releases/download/v5.7.284/pdfjs-5.7.284-dist.zip
sha256sum pdfjs-5.7.284-dist.zip
# 6d1b81252d76358df5831567d7d551f40ebae0cd8e0a554694bc4df0d3db8715
sw.js — service worker, scope /. On first fetch:
fflate@0.8.3 from jsdelivr, sha256-verifies against
FFLATE_SHA256_B64, evaluates via Function() (SW contexts lack
URL.createObjectURL, so the usual blob+importScripts SRI trick
isn’t available; the manual hash-check is equivalent in effect).web/viewer.mjs
(clear defaultUrl so it doesn’t auto-open a missing demo PDF;
enable disablePreferences to avoid IndexedDB-backed prefs +
console warnings) and to web/viewer.html (inject our bridge
<script> at <head> start).404.html — GH Pages fallback, served once per browser on first
visit. Registers /sw.js, waits for activation, reloads. On all later
visits the SW takes over before any HTML loads.
CNAME — pdfjs.lynxseal.com.Parent embeds:
<iframe src="https://pdfjs.lynxseal.com/web/viewer.html"></iframe>
Viewer → parent (once initialized):
{ type: 'pdfjs:ready' }
Parent → viewer (after ready):
{
type: 'pdfjs:open',
id: <correlation id>,
data: <ArrayBuffer>, // PDF bytes (transferred, not copied)
hideToolbarIds: ['print','download','openFile','viewBookmark'] // optional
}
Viewer → parent (per request id):
{ type: 'pdfjs:opened', id: <id> }
{ type: 'pdfjs:error', id: <id>, message: <string> }
The viewer accepts open requests from any parent that embeds it. There’s
nothing sensitive on this origin to gatekeep — anyone who can embed the
viewer can also just render their own PDFs in their own page. The bridge
posts replies back to event.origin, and parents validate that messages
they receive came from pdfjs.lynxseal.com.
Two files = the entire pdf.js audit surface:
sw.js — read it, it does only what’s described here.If either is tampered, the SW refuses to extract and the viewer never loads.