Enforce Content-Security-Policy (CSP) in Obsidian, Desktop, Web apps

Prevent XSS attacks by enforcing Content-Security-Policy (CSP) in apps.
Do not allow loading images, other assets from untrusted domains.

- Only allow loading assets from trusted domains
  like 'self', khoj.dev, ipapi for geolocation, google (fonts, img)
  - images from khoj domain, google (for profile pic)
  - assets from khoj domain
  - Do not allow iframe src
  - Allow unsafe-inline script and styles for now as markdown-it escapes html
    in user, khoj chat

- Add hostURL to CSP of the Desktop, Obsidian apps
  Given web client is served by khoj server, it doesn't need to
  explicitly allow for khoj.dev domain. So if user self-hosting, it'll
  automatically allow the domain in the CSP (via 'self')

  Whereas the Obsidian, Desktop clients allow configure the server URL.
  Note *switching server URL breaks CSP until app is reloaded*
This commit is contained in:
Debanjum Singh Solanky
2024-02-21 03:12:00 +05:30
parent 179c70dba8
commit 9f80c2ab76
3 changed files with 53 additions and 1 deletions

View File

@@ -1,4 +1,4 @@
const { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage, shell } = require('electron');
const { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage, shell, session } = require('electron');
const todesktop = require("@todesktop/runtime");
const khojPackage = require('./package.json');
@@ -401,6 +401,33 @@ async function getUserInfo() {
}
}
function addCSPHeaderToSession () {
// Get hostURL from store or use default
const hostURL = store.get('hostURL') || KHOJ_URL;
// Construct Content Security Policy
const defaultDomains = `'self' ${hostURL} https://app.khoj.dev https://assets.khoj.dev`;
const default_src = `default-src ${defaultDomains};`;
const script_src = `script-src ${defaultDomains} 'unsafe-inline';`;
const connect_src = `connect-src ${hostURL} https://ipapi.co/json;`;
const style_src = `style-src ${defaultDomains} 'unsafe-inline' https://fonts.googleapis.com;`;
const img_src = `img-src ${defaultDomains} data: https://*.khoj.dev https://*.googleusercontent.com;`;
const font_src = `font-src https://fonts.gstatic.com;`;
const child_src = `child-src 'none';`;
const objectSrc = `object-src 'none';`;
const csp = `${default_src} ${script_src} ${connect_src} ${style_src} ${img_src} ${font_src} ${child_src} ${objectSrc}`;
// Add Content Security Policy to all web requests
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [csp]
}
})
})
}
let firstRun = true;
let win = null;
let titleBarStyle = process.platform === 'win32' ? 'default' : 'hidden';
@@ -480,6 +507,8 @@ const createWindow = (tab = 'chat.html') => {
}
app.whenReady().then(() => {
addCSPHeaderToSession();
ipcMain.on('set-title', handleSetTitle);
ipcMain.handle('handleFileOpen', (event, type) => {