2025-06-06 18:30:19 +02:00
/ *
* Vencord , a Discord client mod
* Copyright ( c ) 2025 Vendicated and contributors
* SPDX - License - Identifier : GPL - 3.0 - or - later
* /
2025-06-12 02:19:45 +02:00
import { NativeSettings } from "@main/settings" ;
2025-06-06 18:30:19 +02:00
import { session } from "electron" ;
type PolicyMap = Record < string , string [ ] > ;
export const ConnectSrc = [ "connect-src" ] ;
2025-06-12 02:19:45 +02:00
export const ImageSrc = [ . . . ConnectSrc , "img-src" ] ;
2025-06-06 18:30:19 +02:00
export const CssSrc = [ "style-src" , "font-src" ] ;
2026-07-15 02:13:45 -05:00
export const ImageAndMediaSrc = [ . . . ImageSrc , "media-src" ] ;
2025-06-12 02:19:45 +02:00
export const ImageAndCssSrc = [ . . . ImageSrc , . . . CssSrc ] ;
2025-06-19 22:08:31 -04:00
export const ImageScriptsAndCssSrc = [ . . . ImageAndCssSrc , "script-src" , "worker-src" ] ;
2025-06-26 15:52:24 -04:00
export const CSPSrc = [ "style-src" , "connect-src" , "img-src" , "frame-src" , "font-src" , "media-src" , "worker-src" ] ;
2025-06-06 18:30:19 +02:00
// Plugins can whitelist their own domains by importing this object in their native.ts
// script and just adding to it. But generally, you should just edit this file instead
export const CspPolicies : PolicyMap = {
2025-06-14 00:55:14 +02:00
"http://localhost:*" : ImageAndCssSrc ,
"http://127.0.0.1:*" : ImageAndCssSrc ,
"localhost:*" : ImageAndCssSrc ,
"127.0.0.1:*" : ImageAndCssSrc ,
2025-06-06 18:30:19 +02:00
2025-06-12 02:19:45 +02:00
"*.github.io" : ImageAndCssSrc , // GitHub pages, used by most themes
"github.com" : ImageAndCssSrc , // GitHub content (stuff uploaded to markdown forms), used by most themes
"raw.githubusercontent.com" : ImageAndCssSrc , // GitHub raw, used by some themes
"*.gitlab.io" : ImageAndCssSrc , // GitLab pages, used by some themes
"gitlab.com" : ImageAndCssSrc , // GitLab raw, used by some themes
"*.codeberg.page" : ImageAndCssSrc , // Codeberg pages, used by some themes
"codeberg.org" : ImageAndCssSrc , // Codeberg raw, used by some themes
"*.githack.com" : ImageAndCssSrc , // githack (namely raw.githack.com), used by some themes
"jsdelivr.net" : ImageAndCssSrc , // jsDelivr, used by very few themes
2025-06-06 18:30:19 +02:00
"fonts.googleapis.com" : CssSrc , // Google Fonts, used by many themes
2025-06-12 02:19:45 +02:00
"i.imgur.com" : ImageSrc , // Imgur, used by some themes
"i.ibb.co" : ImageSrc , // ImgBB, used by some themes
"i.pinimg.com" : ImageSrc , // Pinterest, used by some themes
"files.catbox.moe" : ImageAndCssSrc , // Catbox, used by some themes
2025-06-06 18:30:19 +02:00
2025-06-12 02:19:45 +02:00
"cdn.discordapp.com" : ImageAndCssSrc , // Discord CDN, used by Vencord and some themes to load media
"media.discordapp.net" : ImageSrc , // Discord media CDN, possible alternative to Discord CDN
2025-06-06 18:30:19 +02:00
// CDNs used for some things by Vencord.
// FIXME: we really should not be using CDNs anymore
2025-06-12 02:19:45 +02:00
"cdnjs.cloudflare.com" : ImageScriptsAndCssSrc ,
"cdn.jsdelivr.net" : ImageScriptsAndCssSrc ,
2025-06-06 18:30:19 +02:00
// Function Specific
"api.github.com" : ConnectSrc , // used for updating Vencord itself
"ws.audioscrobbler.com" : ConnectSrc , // Last.fm API
2026-07-15 10:03:39 -06:00
"musicbrainz.org" : ConnectSrc ,
"*.listenbrainz.org" : ConnectSrc ,
"coverartarchive.org" : ConnectSrc ,
"archive.org" : ConnectSrc ,
"*.archive.org" : ConnectSrc ,
2025-06-06 18:30:19 +02:00
"translate-pa.googleapis.com" : ConnectSrc , // Google Translate API
2025-06-12 02:19:45 +02:00
"*.vencord.dev" : ImageSrc , // VenCloud (api.vencord.dev) and Badges (badges.vencord.dev)
"manti.vendicated.dev" : ImageSrc , // ReviewDB API
2025-06-06 18:30:19 +02:00
"decor.fieryflames.dev" : ConnectSrc , // Decor API
2025-06-12 02:19:45 +02:00
"ugc.decor.fieryflames.dev" : ImageSrc , // Decor CDN
2025-06-06 18:30:19 +02:00
"sponsor.ajay.app" : ConnectSrc , // Dearrow API
2025-06-12 02:19:45 +02:00
"dearrow-thumb.ajay.app" : ImageSrc , // Dearrow Thumbnail CDN
"usrbg.is-hardly.online" : ImageSrc , // USRBG API
"icons.duckduckgo.com" : ImageSrc , // DuckDuckGo Favicon API (Reverse Image Search)
2026-07-15 02:13:45 -05:00
// Tenor, used by TenorSearch plugin and some themes
"*.tenor.com" : ImageAndMediaSrc ,
"*.tenor.co" : ImageAndMediaSrc ,
2025-06-06 18:30:19 +02:00
} ;
const findHeader = ( headers : PolicyMap , headerName : Lowercase < string > ) = > {
return Object . keys ( headers ) . find ( h = > h . toLowerCase ( ) === headerName ) ;
} ;
const parsePolicy = ( policy : string ) : PolicyMap = > {
const result : PolicyMap = { } ;
policy . split ( ";" ) . forEach ( directive = > {
const [ directiveKey , . . . directiveValue ] = directive . trim ( ) . split ( /\s+/g ) ;
if ( directiveKey && ! Object . prototype . hasOwnProperty . call ( result , directiveKey ) ) {
result [ directiveKey ] = directiveValue ;
}
} ) ;
return result ;
} ;
const stringifyPolicy = ( policy : PolicyMap ) : string = >
Object . entries ( policy )
. filter ( ( [ , values ] ) = > values ? . length )
. map ( directive = > directive . flat ( ) . join ( " " ) )
. join ( "; " ) ;
const patchCsp = ( headers : PolicyMap ) = > {
const reportOnlyHeader = findHeader ( headers , "content-security-policy-report-only" ) ;
if ( reportOnlyHeader )
delete headers [ reportOnlyHeader ] ;
const header = findHeader ( headers , "content-security-policy" ) ;
if ( header ) {
const csp = parsePolicy ( headers [ header ] [ 0 ] ) ;
const pushDirective = ( directive : string , . . . values : string [ ] ) = > {
csp [ directive ] ? ? = [ . . . ( csp [ "default-src" ] ? ? [ ] ) ] ;
csp [ directive ] . push ( . . . values ) ;
} ;
pushDirective ( "style-src" , "'unsafe-inline'" ) ;
// we could make unsafe-inline safe by using strict-dynamic with a random nonce on our Vencord loader script https://content-security-policy.com/strict-dynamic/
// HOWEVER, at the time of writing (24 Jan 2025), Discord is INSANE and also uses unsafe-inline
// Once they stop using it, we also should
pushDirective ( "script-src" , "'unsafe-inline'" , "'unsafe-eval'" ) ;
for ( const directive of [ "style-src" , "connect-src" , "img-src" , "font-src" , "media-src" , "worker-src" ] ) {
2025-10-16 17:56:17 -04:00
pushDirective ( directive , "blob:" , "data:" , "vencord:" , "vesktop:" , "equicord:" , "equibop:" ) ;
2025-06-06 18:30:19 +02:00
}
2025-06-12 02:19:45 +02:00
for ( const [ host , directives ] of Object . entries ( NativeSettings . store . customCspRules ) ) {
for ( const directive of directives ) {
pushDirective ( directive , host ) ;
}
}
2025-06-06 18:30:19 +02:00
for ( const [ host , directives ] of Object . entries ( CspPolicies ) ) {
for ( const directive of directives ) {
pushDirective ( directive , host ) ;
}
}
headers [ header ] = [ stringifyPolicy ( csp ) ] ;
}
} ;
export function initCsp() {
2025-10-09 13:47:58 -04:00
session . defaultSession . webRequest . onHeadersReceived ( ( { responseHeaders , resourceType } , cb ) = > {
2025-06-06 18:30:19 +02:00
if ( responseHeaders ) {
if ( resourceType === "mainFrame" )
patchCsp ( responseHeaders ) ;
// Fix hosts that don't properly set the css content type, such as
// raw.githubusercontent.com
if ( resourceType === "stylesheet" ) {
const header = findHeader ( responseHeaders , "content-type" ) ;
if ( header )
responseHeaders [ header ] = [ "text/css" ] ;
}
}
cb ( { cancel : false , responseHeaders } ) ;
} ) ;
// assign a noop to onHeadersReceived to prevent other mods from adding their own incompatible ones.
// For instance, OpenAsar adds their own that doesn't fix content-type for stylesheets which makes it
// impossible to load css from github raw despite our fix above
session . defaultSession . webRequest . onHeadersReceived = ( ) = > { } ;
}