From 5365a486c3e9291960b59377b42638cd7ac352b9 Mon Sep 17 00:00:00 2001 From: Kaylee Lubick Date: Tue, 15 Sep 2026 08:15:45 -0400 Subject: [PATCH] Tighten CSP I don't see any pdf.js code, so I presume we don't need this. Signed-off-by: Kaylee Lubick --- desktop/src/electron/csp.ts | 10 ++++------ desktop/tests/modular/csp.test.ts | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 desktop/tests/modular/csp.test.ts diff --git a/desktop/src/electron/csp.ts b/desktop/src/electron/csp.ts index 06414b71..f7070d36 100644 --- a/desktop/src/electron/csp.ts +++ b/desktop/src/electron/csp.ts @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 export function buildCspHeader(): string { - const connectSrc = ["'self'", 'http://127.0.0.1:*', 'http://localhost:*'] + // https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html + // https://www.w3.org/TR/CSP/ return ( [ "default-src 'none'", "script-src 'self'", "style-src 'self' 'unsafe-inline'", - `connect-src ${connectSrc.join(' ')}`, + "connect-src 'self'", // `blob:` is needed so the SVG rasterizer can assign a blob URL // to `` for decode (see `extract-svg.ts`). `data:` covers // the inline previews we generate from base64 attachments. @@ -18,10 +19,7 @@ export function buildCspHeader(): string { // fallbacks only, so no remote font origin is needed. "font-src 'self'", "manifest-src 'self'", - // `'self'` covers the Vite-served pdf.js worker module; `blob:` - // is the pdf.js fallback when the worker module can't be loaded - // directly (some Chromium builds spin up a blob-URL shim). - "worker-src 'self' blob:", + "worker-src 'none'", "object-src 'none'", "frame-src 'none'", "base-uri 'self'", diff --git a/desktop/tests/modular/csp.test.ts b/desktop/tests/modular/csp.test.ts new file mode 100644 index 00000000..a5a27e65 --- /dev/null +++ b/desktop/tests/modular/csp.test.ts @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it } from 'vitest' +import { buildCspHeader } from '@/electron/csp' + +describe('content security policy', () => { + it('keeps security-critical directives restrictive', () => { + const directives = buildCspHeader() + .split(';') + .map(directive => directive.trim()) + + expect(directives).toContain("default-src 'none'") + expect(directives).toContain("script-src 'self'") + expect(directives).toContain("connect-src 'self'") + expect(directives).toContain("worker-src 'none'") + expect(directives).toContain("base-uri 'self'") + expect(directives).toContain("object-src 'none'") + }) +})