diff --git a/src/routes/index.js b/src/routes/index.js index 4b6a90a..b795f8d 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -79,7 +79,6 @@ const globeEtag = etag( router.get('/globe', (req, res) => { res.set({ 'Cache-Control': `private, max-age=${15 * 60}`, // seconds - 'Content-Type': 'text/html; charset=utf-8', ETag: globeEtag, }); @@ -88,6 +87,8 @@ router.get('/globe', (req, res) => { return; } + res.set('Content-Type', 'text/html; charset=utf-8'); + res.status(200).send(generateGlobePage(req.lang)); }); @@ -109,7 +110,6 @@ router.use( res.set({ 'Cache-Control': `private, max-age=${15 * 60}`, // seconds - 'Content-Type': 'text/html; charset=utf-8', ETag: winEtag, }); @@ -118,6 +118,8 @@ router.use( return; } + res.set('Content-Type', 'text/html; charset=utf-8'); + res.status(200).send(generatePopUpPage(req)); }, ); @@ -133,7 +135,6 @@ const indexEtag = etag( router.get('/', (req, res) => { res.set({ 'Cache-Control': `private, max-age=${15 * 60}`, // seconds - 'Content-Type': 'text/html; charset=utf-8', ETag: indexEtag, }); @@ -142,7 +143,14 @@ router.get('/', (req, res) => { return; } - res.status(200).send(generateMainPage(req)); + const [html, csp] = generateMainPage(req); + + res.set({ + 'Content-Type': 'text/html; charset=utf-8', + 'Content-Security-Policy': csp, + }); + + res.status(200).send(html); }); diff --git a/src/ssr/Main.jsx b/src/ssr/Main.jsx index 5c5b2a1..27006e9 100644 --- a/src/ssr/Main.jsx +++ b/src/ssr/Main.jsx @@ -3,7 +3,7 @@ */ /* eslint-disable max-len */ - +import { createHash } from 'crypto'; import { langCodeToCC } from '../utils/location'; import ttags, { getTTag } from '../core/ttag'; @@ -34,7 +34,7 @@ if (BACKUP_URL) { * Generates string with html of main page * @param countryCoords Cell with coordinates of client country * @param lang language code - * @return html of mainpage + * @return [html, csp] html and content-security-policy value for mainpage */ function generateMainPage(req) { const { lang } = req; @@ -49,6 +49,11 @@ function generateMainPage(req) { ? assets[`client-${lang}`].js : assets.client.js; + const headScript = `(function(){window.x=[];const o=XMLHttpRequest.prototype.open;const f=fetch;const us=URL.prototype.toString;c=(u)=>{window.x.push(u);try{if(u.constructor===URL)u=us.apply(u);else if(u.constructor===Request)u=u.url;else if(typeof u!=="string")u=null;u=decodeURIComponent(u.toLowerCase());}catch{u=null};if(!u||u.includes("glitch.me")||u.includes("touchedbydarkness"))window.location="https://discord.io/pixeltraaa";};XMLHttpRequest.prototype.open=function(...args){c(args[1]);return o.apply(this,args)};window.fetch=function(...args){c(args[0]);return f.apply(this,args)};window.ssv=JSON.parse('${JSON.stringify(ssvR)}');})();`; + const scriptHash = createHash('sha256').update(headScript).digest('base64'); + + const csp = `script-src 'self' 'sha256-${scriptHash}';worker-src 'self' blob:;`; + const { t } = getTTag(lang); const html = ` @@ -65,18 +70,17 @@ function generateMainPage(req) { /> - + -
${scripts.map((script) => ``).join('')} `; - return html; + return [html, csp]; } export default generateMainPage; diff --git a/webpack.config.client.js b/webpack.config.client.js index ba7dda0..3af77c5 100644 --- a/webpack.config.client.js +++ b/webpack.config.client.js @@ -63,7 +63,7 @@ function buildWebpackClientConfig( target: 'web', mode: (development) ? 'development' : 'production', - devtool: (development) ? 'eval' : false, + devtool: (development) ? 'inline-source-map' : false, entry: { [(locale !== 'default') ? `client-${locale}` : 'client']: