diff --git a/src/__tests__/headers/content-security-policy.ts b/src/__tests__/headers/content-security-policy.ts index 1226d100..1636a7ef 100644 --- a/src/__tests__/headers/content-security-policy.ts +++ b/src/__tests__/headers/content-security-policy.ts @@ -2,20 +2,23 @@ import { fortifyHeaders } from '../..'; describe('Content-Security-Policy Tests', () => { it('exercises full configuration options', () => { - const fortifiedHeaders = fortifyHeaders({ - contentSecurityPolicy: { - defaultSrc: ["'self'", 'somedomain.com', '*.somedomain.com'], - baseUri: ["'self'"], - fontSrc: ["'self'", 'https:', 'data:'], - frameAncestors: ["'self'"], - imgSrc: ["'self'", 'data:'], - objectSrc: ["'none'"], - scriptSrc: ["'self'"], - scriptSrcAttr: ["'none'"], - styleSrc: ["'self'", 'https:', "'unsafe-inline'"], - upgradeInsecureRequests: true, + const fortifiedHeaders = fortifyHeaders( + { + contentSecurityPolicy: { + defaultSrc: ["'self'", 'somedomain.com', '*.somedomain.com'], + baseUri: ["'self'"], + fontSrc: ["'self'", 'https:', 'data:'], + frameAncestors: ["'self'"], + imgSrc: ["'self'", 'data:'], + objectSrc: ["'none'"], + scriptSrc: ["'self'"], + scriptSrcAttr: ["'none'"], + styleSrc: ["'self'", 'https:', "'unsafe-inline'"], + upgradeInsecureRequests: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Content-Security-Policy': @@ -24,9 +27,12 @@ describe('Content-Security-Policy Tests', () => { }); it('returns defaults for Content-Security-Policy when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - contentSecurityPolicy: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + contentSecurityPolicy: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Content-Security-Policy': @@ -35,12 +41,15 @@ describe('Content-Security-Policy Tests', () => { }); it('returns the header as specified', () => { - const fortifiedHeaders = fortifyHeaders({ - contentSecurityPolicy: { - defaultSrc: ["'self'", 'https://'], - upgradeInsecureRequests: false, + const fortifiedHeaders = fortifyHeaders( + { + contentSecurityPolicy: { + defaultSrc: ["'self'", 'https://'], + upgradeInsecureRequests: false, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Content-Security-Policy': "default-src 'self' https://", diff --git a/src/__tests__/headers/cross-origin-embedder-policy.ts b/src/__tests__/headers/cross-origin-embedder-policy.ts index 440bdbd0..a8ef32d5 100644 --- a/src/__tests__/headers/cross-origin-embedder-policy.ts +++ b/src/__tests__/headers/cross-origin-embedder-policy.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('Cross-Origin-Embedder-Policy Tests', () => { it('returns defaults for Cross-Origin-Embedder-Policy when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginEmbedderPolicy: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + crossOriginEmbedderPolicy: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Embedder-Policy': 'require-corp', diff --git a/src/__tests__/headers/cross-origin-opener-policy.ts b/src/__tests__/headers/cross-origin-opener-policy.ts index 655052c0..7b59f8be 100644 --- a/src/__tests__/headers/cross-origin-opener-policy.ts +++ b/src/__tests__/headers/cross-origin-opener-policy.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('Cross-Origin-Opener-Policy Tests', () => { it('returns defaults for Cross-Origin-Opener-Policy when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginOpenerPolicy: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + crossOriginOpenerPolicy: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Opener-Policy': 'same-origin', @@ -12,11 +15,14 @@ describe('Cross-Origin-Opener-Policy Tests', () => { }); it('returns same-origin', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginOpenerPolicy: { - sameOrigin: true, + const fortifiedHeaders = fortifyHeaders( + { + crossOriginOpenerPolicy: { + sameOrigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Opener-Policy': 'same-origin', @@ -24,11 +30,14 @@ describe('Cross-Origin-Opener-Policy Tests', () => { }); it('returns same-origin-allow-popups', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginOpenerPolicy: { - sameOriginAllowPopups: true, + const fortifiedHeaders = fortifyHeaders( + { + crossOriginOpenerPolicy: { + sameOriginAllowPopups: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Opener-Policy': 'same-origin-allow-popups', @@ -36,11 +45,14 @@ describe('Cross-Origin-Opener-Policy Tests', () => { }); it('returns unsafe-none', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginOpenerPolicy: { - unsafeNone: true, + const fortifiedHeaders = fortifyHeaders( + { + crossOriginOpenerPolicy: { + unsafeNone: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Opener-Policy': 'unsafe-none', @@ -49,12 +61,15 @@ describe('Cross-Origin-Opener-Policy Tests', () => { it('enforces single-selection', () => { expect(() => - fortifyHeaders({ - crossOriginOpenerPolicy: { - unsafeNone: true, - sameOriginAllowPopups: true, + fortifyHeaders( + { + crossOriginOpenerPolicy: { + unsafeNone: true, + sameOriginAllowPopups: true, + }, }, - }), + { useDefaults: false }, + ), ).toThrowErrorMatchingInlineSnapshot( `"Cross-Origin-Opener-Policy only allows one selection. You can only specify one option for this header."`, ); diff --git a/src/__tests__/headers/cross-origin-resource-policy.ts b/src/__tests__/headers/cross-origin-resource-policy.ts index 40df6af6..57564c68 100644 --- a/src/__tests__/headers/cross-origin-resource-policy.ts +++ b/src/__tests__/headers/cross-origin-resource-policy.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('Cross-Origin-Resource-Policy Tests', () => { it('returns defaults for Cross-Origin-Resource-Policy when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginResourcePolicy: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + crossOriginResourcePolicy: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Resource-Policy': 'same-origin', @@ -12,11 +15,14 @@ describe('Cross-Origin-Resource-Policy Tests', () => { }); it('returns same-origin', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginResourcePolicy: { - sameOrigin: true, + const fortifiedHeaders = fortifyHeaders( + { + crossOriginResourcePolicy: { + sameOrigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Resource-Policy': 'same-origin', @@ -24,11 +30,14 @@ describe('Cross-Origin-Resource-Policy Tests', () => { }); it('returns same-site', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginResourcePolicy: { - sameSite: true, + const fortifiedHeaders = fortifyHeaders( + { + crossOriginResourcePolicy: { + sameSite: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Resource-Policy': 'same-site', @@ -36,11 +45,14 @@ describe('Cross-Origin-Resource-Policy Tests', () => { }); it('returns cross-origin', () => { - const fortifiedHeaders = fortifyHeaders({ - crossOriginResourcePolicy: { - crossOrigin: true, + const fortifiedHeaders = fortifyHeaders( + { + crossOriginResourcePolicy: { + crossOrigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Cross-Origin-Resource-Policy': 'cross-origin', @@ -49,12 +61,15 @@ describe('Cross-Origin-Resource-Policy Tests', () => { it('enforce single-selection', () => { expect(() => - fortifyHeaders({ - crossOriginResourcePolicy: { - crossOrigin: true, - sameSite: true, + fortifyHeaders( + { + crossOriginResourcePolicy: { + crossOrigin: true, + sameSite: true, + }, }, - }), + { useDefaults: false }, + ), ).toThrowErrorMatchingInlineSnapshot( `"Cross-Origin-Resource-Policy only allows one selection. You can only specify one option for this header."`, ); diff --git a/src/__tests__/headers/expect-ct.ts b/src/__tests__/headers/expect-ct.ts index 45ea2e6c..b6723dfc 100644 --- a/src/__tests__/headers/expect-ct.ts +++ b/src/__tests__/headers/expect-ct.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('Expect-Ct Tests', () => { it('returns defaults for Expect-CT when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - expectCt: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + expectCt: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Expect-Ct': 'max-age=0', @@ -12,13 +15,16 @@ describe('Expect-Ct Tests', () => { }); it('exercise full configuration', () => { - const fortifiedHeaders = fortifyHeaders({ - expectCt: { - enforce: true, - maxAge: 1000, - reportUri: 'report-endpoint/', + const fortifiedHeaders = fortifyHeaders( + { + expectCt: { + enforce: true, + maxAge: 1000, + reportUri: 'report-endpoint/', + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Expect-Ct': 'enforce; max-age=1000; report-uri=report-endpoint/', diff --git a/src/__tests__/headers/origin-agent-cluster.ts b/src/__tests__/headers/origin-agent-cluster.ts index f909b1d2..ce4d2fe5 100644 --- a/src/__tests__/headers/origin-agent-cluster.ts +++ b/src/__tests__/headers/origin-agent-cluster.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('Origin-Agent-Cluster Tests', () => { it('returns defaults for Origin-Agent-Cluster when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - originAgentCluster: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + originAgentCluster: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Origin-Agent-Cluster': '?1', @@ -12,9 +15,12 @@ describe('Origin-Agent-Cluster Tests', () => { }); it('returns defaults for Origin-Agent-Cluster when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - originAgentCluster: { enable: true }, - }); + const fortifiedHeaders = fortifyHeaders( + { + originAgentCluster: { enable: true }, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Origin-Agent-Cluster': '?1', @@ -22,9 +28,12 @@ describe('Origin-Agent-Cluster Tests', () => { }); it('returns defaults for Origin-Agent-Cluster when enabled is set to false', () => { - const fortifiedHeaders = fortifyHeaders({ - originAgentCluster: { enable: false }, - }); + const fortifiedHeaders = fortifyHeaders( + { + originAgentCluster: { enable: false }, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Origin-Agent-Cluster': '?0', diff --git a/src/__tests__/headers/referrer-policy.ts b/src/__tests__/headers/referrer-policy.ts index 83387c00..377d11be 100644 --- a/src/__tests__/headers/referrer-policy.ts +++ b/src/__tests__/headers/referrer-policy.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('Referrer-Policy Tests', () => { it('returns defaults for Referrer-Policy when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'no-referrer', @@ -12,11 +15,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns no-referrer', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - noReferrer: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + noReferrer: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'no-referrer', @@ -24,11 +30,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns no-referrer-when-downgrade', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - noReferrerWhenDowngrade: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + noReferrerWhenDowngrade: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'no-referrer-when-downgrade', @@ -36,11 +45,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns same-origin', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - sameOrigin: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + sameOrigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'same-origin', @@ -48,11 +60,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns origin', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - origin: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + origin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'origin', @@ -60,11 +75,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns strict-origin', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - strictOrigin: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + strictOrigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'strict-origin', @@ -72,11 +90,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns origin-when-cross-origin', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - originWhenCrossOrigin: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + originWhenCrossOrigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'origin-when-cross-origin', @@ -84,11 +105,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns strict-origin-when-cross-origin', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - strictOriginWhenCrossOrigin: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + strictOriginWhenCrossOrigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'strict-origin-when-cross-origin', @@ -96,11 +120,14 @@ describe('Referrer-Policy Tests', () => { }); it('returns unsafe-url', () => { - const fortifiedHeaders = fortifyHeaders({ - referrerPolicy: { - unsafeUrl: true, + const fortifiedHeaders = fortifyHeaders( + { + referrerPolicy: { + unsafeUrl: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Referrer-Policy': 'unsafe-url', diff --git a/src/__tests__/headers/strict-transport-security.ts b/src/__tests__/headers/strict-transport-security.ts index 195ba844..52ec53b1 100644 --- a/src/__tests__/headers/strict-transport-security.ts +++ b/src/__tests__/headers/strict-transport-security.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('Strict-Transport-Security Tests', () => { it('returns defaults for Strict-Transport-Security when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - strictTransportSecurity: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + strictTransportSecurity: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'Strict-Transport-Security': 'max-age=15552000', diff --git a/src/__tests__/headers/x-content-type-options.ts b/src/__tests__/headers/x-content-type-options.ts index be476a51..966f4f80 100644 --- a/src/__tests__/headers/x-content-type-options.ts +++ b/src/__tests__/headers/x-content-type-options.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('X-Content-Type-Options Tests', () => { it('returns defaults for X-Content-Type-Options when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - xContentTypeOptions: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + xContentTypeOptions: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Content-Type-Options': 'nosniff', @@ -12,11 +15,14 @@ describe('X-Content-Type-Options Tests', () => { }); it('returns nosniff', () => { - const fortifiedHeaders = fortifyHeaders({ - xContentTypeOptions: { - nosniff: true, + const fortifiedHeaders = fortifyHeaders( + { + xContentTypeOptions: { + nosniff: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Content-Type-Options': 'nosniff', diff --git a/src/__tests__/headers/x-dns-prefetch-control.ts b/src/__tests__/headers/x-dns-prefetch-control.ts index dcf0a0df..f535a808 100644 --- a/src/__tests__/headers/x-dns-prefetch-control.ts +++ b/src/__tests__/headers/x-dns-prefetch-control.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('X-DNS-Prefetch-Control Tests', () => { it('returns defaults for X-DNS-Prefetch-Control when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - xDnsPrefetchControl: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + xDnsPrefetchControl: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Dns-Prefetch-Control': 'off', @@ -12,11 +15,14 @@ describe('X-DNS-Prefetch-Control Tests', () => { }); it('returns off', () => { - const fortifiedHeaders = fortifyHeaders({ - xDnsPrefetchControl: { - off: true, + const fortifiedHeaders = fortifyHeaders( + { + xDnsPrefetchControl: { + off: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Dns-Prefetch-Control': 'off', @@ -24,11 +30,14 @@ describe('X-DNS-Prefetch-Control Tests', () => { }); it('returns on', () => { - const fortifiedHeaders = fortifyHeaders({ - xDnsPrefetchControl: { - on: true, + const fortifiedHeaders = fortifyHeaders( + { + xDnsPrefetchControl: { + on: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Dns-Prefetch-Control': 'on', @@ -37,12 +46,15 @@ describe('X-DNS-Prefetch-Control Tests', () => { it('enforces single-selection', () => { expect(() => - fortifyHeaders({ - xDnsPrefetchControl: { - on: true, - off: true, + fortifyHeaders( + { + xDnsPrefetchControl: { + on: true, + off: true, + }, }, - }), + { useDefaults: false }, + ), ).toThrowErrorMatchingInlineSnapshot( `"X-Dns-Prefetch-Control only allows one selection. You can only specify one option for this header."`, ); diff --git a/src/__tests__/headers/x-download-options.ts b/src/__tests__/headers/x-download-options.ts index 224ff969..74d3d638 100644 --- a/src/__tests__/headers/x-download-options.ts +++ b/src/__tests__/headers/x-download-options.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('X-Download-Options Tests', () => { it('returns defaults for X-Download-Options when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - xDownloadOptions: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + xDownloadOptions: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Download-Options': 'noopen', @@ -12,11 +15,14 @@ describe('X-Download-Options Tests', () => { }); it('returns noopen', () => { - const fortifiedHeaders = fortifyHeaders({ - xDownloadOptions: { - noopen: true, + const fortifiedHeaders = fortifyHeaders( + { + xDownloadOptions: { + noopen: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Download-Options': 'noopen', diff --git a/src/__tests__/headers/x-frame-options.ts b/src/__tests__/headers/x-frame-options.ts index 8744f2f2..688d09eb 100644 --- a/src/__tests__/headers/x-frame-options.ts +++ b/src/__tests__/headers/x-frame-options.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('X-Frame-Options Tests', () => { it('returns defaults for X-Frame-Options when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - xFrameOptions: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + xFrameOptions: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Frame-Options': 'SAMEORIGIN', @@ -12,11 +15,14 @@ describe('X-Frame-Options Tests', () => { }); it('returns SAMEORIGIN', () => { - const fortifiedHeaders = fortifyHeaders({ - xFrameOptions: { - sameorigin: true, + const fortifiedHeaders = fortifyHeaders( + { + xFrameOptions: { + sameorigin: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Frame-Options': 'SAMEORIGIN', @@ -24,11 +30,14 @@ describe('X-Frame-Options Tests', () => { }); it('returns DENY', () => { - const fortifiedHeaders = fortifyHeaders({ - xFrameOptions: { - deny: true, + const fortifiedHeaders = fortifyHeaders( + { + xFrameOptions: { + deny: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Frame-Options': 'DENY', @@ -36,11 +45,14 @@ describe('X-Frame-Options Tests', () => { }); it('returns DENY', () => { - const fortifiedHeaders = fortifyHeaders({ - xFrameOptions: { - deny: true, + const fortifiedHeaders = fortifyHeaders( + { + xFrameOptions: { + deny: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Frame-Options': 'DENY', @@ -49,12 +61,15 @@ describe('X-Frame-Options Tests', () => { it('enforces single-selection', () => { expect(() => - fortifyHeaders({ - xFrameOptions: { - deny: true, - sameorigin: true, + fortifyHeaders( + { + xFrameOptions: { + deny: true, + sameorigin: true, + }, }, - }), + { useDefaults: false }, + ), ).toThrowErrorMatchingInlineSnapshot( `"X-Frame-Options only allows one selection. You can only specify one option for this header."`, ); diff --git a/src/__tests__/headers/x-permitted-cross-domain-policies.ts b/src/__tests__/headers/x-permitted-cross-domain-policies.ts index c9fdca76..d56af665 100644 --- a/src/__tests__/headers/x-permitted-cross-domain-policies.ts +++ b/src/__tests__/headers/x-permitted-cross-domain-policies.ts @@ -2,9 +2,12 @@ import { fortifyHeaders } from '../..'; describe('X-Permitted-Cross-Domain-Policies Tests', () => { it('returns defaults for X-Permitted-Cross-Domain-Policies when nothing is specified', () => { - const fortifiedHeaders = fortifyHeaders({ - xPermittedCrossDomainPolicies: {}, - }); + const fortifiedHeaders = fortifyHeaders( + { + xPermittedCrossDomainPolicies: {}, + }, + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Permitted-Cross-Domain-Policies': 'none', @@ -12,11 +15,14 @@ describe('X-Permitted-Cross-Domain-Policies Tests', () => { }); it('returns none', () => { - const fortifiedHeaders = fortifyHeaders({ - xPermittedCrossDomainPolicies: { - none: true, + const fortifiedHeaders = fortifyHeaders( + { + xPermittedCrossDomainPolicies: { + none: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Permitted-Cross-Domain-Policies': 'none', @@ -24,11 +30,14 @@ describe('X-Permitted-Cross-Domain-Policies Tests', () => { }); it('returns master-only', () => { - const fortifiedHeaders = fortifyHeaders({ - xPermittedCrossDomainPolicies: { - masterOnly: true, + const fortifiedHeaders = fortifyHeaders( + { + xPermittedCrossDomainPolicies: { + masterOnly: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Permitted-Cross-Domain-Policies': 'master-only', @@ -36,11 +45,14 @@ describe('X-Permitted-Cross-Domain-Policies Tests', () => { }); it('returns by-content-type', () => { - const fortifiedHeaders = fortifyHeaders({ - xPermittedCrossDomainPolicies: { - byContentType: true, + const fortifiedHeaders = fortifyHeaders( + { + xPermittedCrossDomainPolicies: { + byContentType: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Permitted-Cross-Domain-Policies': 'by-content-type', @@ -48,11 +60,14 @@ describe('X-Permitted-Cross-Domain-Policies Tests', () => { }); it('returns all', () => { - const fortifiedHeaders = fortifyHeaders({ - xPermittedCrossDomainPolicies: { - all: true, + const fortifiedHeaders = fortifyHeaders( + { + xPermittedCrossDomainPolicies: { + all: true, + }, }, - }); + { useDefaults: false }, + ); expect(fortifiedHeaders).toEqual({ 'X-Permitted-Cross-Domain-Policies': 'all', @@ -61,12 +76,15 @@ describe('X-Permitted-Cross-Domain-Policies Tests', () => { it('enforces single-selection', () => { expect(() => - fortifyHeaders({ - xPermittedCrossDomainPolicies: { - masterOnly: true, - all: true, + fortifyHeaders( + { + xPermittedCrossDomainPolicies: { + masterOnly: true, + all: true, + }, }, - }), + { useDefaults: false }, + ), ).toThrowErrorMatchingInlineSnapshot( `"X-Permitted-Cross-Domain-Policies only allows one selection. You can only specify one option for this header."`, ); diff --git a/src/__tests__/index.ts b/src/__tests__/index.ts index 0c720845..ab07ec17 100644 --- a/src/__tests__/index.ts +++ b/src/__tests__/index.ts @@ -3,7 +3,7 @@ import { fortifyHeaders } from '..'; describe('fortify-core entrypoint tests', () => { describe('default state', () => { it('gets all defaults when initialized to an empty config', () => { - const fortifiedHeaders = fortifyHeaders({}); + const fortifiedHeaders = fortifyHeaders({}, { useDefaults: true }); expect(fortifiedHeaders).toEqual({ 'Content-Security-Policy': @@ -42,13 +42,49 @@ describe('fortify-core entrypoint tests', () => { 'Strict-Transport-Security': 'includeSubDomains; max-age=6000000', }); }); + + // default posture + it('opts-out when user specifies false', () => { + const fortifiedHeaders = fortifyHeaders( + { + contentSecurityPolicy: false, // false will opt-out of the header default + crossOriginEmbedderPolicy: false, // and not add it to the final result + crossOriginResourcePolicy: false, // even though we are using defaults + crossOriginOpenerPolicy: { + unsafeNone: true, + }, + strictTransportSecurity: { + includeSubDomains: true, + maxAge: 6000000, + }, + expectCt: false, + originAgentCluster: false, + }, + { useDefaults: true }, + ); + + expect(fortifiedHeaders).toEqual({ + 'Cross-Origin-Opener-Policy': 'unsafe-none', + 'Strict-Transport-Security': 'includeSubDomains; max-age=6000000', + 'Referrer-Policy': 'no-referrer', + 'X-Content-Type-Options': 'nosniff', + 'X-Dns-Prefetch-Control': 'off', + 'X-Download-Options': 'noopen', + 'X-Frame-Options': 'SAMEORIGIN', + 'X-Permitted-Cross-Domain-Policies': 'none', + }); + }); }); describe('failed-states', () => { it('throws when a property is on config that does not map to a header', () => { // potential untyped JavaScript consumption expect(() => - fortifyHeaders({ unknownHeader: ['SAMEORIGIN'] } as any), + console.log( + fortifyHeaders({ unknownHeader: ['SAMEORIGIN'] } as any, { + useDefaults: false, + }), + ), ).toThrowErrorMatchingInlineSnapshot( `"unknownHeader is not a supported header"`, ); diff --git a/src/fortifyHeaders.ts b/src/fortifyHeaders.ts index f1c8c7f6..0aff68a2 100644 --- a/src/fortifyHeaders.ts +++ b/src/fortifyHeaders.ts @@ -1,4 +1,4 @@ -import { FortifyHeaders, FortifySettings } from './types'; +import { FortifyHeaders, FortifySettings, GenerationOptions } from './types'; import { toHeaderCasing } from './directives/normalize'; import { getAllHeaders } from './headers'; import { HeaderFunction } from './headers/types'; @@ -11,34 +11,47 @@ function getConfig( availableHeaders: Record, config: FortifySettings, ): FortifySettings { - if (Object.keys(config).length === 0) { - const defaults: Record = {}; - const headerKeys = Object.keys(availableHeaders); - headerKeys.forEach((keyName) => { - defaults[keyName] = {}; - }); - return defaults; - } - return config; + return Object.keys(availableHeaders).reduce( + (acc: FortifySettings, cur: string) => { + const configValue = config[cur]; + if (typeof configValue === 'undefined' || configValue === null) { + acc[cur] = {}; + } else { + acc[cur] = config[cur]; + } + return acc; + }, + {}, + ); } /** * The primary entrypoint for generating HTTP security headers */ -export function fortifyHeaders(config: FortifySettings): FortifyHeaders { +export function fortifyHeaders( + settings: FortifySettings, + options: GenerationOptions = { useDefaults: false }, +): FortifyHeaders { const availableHeaders = getAllHeaders(); - const headerConfig = getConfig(availableHeaders, config); - const result = Object.entries(headerConfig).map( - ([directiveName, directiveValues]) => { - const headerName = toHeaderCasing(directiveName); - const headerFactory: HeaderFunction = availableHeaders[directiveName]; + const headerConfig = options.useDefaults + ? getConfig(availableHeaders, settings) + : settings; + return Object.keys(headerConfig).reduce( + (acc: FortifyHeaders, cur) => { + const directiveValues = headerConfig[cur]; + if (directiveValues === false) { + return acc; + } + + const headerName = toHeaderCasing(cur); + const headerFactory: HeaderFunction = availableHeaders[cur]; if (!headerFactory) { - throw new Error(`${directiveName} is not a supported header`); + throw new Error(`${cur} is not a supported header`); } const headerResult = headerFactory(directiveValues); - return [headerName, headerResult[headerName]]; + acc[headerName] = headerResult[headerName]; + return acc; }, + {}, ); - - return Object.fromEntries(result); } diff --git a/src/headers/cross-origin-embedder-policy/index.ts b/src/headers/cross-origin-embedder-policy/index.ts index 940de51b..64519530 100644 --- a/src/headers/cross-origin-embedder-policy/index.ts +++ b/src/headers/cross-origin-embedder-policy/index.ts @@ -5,7 +5,7 @@ import type { CrossOriginEmbedderPolicy } from './types'; const HEADER_NAME = 'Cross-Origin-Embedder-Policy'; const validation = directiveValidation(HEADER_NAME, { - allowedDirectives: ['require-corp'], + allowedDirectives: ['require-corp', 'unsafe-none', 'credentialless'], }); /** diff --git a/src/headers/cross-origin-embedder-policy/types.ts b/src/headers/cross-origin-embedder-policy/types.ts index e939ef14..a1d90ffd 100644 --- a/src/headers/cross-origin-embedder-policy/types.ts +++ b/src/headers/cross-origin-embedder-policy/types.ts @@ -13,4 +13,8 @@ export interface CrossOriginEmbedderPolicy extends FortifyHeader { * Allows the document to fetch cross-origin resources without giving explicit permission through the CORS protocol or the Cross-Origin-Resource-Policy header. */ unsafeNone?: boolean; + /** + * Test for google + */ + credentialless?: boolean; } diff --git a/src/types.ts b/src/types.ts index 39415d6d..18645c2b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,76 +15,86 @@ import { XPermittedCrossDomainPolicies } from './headers/x-permitted-cross-domai /** * Represents the primary configuration for FortifyJS */ -export type FortifySettings = { +export type FortifySettings = { [key: string]: object | boolean } & { /** * Configuration for Content-Security-Policy */ - contentSecurityPolicy?: ContentSecurityPolicy; + contentSecurityPolicy?: ContentSecurityPolicy | boolean; /** * Configuration for Cross-Origin-Embedder-Policy */ - crossOriginEmbedderPolicy?: CrossOriginEmbedderPolicy; + crossOriginEmbedderPolicy?: CrossOriginEmbedderPolicy | boolean; /** * Configuration for Cross-Origin-Opener-Policy */ - crossOriginOpenerPolicy?: CrossOriginOpenerPolicy; + crossOriginOpenerPolicy?: CrossOriginOpenerPolicy | boolean; /** * Configuration for Cross-Origin-Resource-Policy */ - crossOriginResourcePolicy?: CrossOriginResourcePolicy; + crossOriginResourcePolicy?: CrossOriginResourcePolicy | boolean; /** * Configuration for Expect-CT */ - expectCt?: ExpectCt; + expectCt?: ExpectCt | boolean; /** * Configuration for Origin-Agent-Cluster */ - originAgentCluster?: OriginAgentCluster; + originAgentCluster?: OriginAgentCluster | boolean; /** * Configuration for Referrer-Policy */ - referrerPolicy?: ReferrerPolicy; + referrerPolicy?: ReferrerPolicy | boolean; /** * Configuration for Strict-Transport-Security */ - strictTransportSecurity?: StrictTransportSecurity; + strictTransportSecurity?: StrictTransportSecurity | boolean; /** * Configuration for X-Content-Type-Options */ - xContentTypeOptions?: XContentTypeOotions; + xContentTypeOptions?: XContentTypeOotions | boolean; /** * Configuration for X-DNS-Prefetch-Control */ - xDnsPrefetchControl?: XDnsPrefetchControl; + xDnsPrefetchControl?: XDnsPrefetchControl | boolean; /** * Configuration for X-Download-Options */ - xDownloadOptions?: XDownloadOptions; + xDownloadOptions?: XDownloadOptions | boolean; /** * Configuration for X-Frame-Options */ - xFrameOptions?: XFrameOptions; + xFrameOptions?: XFrameOptions | boolean; /** * Configuration for X-Permitted-Cross-Domain-Policies */ - xPermittedCrossDomainPolicies?: XPermittedCrossDomainPolicies; + xPermittedCrossDomainPolicies?: XPermittedCrossDomainPolicies | boolean; +}; + +/** + * Represents higher-order options for header config generation + */ +export type GenerationOptions = { + /** + * Directs FortifyJS to opt in or out of defaults + */ + useDefaults?: boolean; }; /** * Represents the final security header configuration returned by FortifyJS */ -export type FortifyHeaders = { - 'Content-Security-Policy': string; - 'Cross-Origin-Embedder-Policy': string; - 'Cross-Origin-Opener-Policy': string; - 'Cross-Origin-Resource-Policy': string; - 'Expect-Ct': string; - 'Origin-Agent-Cluster': string; - 'Referrer-Policy': string; - 'Strict-Transport-Policy': string; - 'X-Content-Type-Options': string; - 'X-Dns-Prefetch-Control': string; - 'X-Download-Options': string; - 'X-Frame-Options': string; - 'X-Permitted-Cross-Domain-Policies': string; +export type FortifyHeaders = { [key: string]: string } & { + 'Content-Security-Policy'?: string; + 'Cross-Origin-Embedder-Policy'?: string; + 'Cross-Origin-Opener-Policy'?: string; + 'Cross-Origin-Resource-Policy'?: string; + 'Expect-Ct'?: string; + 'Origin-Agent-Cluster'?: string; + 'Referrer-Policy'?: string; + 'Strict-Transport-Policy'?: string; + 'X-Content-Type-Options'?: string; + 'X-Dns-Prefetch-Control'?: string; + 'X-Download-Options'?: string; + 'X-Frame-Options'?: string; + 'X-Permitted-Cross-Domain-Policies'?: string; };