diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index fe1eb268c1..6d453bcab3 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -4,6 +4,7 @@ exports[`should create default config 1`] = ` { "accessControlMaxAge": 86400, "additionalCspAllowedDomains": { + "connectSrc": [], "defaultSrc": [], "fontSrc": [], "imgSrc": [], diff --git a/src/lib/create-config.test.ts b/src/lib/create-config.test.ts index 3f367ed7d6..8995972fe2 100644 --- a/src/lib/create-config.test.ts +++ b/src/lib/create-config.test.ts @@ -277,6 +277,7 @@ test('should yield all empty lists when no additionalCspAllowedDomains are set', expect(config.additionalCspAllowedDomains.styleSrc).toStrictEqual([]); expect(config.additionalCspAllowedDomains.scriptSrc).toStrictEqual([]); expect(config.additionalCspAllowedDomains.imgSrc).toStrictEqual([]); + expect(config.additionalCspAllowedDomains.connectSrc).toStrictEqual([]); }); test('If additionalCspAllowedDomains is set in config map, passes through', async () => { @@ -287,6 +288,7 @@ test('If additionalCspAllowedDomains is set in config map, passes through', asyn styleSrc: [], scriptSrc: [], imgSrc: [], + connectSrc: [], }, }); expect(config.additionalCspAllowedDomains).toBeDefined(); @@ -297,6 +299,7 @@ test('If additionalCspAllowedDomains is set in config map, passes through', asyn expect(config.additionalCspAllowedDomains.styleSrc).toStrictEqual([]); expect(config.additionalCspAllowedDomains.scriptSrc).toStrictEqual([]); expect(config.additionalCspAllowedDomains.imgSrc).toStrictEqual([]); + expect(config.additionalCspAllowedDomains.connectSrc).toStrictEqual([]); }); test('Can set partial additionalCspDomains', () => { @@ -321,6 +324,7 @@ test.each([ ['CSP_ALLOWED_STYLE', 'googlefonts.com', 'styleSrc'], ['CSP_ALLOWED_SCRIPT', 'googlefonts.com', 'scriptSrc'], ['CSP_ALLOWED_IMG', 'googlefonts.com', 'imgSrc'], + ['CSP_ALLOWED_CONNECT', 'googlefonts.com', 'connectSrc'], ])( 'When %s is set to %s. %s should include passed in domain', (env, domain, key) => { @@ -342,6 +346,7 @@ test('When multiple CSP environment variables are set, respects them all', () => process.env.CSP_ALLOWED_DEFAULT = 'googlefonts.com'; process.env.CSP_ALLOWED_IMG = 'googlefonts.com'; process.env.CSP_ALLOWED_SCRIPT = 'plausible.getunleash.io'; + process.env.CSP_ALLOWED_CONNECT = 'plausible.getunleash.io'; const config = createConfig({}); expect(config.additionalCspAllowedDomains.imgSrc).toStrictEqual([ 'googlefonts.com', @@ -352,9 +357,13 @@ test('When multiple CSP environment variables are set, respects them all', () => expect(config.additionalCspAllowedDomains.scriptSrc).toStrictEqual([ 'plausible.getunleash.io', ]); + expect(config.additionalCspAllowedDomains.connectSrc).toStrictEqual([ + 'plausible.getunleash.io', + ]); delete process.env.CSP_ALLOWED_DEFAULT; delete process.env.CSP_ALLOWED_IMG; delete process.env.CSP_ALLOWED_SCRIPT; + delete process.env.CSP_ALLOWED_CONNECT; }); test('Supports multiple domains comma separated in environment variables', () => { diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index 8fa5aac72e..1cd2115878 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -310,6 +310,7 @@ const parseCspConfig = ( scriptSrc: cspConfig.scriptSrc || [], imgSrc: cspConfig.imgSrc || [], styleSrc: cspConfig.styleSrc || [], + connectSrc: cspConfig.connectSrc || [], }; }; @@ -319,12 +320,14 @@ const parseCspEnvironmentVariables = (): ICspDomainConfig => { const styleSrc = process.env.CSP_ALLOWED_STYLE?.split(',') || []; const scriptSrc = process.env.CSP_ALLOWED_SCRIPT?.split(',') || []; const imgSrc = process.env.CSP_ALLOWED_IMG?.split(',') || []; + const connectSrc = process.env.CSP_ALLOWED_CONNECT?.split(',') || []; return { defaultSrc, fontSrc, styleSrc, scriptSrc, imgSrc, + connectSrc, }; }; diff --git a/src/lib/middleware/secure-headers.ts b/src/lib/middleware/secure-headers.ts index 982e5d944e..29d4488d4b 100644 --- a/src/lib/middleware/secure-headers.ts +++ b/src/lib/middleware/secure-headers.ts @@ -47,6 +47,13 @@ const secureHeaders: (config: IUnleashConfig) => RequestHandler = (config) => { 'gravatar.com', ...config.additionalCspAllowedDomains.imgSrc, ], + connectSrc: [ + "'self'", + 'cdn.getunleash.io', + 'gravatar.com', + 'europe-west3-metrics-304612.cloudfunctions.net', + ...config.additionalCspAllowedDomains.connectSrc, + ], }, }, crossOriginEmbedderPolicy: false, diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts index 17c4c8035d..e8adf6186e 100644 --- a/src/lib/types/option.ts +++ b/src/lib/types/option.ts @@ -159,6 +159,7 @@ export interface ICspDomainOptions { styleSrc?: string[]; scriptSrc?: string[]; imgSrc?: string[]; + connectSrc?: string[]; } export interface ICspDomainConfig { @@ -167,6 +168,7 @@ export interface ICspDomainConfig { styleSrc: string[]; scriptSrc: string[]; imgSrc: string[]; + connectSrc: string[]; } interface IFrontendApi { diff --git a/website/docs/reference/deploy/configuring-unleash.md b/website/docs/reference/deploy/configuring-unleash.md index 3f9e9378ec..11641785be 100644 --- a/website/docs/reference/deploy/configuring-unleash.md +++ b/website/docs/reference/deploy/configuring-unleash.md @@ -107,6 +107,7 @@ unleash.start(unleashOptions); - You can set the environment variable CSP_ALLOWED_STYLE to allow new styleSrc (comma separated list) - You can set the environment variable CSP_ALLOWED_SCRIPT to allow new scriptSrc (comma separated list) - You can set the environment variable CSP_ALLOWED_IMG to allow new imgSrc (comma separated list) + - You can set the environment variable CSP_ALLOWED_CONNECT to allow new connectSrc (comma separated list) - **server** - The server config object taking the following properties - _port_ - which port the unleash-server should bind to. If port is omitted or is 0, the operating system will assign an arbitrary unused port. Will be ignored if pipe is specified. This value may also be set via the `HTTP_PORT` environment variable - _host_ - which host the unleash-server should bind to. If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. This value may also be set via the `HTTP_HOST` environment variable