fix(02-12.1): reach fenced document routes through the app transport

A browser-issued <img src>, <iframe src> or <a href download> cannot
carry the record-session assertion, so the fence refused all three on
any session that had been inside a shared record — permanently, since
none of them will ever send a header.

- add a structural guard: no client file may name a record-fenced route
  in a src=, href= or window.location sink
- fetch the thumbnail, the inline preview and the download through the
  app transport and hand the DOM an object URL, revoked on unmount
- make the two API probes in the managed-profiles browser spec assert
  the same context the app beside them asserts
This commit is contained in:
Marc-André Bombeck committed 2026-08-06 20:36:45 +02:00
1 parent 8fcbdcc0df
commit ffe38c9bb6
7 files changed
+739 -27

No files matched your search

@@ -118,7 +118,20 @@ describe("<DocumentCard>", () => {
expect(html).toContain("befund.docx");
});
it("renders the preview thumbnail image when hasThumbnail is set", () => {
it("never emits a browser-issued /api subresource for the thumbnail", () => {
// v1.37.0 — this used to assert `src="/api/documents/inbound/…/thumbnail"`
// in the markup, and that contract is deliberately gone. A `<img src>` is
// issued by the BROWSER, so it cannot carry the record-session assertion,
// and the fence refuses an unasserted request on any session that has been
// inside a shared record — a thumbnail that silently stops rendering for
// good. The bytes now come through the app transport as an object URL
// (`useFencedObjectUrl`), which means nothing paints on a server render.
//
// The assertion is inverted rather than deleted: the markup must contain no
// `/api/` reference at all. `src/__tests__/record-fence-headerless-transport-guard.test.ts`
// holds the same property across the whole client tree, and
// `src/hooks/__tests__/use-fenced-object-url.test.ts` proves the request
// really does carry the headers.
const html = render(
<DocumentCard
document={doc({ hasThumbnail: true })}
@@ -128,9 +141,11 @@ describe("<DocumentCard>", () => {
highlighted={false}
/>,
);
expect(html).toContain('data-slot="document-thumbnail"');
expect(html).toContain('src="/api/documents/inbound/doc-1/thumbnail"');
expect(html).toContain('loading="lazy"');
expect(html).not.toContain("/api/documents");
// Until the blob lands the card shows its kind icon, exactly as a card
// with no thumbnail does — never a broken image.
expect(html).not.toContain('data-slot="document-thumbnail"');
expect(html).toContain("lucide-scan-line");
});
it("shows the kind icon (no thumbnail image) when hasThumbnail is false", () => {