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.jsisEvalSupported: false, preventing its dynamic code-generation path while rendering PDF operations. -
Restricted Webview Policy
Starts the Content Security Policy withdefault-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
UsesCustomReadonlyEditorProviderso 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:
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>`;
}
- Generates a fresh nonce for the webview instance.
- Denies resources by default, then allows the listed script, style, and image sources.
- 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
.pdfdocuments 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:
- Open VS Code and navigate to Extensions (
++ctrl+shift+x++or++cmd+shift+x++). - Search for
Safe PDF Viewer. - 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
- VS Code Marketplace: Safe PDF Viewer Extension
- GitHub Repository: Safe PDF Viewer Source Code
- Mozilla PDF.js Documentation and Security Architecture
- VS Code Webview API Security Guidelines
- GitHub Security Advisory: PDF.js arbitrary JavaScript execution in affected versions
Related Site Guides
- Lightweight Markdown Preview - Minimal, privacy-first markdown preview extension for VS Code
- VSCode Insiders Revert Guide - Practical field guide for managing VS Code extension environments