Skip to content

Safe PDF Viewer for VS Code: A Local, Read-Only PDF Extension

A PDF viewer parses a complex, potentially untrusted file. Its safety depends on the parser version, the browser or webview boundary, the resources it can load, and the capabilities the extension enables.

I developed Safe PDF Viewer to make those choices explicit inside VS Code. It bundles PDF.js locally, restricts its webview with a Content Security Policy, disables dynamic JavaScript evaluation in PDF.js, and exposes the file through a read-only editor. This post explains what each control reduces and what it does not guarantee.

Navigate this post

Security Risks in Editor PDF Viewers

The PDF format supports active features such as links, forms, and embedded actions, and PDF parsers have had security vulnerabilities. Opening an untrusted file therefore gives a large parser attacker-controlled input. The outcome depends on the viewer: a modern sandboxed viewer is different from an old or permissively configured parser.

A VS Code PDF extension also chooses where its rendering library comes from and which workspace resources its webview may load. Bundling PDF.js removes a runtime dependency on a content delivery network (CDN). Restricting localResourceRoots and applying a Content Security Policy reduce what a compromised rendering context can reach.

Treat the parser as part of the attack surface

A viewer reduces risk; it does not make every PDF safe. Keep PDF.js, VS Code, and the extension updated, and open highly sensitive untrusted files inside an isolation boundary suited to your threat model.

To understand core security terms used throughout this guide:

PDF.js

Mozilla's JavaScript library that parses and renders PDF documents into HTML5 Canvas elements inside web browsers.

Webview

A mini, isolated browser window embedded inside VS Code that allows extensions to render custom HTML and user interfaces.

Content Security Policy (CSP)

An HTTP header or HTML meta tag that specifies which scripts, styles, images, and network destinations a webview may load.

Nonce

A random value generated for one page load. A Content Security Policy can require the matching nonce before an allowed script runs.

Offline Operation

Rendering without fetching application scripts or document content from a remote service. This does not make the entire computer air-gapped.

How Safe PDF Viewer Shields Your Editor

Safe PDF Viewer implements a defense-in-depth model that explicitly disables high-risk capabilities while retaining essential reading features. Every document opens inside a sandbox configured with strict security boundaries.

  • Dynamic Evaluation Disabled
    Sets PDF.js isEvalSupported: false, preventing its dynamic code-generation path while rendering PDF operations.

  • Restricted Webview Policy
    Starts the Content Security Policy with default-src 'none' and permits only the local resources the viewer needs.

  • Local Renderer Bundle
    Bundles PDF.js with the extension instead of downloading the renderer from a CDN at runtime.

  • Read-Only Editor
    Uses CustomReadonlyEditorProvider so the custom editor does not expose a save path for the PDF.

To verify how Safe PDF Viewer protects your environment compared to typical PDF extension implementations, the following table details key security constraints:

Design question Safe PDF Viewer choice What to verify in another viewer
PDF.js dynamic evaluation isEvalSupported: false PDF.js version and evaluation setting
Webview content policy Starts with default-src 'none' Allowed script, image, style, and connection sources
Renderer delivery Bundled with the extension Bundled file, CDN, or another remote source
Local resource scope Explicitly limited localResourceRoots and URI handling
Editor write path Read-only provider Whether the extension can modify the document

These controls reduce independent risks: dynamic code generation, unexpected resource loading, broad local-file access, and accidental writes. They do not prove that PDF.js or the surrounding extension has no parser bugs.

Security Architecture and Core Capabilities

The extension relies on VS Code's CustomReadonlyEditorProvider API. This architecture ensures that the extension host never opens a write handle to your documents, preventing accidental file modifications or corruption.

---
title: "Safe PDF Viewer Security Sandboxing Flow"
---
flowchart TB
    accTitle: Safe PDF Viewer sandboxing flow
    accDescr: An untrusted PDF is passed to a restricted webview and a bundled PDF.js engine with dynamic evaluation disabled, then rendered to a canvas.
    A["Untrusted PDF File"] --> B["VS Code Extension Host"]
    B -->|"Scopes Read Path"| C["Isolated Webview Container"]
    C -->|"Validates Nonce Token"| D["Local Bundled PDF.js Engine"]
    D -->|"Disables dynamic eval"| E["HTML5 Render Canvas"]

The extension host creates the webview HTML and a fresh nonce. The Content Security Policy allows the matching bundled script, while other resource types remain limited to the listed sources. PDF.js receives isEvalSupported: false and renders the document pages to a canvas.

The following TypeScript code snippet demonstrates how the extension configures the strict Content Security Policy meta header and Webview options:

src/pdfEditorProvider.ts
private getHtmlForWebview(webview: vscode.Webview, documentUri: vscode.Uri): string {
    const nonce = getNonce(); // (1)
    const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this.context.extensionUri, 'media', 'pdf.js'));

    return `<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta http-equiv="Content-Security-Policy"
              content="default-src 'none'; script-src 'nonce-${nonce}'; style-src ${webview.cspSource}; img-src ${webview.cspSource} data:;"> // (2)
        <script nonce="${nonce}" src="${scriptUri}"></script> // (3)
    </head>
    <body>
        <div id="viewerContainer"><canvas id="pdfCanvas"></canvas></div>
    </body>
    </html>`;
}
  1. Generates a fresh nonce for the webview instance.
  2. Denies resources by default, then allows the listed script, style, and image sources.
  3. Attaches the nonce to the bundled PDF.js script so it satisfies the policy.
Verifying the renderer's network behavior

Open VS Code Developer Tools (++ctrl+shift+i++ or ++cmd+option+i++), select the Network tab, and open a PDF. Filter the requests to the viewer webview and confirm that it does not fetch the renderer or document content from a remote origin. Other VS Code features may still make unrelated requests.

Included Features vs Intentionally Excluded Features

To maintain a minimal security profile, features were evaluated against their risk potential:

Core Included Features:

  • Zero-config viewing: Instantly open .pdf documents directly inside VS Code.
  • Keyboard navigation: Move across pages using arrow keys, Page Up/Down, or page numbers.
  • In-document text search: Execute text search with ++ctrl+f++ or ++cmd+f++ and view match highlights.
  • Flexible zoom controls: Fit pages to width, step zoom in/out, or set custom zoom levels.
  • Password support: Inline secure decryption prompts for password-protected PDF files.
  • Theme integration: Matches VS Code light, dark, and high-contrast editor themes.

Intentionally Excluded Features:

  • No PDF scripting features: The viewer does not expose interactive PDF scripting, and it disables PDF.js dynamic evaluation during rendering.
  • No form filling: Eliminates payload delivery vectors present in interactive form fields.
  • No embedded multimedia playback: Excludes audio and video elements to enforce sandboxing.
  • No file editing or annotations: Strictly read-only to preserve document integrity.

If your workflow requires filling PDF forms or applying annotations, we recommend using an audited, standalone PDF editor designed for interactive document editing.

Installation and Code Exploration

Safe PDF Viewer is available on the VS Code Marketplace and builds directly from source under the open-source MIT License.

To install the extension:

  1. Open VS Code and navigate to Extensions (++ctrl+shift+x++ or ++cmd+shift+x++).
  2. Search for Safe PDF Viewer.
  3. Click Install.

Alternatively, inspect or contribute to the extension source code directly on the Safe PDF Viewer GitHub Repository.

Conclusion

Safe PDF Viewer narrows its trust boundary by bundling the renderer, denying webview resources by default, disabling dynamic evaluation, and using a read-only custom editor. Each measure is inspectable in the source and addresses a different class of failure.

No viewer can promise safety for every malicious file. Keep the parser updated, verify the extension's current configuration, and use stronger operating-system or virtual-machine isolation when the document risk justifies it.

References and further reading

Open the complete reference catalog

Primary Sources