From b0cd1bac18cb6bb454545cf6e0333fc82fcbc445 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:31:09 +0200 Subject: [PATCH] add CSP nonce to styles --- frontend/index.html | 1 + frontend/src/assets/img/logo.svg | 10 +++--- frontend/src/assets/img/logoDark.svg | 9 ++++- frontend/src/assets/img/logoDarkWithText.svg | 26 +++++++++++++- .../img/logoWhiteTransparentHorizontal.svg | 34 ++++++++++++++++++- frontend/src/assets/img/logoWithWhiteText.svg | 26 +++++++++++++- frontend/src/themes/ThemeProvider.tsx | 5 +++ src/lib/app.ts | 6 ++-- src/lib/middleware/secure-headers.ts | 7 ++-- src/lib/util/load-index-html.ts | 9 ++++- src/lib/util/rewriteHTML.ts | 10 ++++++ .../img/Symbol_DarkBlue_Transparent.svg | 10 +++++- .../static/img/Symbol_White_Transparent.svg | 11 +++++- website/static/img/logo.svg | 20 ++++++++++- 14 files changed, 167 insertions(+), 17 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index a74fbe07c1..7629c273ca 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7,6 +7,7 @@ + Unleash diff --git a/frontend/src/assets/img/logo.svg b/frontend/src/assets/img/logo.svg index 843669fff4..761ab03730 100644 --- a/frontend/src/assets/img/logo.svg +++ b/frontend/src/assets/img/logo.svg @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/frontend/src/assets/img/logoDark.svg b/frontend/src/assets/img/logoDark.svg index b79f917031..306c574001 100644 --- a/frontend/src/assets/img/logoDark.svg +++ b/frontend/src/assets/img/logoDark.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + + + diff --git a/frontend/src/assets/img/logoDarkWithText.svg b/frontend/src/assets/img/logoDarkWithText.svg index 51862398e8..e590964ba7 100644 --- a/frontend/src/assets/img/logoDarkWithText.svg +++ b/frontend/src/assets/img/logoDarkWithText.svg @@ -1 +1,25 @@ - \ No newline at end of file + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/logoWhiteTransparentHorizontal.svg b/frontend/src/assets/img/logoWhiteTransparentHorizontal.svg index 8f78bf75a2..7ce72f39f9 100644 --- a/frontend/src/assets/img/logoWhiteTransparentHorizontal.svg +++ b/frontend/src/assets/img/logoWhiteTransparentHorizontal.svg @@ -1 +1,33 @@ - \ No newline at end of file + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/logoWithWhiteText.svg b/frontend/src/assets/img/logoWithWhiteText.svg index c966754f9e..705deff71d 100644 --- a/frontend/src/assets/img/logoWithWhiteText.svg +++ b/frontend/src/assets/img/logoWithWhiteText.svg @@ -1 +1,25 @@ - \ No newline at end of file + + + + + + + + + + + + + + + diff --git a/frontend/src/themes/ThemeProvider.tsx b/frontend/src/themes/ThemeProvider.tsx index ed50d60300..a226dce4af 100644 --- a/frontend/src/themes/ThemeProvider.tsx +++ b/frontend/src/themes/ThemeProvider.tsx @@ -5,9 +5,14 @@ import createCache from '@emotion/cache'; import { CacheProvider } from '@emotion/react'; import { useThemeMode } from 'hooks/useThemeMode'; +const nonce = + document.querySelector('meta[name=cspNonce]')?.getAttribute('content') || + undefined; + export const muiCache = createCache({ key: 'mui', prepend: true, + nonce, }); export const ThemeProvider: FC = ({ children }) => { diff --git a/src/lib/app.ts b/src/lib/app.ts index 580e2b9d00..e03299a15a 100644 --- a/src/lib/app.ts +++ b/src/lib/app.ts @@ -1,3 +1,4 @@ +import crypto from 'crypto'; import express, { Application, RequestHandler } from 'express'; import compression from 'compression'; import favicon from 'serve-favicon'; @@ -41,7 +42,8 @@ export default async function getApp( const baseUriPath = config.server.baseUriPath || ''; const publicFolder = config.publicFolder || findPublicFolder(); - let indexHTML = await loadIndexHTML(config, publicFolder); + const cspNonce = crypto.randomBytes(16).toString('hex'); + let indexHTML = await loadIndexHTML(config, publicFolder, cspNonce); app.set('trust proxy', true); app.disable('x-powered-by'); @@ -84,7 +86,7 @@ export default async function getApp( if (unleashSession) { app.use(unleashSession); } - app.use(secureHeaders(config)); + app.use(secureHeaders(config, cspNonce)); app.use(express.urlencoded({ extended: true })); app.use(favicon(path.join(publicFolder, 'favicon.ico'))); app.use(baseUriPath, favicon(path.join(publicFolder, 'favicon.ico'))); diff --git a/src/lib/middleware/secure-headers.ts b/src/lib/middleware/secure-headers.ts index 3a1812bae7..ed39bab368 100644 --- a/src/lib/middleware/secure-headers.ts +++ b/src/lib/middleware/secure-headers.ts @@ -3,7 +3,10 @@ import { RequestHandler } from 'express'; import { IUnleashConfig } from '../types'; import { hoursToSeconds } from 'date-fns'; -const secureHeaders: (config: IUnleashConfig) => RequestHandler = (config) => { +const secureHeaders: ( + config: IUnleashConfig, + cspNonce?: string, +) => RequestHandler = (config, cspNonce) => { if (config.secureHeaders) { return helmet({ hsts: { @@ -28,7 +31,7 @@ const secureHeaders: (config: IUnleashConfig) => RequestHandler = (config) => { ], styleSrc: [ "'self'", - "'unsafe-inline'", + ...(cspNonce ? [`'nonce-${cspNonce}'`] : []), 'cdn.getunleash.io', 'fonts.googleapis.com', 'fonts.gstatic.com', diff --git a/src/lib/util/load-index-html.ts b/src/lib/util/load-index-html.ts index 865162af0e..ce36be13d3 100644 --- a/src/lib/util/load-index-html.ts +++ b/src/lib/util/load-index-html.ts @@ -7,6 +7,7 @@ import fetch from 'make-fetch-happen'; export async function loadIndexHTML( config: IUnleashConfig, publicFolder: string, + cspNonce?: string, ): Promise { const { cdnPrefix, baseUriPath = '' } = config.server; const uiFlags = JSON.stringify(config.ui.flags); @@ -23,5 +24,11 @@ export async function loadIndexHTML( .toString(); } - return rewriteHTML(indexHTML, baseUriPath, cdnPrefix, uiFlags); + return rewriteHTML( + indexHTML, + baseUriPath, + cdnPrefix, + uiFlags, + config.secureHeaders ? cspNonce : undefined, + ); } diff --git a/src/lib/util/rewriteHTML.ts b/src/lib/util/rewriteHTML.ts index 58de0c11c4..30daf44437 100644 --- a/src/lib/util/rewriteHTML.ts +++ b/src/lib/util/rewriteHTML.ts @@ -3,6 +3,7 @@ export const rewriteHTML = ( rewriteValue: string, cdnPrefix?: string, uiFlags?: string, + cspNonce?: string, ): string => { let result = input; result = result.replace(/::baseUriPath::/gi, rewriteValue); @@ -18,5 +19,14 @@ export const rewriteHTML = ( `${cdnPrefix || rewriteValue}/static`, ); + if (cspNonce) { + result = result.replace(/::cspNonce::/gi, cspNonce); + } else { + result = result.replace( + '', + '', + ); + } + return result; }; diff --git a/website/static/img/Symbol_DarkBlue_Transparent.svg b/website/static/img/Symbol_DarkBlue_Transparent.svg index b79f917031..b0fe294dbe 100644 --- a/website/static/img/Symbol_DarkBlue_Transparent.svg +++ b/website/static/img/Symbol_DarkBlue_Transparent.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + diff --git a/website/static/img/Symbol_White_Transparent.svg b/website/static/img/Symbol_White_Transparent.svg index 86084db7fa..e7285ada8c 100644 --- a/website/static/img/Symbol_White_Transparent.svg +++ b/website/static/img/Symbol_White_Transparent.svg @@ -1 +1,10 @@ - \ No newline at end of file + + + + + + + diff --git a/website/static/img/logo.svg b/website/static/img/logo.svg index b79f917031..f7e0e566dc 100644 --- a/website/static/img/logo.svg +++ b/website/static/img/logo.svg @@ -1 +1,19 @@ - \ No newline at end of file + + + + + +