# pasteToPrint handoff security and release gate

Version: 2  
Release stage: 6  
Last updated: 2026-08-16

This document defines the security boundary and deployment gate for every
pasteToPrint handoff: inline text, inline PTDF, remote JSON, Agent Import,
external `postMessage`, local `.ptdf` files, PWA file handling and the optional
AI-chat browser extension.

## Non-negotiable guarantees

1. A managed, editable PTDF session may populate only a genuinely empty editor.
   It must show a visible notice with guarded one-click **Undo**. Every other
   handoff opens a review dialog and does not modify the document until the user
   chooses **Append** or **Replace**. No handoff automatically overwrites an
   existing document.
2. Import code does not call print, PDF export, download or e-mail actions.
3. Supported content-bearing fragments (`#text`, `#ptdf`, `#agentImport`) and
   legacy import query parameters are removed with `history.replaceState`
   before analytics can start. Non-import parameters such as `lang` remain.
4. Analytics is disabled for all import requests, including the legacy query
   routes. The raw fragment is retained only in a closure and can be consumed
   once by the receiver.
5. Raw content and caught exception objects are not written to the console,
   analytics or error-reporting payloads.
6. Text is escaped. HTML and HTML previews pass through the application
   sanitizer. CSS is accepted only through the HTML/CSS sanitizer.
7. PTDF must use `schemaVersion: "ptp.document/1"` and pass the same
   normalization, size and structural limits as other PTDF entry points.
8. A malformed or oversized payload fails closed and shows a generic localized
   error. It is never partially inserted.
9. The service worker does not cache capability/session API routes or legacy
   content-bearing query imports. A handoff release always changes the receiver
   asset version and runtime cache name.
10. The browser extension has no background worker, cookie access or clipboard
    permission and transfers a selected response only after a user click.

## Threat model

The sender and the supplied document are untrusted. They may contain malformed
UTF-8, duplicate selectors, oversized values, executable HTML, event
attributes, external resources, unsafe URLs, misleading titles or a requested
replace mode. The sender may also attempt to trigger a synthetic flow without
human review. The empty-editor exception does not trust the sender: it requires
the managed API v2 session, editable permissions and the validated PTDF
boundary, and it is non-overwriting and undoable.

The pasteToPrint application origin, its shipped JavaScript, the browser and a
deliberately installed extension are trusted. A compromised application origin
is outside this boundary.

## Required deployment order

Deploy these files as one release:

1. `assets/js/import-fragment-guard.js`
2. `assets/js/external-import.js`
3. `index.html` with the matching cache-busting query
4. `service-worker.js` with the new runtime cache name
5. discovery files and this document

The guard must appear before the analytics bootstrap in `index.html`. The
receiver must appear after the editor core and PTDF renderer.

## Automated release gate

From the repository root on Windows:

```powershell
powershell -ExecutionPolicy Bypass -File scripts/test-handoff-release.ps1
```

The gate verifies the privacy guard, analytics ordering, single-use fragment
consumption, generic diagnostics, sanitization and PTDF boundaries, the narrow
managed-empty-editor automatic path, guarded undo, confirmation for all other
imports, absence of automatic print/export actions, service-worker cache
rotation, localized entry pages, discovery consistency and all existing
handoff regression suites.

Any failed assertion blocks deployment. Do not bypass the gate by changing an
assertion unless the public contract is intentionally versioned and this
document is updated in the same release.

## Manual smoke test

1. Create a managed editable PTDF session through API v2 or MCP and open its
   editor link with an empty editor.
2. Confirm the content appears without a modal, the AI import notice is visible
   and **Undo** restores the empty editor.
3. Restore or type existing content, open the same managed link and confirm the
   review dialog appears without changing the existing document.
4. Open `/#text=Stage%206%20smoke%20test`.
5. Confirm the address bar no longer contains a fragment.
6. Confirm a localized review dialog appears and the editor is unchanged.
7. Choose Cancel and confirm the editor remains unchanged.
8. Repeat and choose Replace; confirm only then the text reaches the editor.
9. Repeat with a valid `#ptdf=` vector and with a `.ptdf` file.
10. In browser developer tools, confirm no analytics request is made on import
    routes and no imported content appears in console output.
11. Confirm normal non-import page views still initialize analytics.

## Rollback rule

Roll back `index.html`, the receiver, guard and service worker together. Never
roll back only the guard or only the receiver because doing so can either lose
the captured fragment or expose it to scripts that run later.
