mirror of
https://github.com/khoj-ai/khoj.git
synced 2024-11-30 19:03:01 +01:00
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:
parent
179c70dba8
commit
9f80c2ab76
3 changed files with 53 additions and 1 deletions
|
@ -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 todesktop = require("@todesktop/runtime");
|
||||||
const khojPackage = require('./package.json');
|
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 firstRun = true;
|
||||||
let win = null;
|
let win = null;
|
||||||
let titleBarStyle = process.platform === 'win32' ? 'default' : 'hidden';
|
let titleBarStyle = process.platform === 'win32' ? 'default' : 'hidden';
|
||||||
|
@ -480,6 +507,8 @@ const createWindow = (tab = 'chat.html') => {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
|
addCSPHeaderToSession();
|
||||||
|
|
||||||
ipcMain.on('set-title', handleSetTitle);
|
ipcMain.on('set-title', handleSetTitle);
|
||||||
|
|
||||||
ipcMain.handle('handleFileOpen', (event, type) => {
|
ipcMain.handle('handleFileOpen', (event, type) => {
|
||||||
|
|
|
@ -80,6 +80,20 @@ export class KhojChatView extends KhojPaneView {
|
||||||
|
|
||||||
super.onOpen();
|
super.onOpen();
|
||||||
|
|
||||||
|
// Construct Content Security Policy
|
||||||
|
let defaultDomains = `'self' ${this.setting.khojUrl} https://app.khoj.dev https://assets.khoj.dev`;
|
||||||
|
const defaultSrc = `default-src ${defaultDomains};`;
|
||||||
|
const scriptSrc = `script-src ${defaultDomains} 'unsafe-inline';`;
|
||||||
|
const connectSrc = `connect-src ${this.setting.khojUrl} https://ipapi.co/json;`;
|
||||||
|
const styleSrc = `style-src ${defaultDomains} 'unsafe-inline';`;
|
||||||
|
const imgSrc = `img-src ${defaultDomains} data: https://*.khoj.dev https://*.googleusercontent.com;`;
|
||||||
|
const childSrc = `child-src 'none';`;
|
||||||
|
const objectSrc = `object-src 'none';`;
|
||||||
|
const csp = `${defaultSrc} ${scriptSrc} ${connectSrc} ${styleSrc} ${imgSrc} ${childSrc} ${objectSrc}`;
|
||||||
|
|
||||||
|
// Add CSP meta tag to the Khoj Chat modal
|
||||||
|
document.head.createEl("meta", { attr: { "http-equiv": "Content-Security-Policy", "content": `${csp}` } });
|
||||||
|
|
||||||
// Create area for chat logs
|
// Create area for chat logs
|
||||||
let chatBodyEl = contentEl.createDiv({ attr: { id: "khoj-chat-body", class: "khoj-chat-body" } });
|
let chatBodyEl = contentEl.createDiv({ attr: { id: "khoj-chat-body", class: "khoj-chat-body" } });
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,15 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
|
||||||
<meta property="og:image" content="https://assets.khoj.dev/khoj_hero.png">
|
<meta property="og:image" content="https://assets.khoj.dev/khoj_hero.png">
|
||||||
|
<meta http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self' https://assets.khoj.dev;
|
||||||
|
script-src 'self' https://assets.khoj.dev 'unsafe-inline';
|
||||||
|
connect-src 'self' https://ipapi.co/json;
|
||||||
|
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
|
||||||
|
img-src 'self' data: https://*.khoj.dev https://*.googleusercontent.com;
|
||||||
|
font-src https://assets.khoj.dev https://fonts.gstatic.com;
|
||||||
|
child-src 'none';
|
||||||
|
object-src 'none';">
|
||||||
<title>Khoj - Chat</title>
|
<title>Khoj - Chat</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/assets/khoj.css?v={{ khoj_version }}">
|
<link rel="stylesheet" href="/static/assets/khoj.css?v={{ khoj_version }}">
|
||||||
|
|
Loading…
Reference in a new issue