1/** 2 * Shared Content-Security-Policy for SPA dashboard entry pages. 3 * 4 * Load synchronously in <head> (before restrictive CSP meta tags): 5 * <script src="/scripts/effects/spaDashboardCsp.js"></script> 6 * 7 * Optional extra connect-src hosts (tool pages): 8 * <script>window.__spaCspExtraConnect = 'https://example.com';</script> 9 * <script src="/scripts/effects/spaDashboardCsp.js"></script> 10 * 11 * Meta-tag CSP is fixed at first paint â changing it later via JS does NOT relax the policy. 12 */ 13(function (global) { 14 'use strict'; 15 16 var SPA_DASHBOARD_CSP_PARTS = [ 17 "default-src 'self' https:", 18 "script-src 'self' 'unsafe-inline' https: blob: 'wasm-unsafe-eval' 'unsafe-eval'", 19 "style-src 'self' 'unsafe-inline' https:", 20 "font-src 'self' https://cdnjs.cloudflare.com https://fonts.googleapis.com https://fonts.gstatic.com data:", 21 "img-src 'self' data: blob: https: https://cdn.throne.com https://cdn.discordapp.com https://i.scdn.co https://storage.googleapis.com https://static.wikia.nocookie.net https://adriansblinkiecollection.neocities.org https://wowee.neocities.org https://i.pinimg.com https://64.media.tumblr.com https://deckofcardsapi.com", 22 "connect-src 'self' https://api.pexels.com https://litter.catbox.moe https://media1.vocaroo.com https://raw.githubusercontent.com https://cdn.pixabay.com https://storage.googleapis.com https://www.gstatic.com https://*.firebaseio.com wss://*.firebaseio.com https://cdn.jsdelivr.net https://*.jsdelivr.net https://weirdscifi.ratiosemper.com https://api.sunrise-sunset.org https://api.weatherapi.com https://api.ipify.org https://ipapi.co https://icanhazip.com https://do.pishock.com https://api.allorigins.win https://corsproxy.io https://throne.com https://cdn.throne.com https://api.lanyard.rest wss://api.lanyard.rest https://discord.com https://open.spotify.com https://secure.geonames.org https://api.dictionaryapi.dev https://words.bighugelabs.com https://esm.sh https://unpkg.com https://*.unpkg.com https://0.peerjs.com wss://0.peerjs.com https://linkmeta.dev https://api.klipy.com https://api.themoviedb.org https://latest.currency-api.pages.dev https://api.bigdatacloud.net https://archive.org https://*.archive.org https://hearthis.at https://*.hearthis.at https://hearthis.app https://*.hearthis.app https://api.mixfall.com https://mixfall.com https://cdn.mixfall.com https://stream.mixfall.com https://img.hearthis.at https://cdn.hearthis.at https://strudel.cc https://*.strudel.cc", 23 "media-src 'self' https: blob: data:", 24 "frame-src 'self' https://throne.com https://*.firebaseio.com https://*.firebaseapp.com https://app.hearthis.at https://strudel.cc https://*.strudel.cc https://wowee.neocities.org", 25 "worker-src 'self' blob:" 26 ]; 27 28 function mergeConnectExtras(baseParts, extraConnect) { 29 if (!extraConnect || !String(extraConnect).trim()) return baseParts.join('; ') + ';'; 30 var parts = baseParts.slice(); 31 var i; 32 for (i = 0; i < parts.length; i++) { 33 if (parts[i].indexOf('connect-src ') === 0) { 34 parts[i] = parts[i] + ' ' + String(extraConnect).trim(); 35 break; 36 } 37 } 38 return parts.join('; ') + ';'; 39 } 40 41 function buildDashboardCsp() { 42 var extra = global.__spaCspExtraConnect || ''; 43 return mergeConnectExtras(SPA_DASHBOARD_CSP_PARTS, extra); 44 } 45 46 /** True only when the policy already includes hosts added for global voice + chatroom. */ 47 function cspHasDashboardHosts(content) { 48 var c = content || ''; 49 return c.indexOf('api.lanyard.rest') !== -1 50 && c.indexOf('0.peerjs.com') !== -1 51 && c.indexOf('fonts.gstatic.com') !== -1; 52 } 53 54 function injectDashboardCspMeta() { 55 var content = buildDashboardCsp(); 56 global.SPA_DASHBOARD_CSP = content; 57 58 var existing = document.querySelector('meta[http-equiv="Content-Security-Policy"], meta[http-equiv="content-security-policy"]'); 59 if (existing && cspHasDashboardHosts(existing.content || '')) { 60 return; 61 } 62 63 var metas = document.querySelectorAll('meta[http-equiv="Content-Security-Policy"], meta[http-equiv="content-security-policy"]'); 64 var i; 65 for (i = metas.length - 1; i >= 0; i--) { 66 if (metas[i].parentNode) metas[i].parentNode.removeChild(metas[i]); 67 } 68 69 if (document.readyState === 'loading' && typeof document.write === 'function') {
70 document.write('<meta http-equiv="Content-Security-Policy" content="' + content.replace(/"/g, '"') + '">'); 71 return; 72 } 73 74 var meta = document.createElement('meta'); 75 meta.setAttribute('http-equiv', 'Content-Security-Policy'); 76 meta.setAttribute('content', content); 77 var head = document.head || document.getElementsByTagName('head')[0]; 78 if (head) head.insertBefore(meta, head.firstChild); 79 } 80 81 global.SPA_DASHBOARD_CSP = buildDashboardCsp(); 82 global.SpaDashboardCsp = { 83 build: buildDashboardCsp, 84 hasDashboardHosts: cspHasDashboardHosts, 85 inject: injectDashboardCspMeta, 86 warnIfIncomplete: function () { 87 var meta = document.querySelector('meta[http-equiv="Content-Security-Policy"], meta[http-equiv="content-security-policy"]'); 88 if (!meta || !cspHasDashboardHosts(meta.content || '')) { 89 console.warn( 90 '[SpaDashboardCsp] This page CSP is missing dashboard hosts (e.g. api.lanyard.rest, 0.peerjs.com). ' + 91 'Add a synchronous <script src="/scripts/effects/spaDashboardCsp.js"></script> in <head> ' + 92 'and remove restrictive CSP meta tags. SPA navigation cannot upgrade CSP after first paint.' 93 ); 94 } 95 } 96 }; 97 98 injectDashboardCspMeta(); 99})(typeof window !== 'undefined' ? window : this);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.