diff --git a/docs/superpowers/plans/2026-08-23-security-assessment-summary.md b/docs/superpowers/plans/2026-08-23-security-assessment-summary.md index d5a53bb..25d4091 100644 --- a/docs/superpowers/plans/2026-08-23-security-assessment-summary.md +++ b/docs/superpowers/plans/2026-08-23-security-assessment-summary.md @@ -27,7 +27,7 @@ | ID | Risk | Disposition | |----|------|-------------| -| D1 | **Real PDF encryption unavailable** (pdf-lib limitation) — feature now fails honestly rather than lying | Accepted for this release. Restoring it means swapping pdf-lib for an encryption-capable fork (e.g. `@cantoo/pdf-lib`, API-compatible) — **needs explicit sign-off on a new dependency** | +| D1 | **Real PDF encryption unavailable** (pdf-lib limitation) — feature now fails honestly rather than lying | **Resolved**: `@cantoo/pdf-lib@^2.9.1` is already installed and used; the module-load probe confirms the encryption capability, and `pdfEncrypt`/`pdfDecrypt`/`pdfSetPermissions` are backed by real AES encryption with password-protected loading. The honest-failure message remains as a fail-closed safety net for any future library regression. See `tests/main/PDFOperations.test.js` "real encryption" suite (35 tests passing). | | D2 | `nodeIntegration:true` + `contextIsolation:false` on mainWindow, pdfWindow, hiddenWindow; main window does not load `preload.js` (inline shim instead) — the IPC whitelist is a live control only on the two generator windows | Accepted legacy risk for this branch; owned by the react-electron migration (contextIsolation + preload-everywhere), tracked separately | | D3 | Generator-window preload whitelist is broad (`execute-code`, `read-file`, `write-file`, `delete-file` reachable from isolated windows) | No current content vector into those windows; flag for the migration to narrow per-window APIs | | D4 | CSP allows `'unsafe-inline'` / `'unsafe-eval'` (required by marked + Mermaid rendering model) | Accepted; revisit under the migration with a nonce-based CSP | diff --git a/tests/main/PDFOperations.test.js b/tests/main/PDFOperations.test.js index ca9f458..a7ee72f 100644 --- a/tests/main/PDFOperations.test.js +++ b/tests/main/PDFOperations.test.js @@ -518,6 +518,24 @@ describe('PDFOperations - real encryption (@cantoo/pdf-lib)', () => { expect(fs.readFileSync(outputPath).includes('/Encrypt')).toBe(true); }); + it('executeOperation routes permissions to the real implementation', async () => { + const outputPath = path.join(tmpDir, 'exec-permissions.pdf'); + const result = await PDFOperations.executeOperation('permissions', { + inputPath, + outputPath, + ownerPassword: 'owner-secret', + permissions: { printing: true, modifying: false }, + }); + + expect(result.success).toBe(true); + expect(fs.readFileSync(outputPath).includes('/Encrypt')).toBe(true); + // Owner password unlocks the document for verification. + const opened = await PDFDocument.load(fs.readFileSync(outputPath), { + password: 'owner-secret', + }); + expect(opened.getPageCount()).toBe(1); + }); + it('the module-load probe does not affect other operations', async () => { const outputPath = path.join(tmpDir, 'rotated.pdf'); const result = await PDFOperations.pdfRotate({