# How Draw.io Desktop Configures Content Security Policy (CSP) to Prevent External Script Execution

> Learn how Draw.io Desktop configures Content Security Policy to block external scripts. Discover its robust security measures for preventing unauthorized code execution.

- Repository: [draw.io/drawio-desktop](https://github.com/jgraph/drawio-desktop)
- Tags: internals
- Published: 2026-03-05

---

**Draw.io Desktop enforces a strict Content Security Policy by intercepting all HTTP responses in the Electron main process to inject CSP headers that whitelist only local scripts with verified SHA-256 hashes, while simultaneously blocking file protocol requests outside the application directory.**

The drawio-desktop application runs the diagram editor inside an Electron `BrowserWindow`, which requires robust isolation from potentially malicious external code. By implementing a custom **Content Security Policy (CSP) configuration** through Electron's `webRequest` API, the application ensures that only bundled, hash-verified scripts can execute, effectively eliminating injection vectors for third-party JavaScript.

## Injecting Strict CSP Headers via Electron's Main Process

In [`src/main/electron.js`](https://github.com/jgraph/drawio-desktop/blob/main/src/main/electron.js), the main process registers a header-modifying hook using `session.defaultSession.webRequest.onHeadersReceived` before any renderer content loads. This intercepts every network response and appends a strict **Content Security Policy** header that restricts resource loading to the application's own files.

The CSP string constructed at lines 73-84 combines multiple directives to create a locked-down execution environment. The `default-src 'self'` directive establishes a baseline that prevents loading any resource type from remote origins unless explicitly overridden by subsequent directives.

### Script Source Validation with SHA-256 Hashes

The most critical directive for preventing external script execution is `script-src 'self'` combined with three specific SHA-256 hashes. As implemented in [`src/main/electron.js`](https://github.com/jgraph/drawio-desktop/blob/main/src/main/electron.js), the policy only permits scripts that either originate from the app's local file system or match one of these predetermined hashes:

- `'sha256-f6cHSTUnCvbQqwa6rKcbWIpgN9dLl0ROfpEKTQUQPr8='`
- `'sha256-6g514VrT/cZFZltSaKxIVNFF46+MFaTSDTPB8WfYK+c='`
- `'sha256-ZQ86kVKhLmcnklYAnUksoyZaLkv7vvOG9cc/hBJAEuQ='`

Because the browser calculates the hash of every script block before execution, any modification to the bundled JavaScript—even a single character change—would result in a hash mismatch, causing the browser to refuse execution immediately.

```js
// src/main/electron.js (lines 73-84)
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
  callback({
    responseHeaders: {
      ...details.responseHeaders,
      'Content-Security-Policy': [
        "default-src 'self'; " +
        "script-src 'self' " +
        "'sha256-f6cHSTUnCvbQqwa6rKcbWIpgN9dLl0ROfpEKTQUQPr8=' " +
        "'sha256-6g514VrT/cZFZltSaKxIVNFF46+MFaTSDTPB8WfYK+c=' " +
        "'sha256-ZQ86kVKhLmcnklYAnUksoyZaLkv7vvOG9cc/hBJAEuQ='; " +
        "connect-src 'self'" + (isGoogleFontsEnabled ?
          " https://fonts.googleapis.com https://fonts.gstatic.com" : "") +
        "; img-src * data:; media-src *; font-src * data:; " +
        "frame-src 'none'; style-src 'self' 'unsafe-inline'" +
        (isGoogleFontsEnabled ? " https://fonts.googleapis.com" : "") +
        "; base-uri 'none'; child-src 'self'; object-src 'none';"
      ]
    }
  });
});

```

### Restricting Network Connections and External Resources

Beyond script execution, the **CSP configuration** limits external communication through the `connect-src 'self'` directive, which restricts XMLHttpRequest, fetch, and WebSocket connections to the application itself. The policy optionally appends Google Fonts URLs when font integration is enabled, but maintains strict prohibitions on arbitrary external data exfiltration or script loading.

Additional directives harden the surface area further: `frame-src 'none'` prevents embedding external frames, `object-src 'none'` blocks plugin execution, and `base-uri 'none'` restricts base element manipulation.

## File Protocol Request Filtering

Complementing the CSP headers, drawio-desktop implements a second security layer through `session.defaultSession.webRequest.onBeforeRequest` at lines 90-103 of [`src/main/electron.js`](https://github.com/jgraph/drawio-desktop/blob/main/src/main/electron.js). This hook intercepts all `file://` protocol requests and validates them against the application's installation directory and an optional plugins folder.

Any attempt to load JavaScript, HTML, or other resources from arbitrary user-supplied paths outside these whitelisted directories is cancelled before reaching the renderer. This prevents attackers from exploiting local file inclusion vulnerabilities to execute scripts stored elsewhere on the filesystem.

```js
// src/main/electron.js (lines 90-103)
session.defaultSession.webRequest.onBeforeRequest(
  { urls: ['file://*'] },
  (details, callback) => {
    const url = details.url.replace(/\/.\:\//, s => s.toUpperCase());

    // Only allow files under the app's code folder or the plugins folder
    if (!url.startsWith(codeUrl) &&
        (!isPluginsEnabled() || (isPluginsEnabled() && !url.startsWith(pluginsCodeUrl)))) {
      console.log('Blocked loading file from', details.url);
      callback({ cancel: true });
    } else {
      callback({});
    }
  });

```

## Defense in Depth Architecture

Together, these mechanisms create a comprehensive defense-in-depth strategy that prevents **external script execution**:

1. **Header Injection** enforces strict source whitelisting for all resource types including scripts, styles, and images through the `onHeadersReceived` hook.
2. **Cryptographic Verification** via SHA-256 hashes guarantees that bundled script content has not been tampered with between releases, as any modification invalidates the hash.
3. **Filesystem Sandboxing** ensures only files within the application bundle (`codeUrl`) or approved plugin directories (`pluginsCodeUrl`) can be loaded via the file protocol.

## Summary

- The **CSP configuration** in drawio-desktop is enforced through Electron's `webRequest.onHeadersReceived` API in [`src/main/electron.js`](https://github.com/jgraph/drawio-desktop/blob/main/src/main/electron.js) at lines 73-84.
- **Script execution** is restricted to local files matching three specific SHA-256 hashes, preventing injection of arbitrary external code via `script-src` directives.
- **Network connections** are limited to `connect-src 'self'`, blocking unauthorized data exfiltration and remote script loading.
- **File protocol filtering** via `webRequest.onBeforeRequest` (lines 90-103) blocks attempts to load resources from outside the application directory or plugin folders.
- This multi-layered approach ensures that only cryptographically verified, bundled scripts can execute within the drawio-desktop application.

## Frequently Asked Questions

### What specific CSP directives prevent external scripts from running in Draw.io Desktop?

The `script-src 'self'` directive combined with three SHA-256 hash whitelists ensures only local scripts with matching cryptographic hashes can execute, while `default-src 'self'` blocks all remote resource loading by default unless explicitly allowed.

### Where in the source code is the CSP header actually injected?

The header injection occurs in [`src/main/electron.js`](https://github.com/jgraph/drawio-desktop/blob/main/src/main/electron.js) at lines 73-84, where `session.defaultSession.webRequest.onHeadersReceived` intercepts all responses and appends the Content-Security-Policy header before the renderer processes them.

### How does Draw.io Desktop prevent loading JavaScript files from arbitrary user directories?

Through the `onBeforeRequest` hook at lines 90-103 in [`src/main/electron.js`](https://github.com/jgraph/drawio-desktop/blob/main/src/main/electron.js), the application cancels any `file://` URL that does not reside within the application's code folder or the authorized plugins directory, preventing local file inclusion attacks.

### Why are SHA-256 hashes used in the script-src directive instead of nonce-based CSP?

SHA-256 hashes provide deterministic verification of specific bundled script blocks without requiring runtime nonce generation, ensuring that any modification to the application's JavaScript—whether malicious or accidental—immediately invalidates the script's authorization to run.