mirror of
https://github.com/honojs/hono.git
synced 2024-11-21 18:18:57 +01:00
feat(secureHeader): add CSP Report-Only mode support (#3413)
This commit is contained in:
parent
fe0a82a615
commit
8130a67da1
@ -38,6 +38,7 @@ describe('Secure Headers Middleware', () => {
|
||||
expect(res.headers.get('Origin-Agent-Cluster')).toEqual('?1')
|
||||
expect(res.headers.get('Permissions-Policy')).toBeNull()
|
||||
expect(res.headers.get('Content-Security-Policy')).toBeFalsy()
|
||||
expect(res.headers.get('Content-Security-Policy-ReportOnly')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('all headers enabled', async () => {
|
||||
@ -48,6 +49,9 @@ describe('Secure Headers Middleware', () => {
|
||||
contentSecurityPolicy: {
|
||||
defaultSrc: ["'self'"],
|
||||
},
|
||||
contentSecurityPolicyReportOnly: {
|
||||
defaultSrc: ["'self'"],
|
||||
},
|
||||
crossOriginEmbedderPolicy: true,
|
||||
permissionsPolicy: {
|
||||
camera: [],
|
||||
@ -78,6 +82,7 @@ describe('Secure Headers Middleware', () => {
|
||||
expect(res.headers.get('Cross-Origin-Embedder-Policy')).toEqual('require-corp')
|
||||
expect(res.headers.get('Permissions-Policy')).toEqual('camera=()')
|
||||
expect(res.headers.get('Content-Security-Policy')).toEqual("default-src 'self'")
|
||||
expect(res.headers.get('Content-Security-Policy-Report-Only')).toEqual("default-src 'self'")
|
||||
})
|
||||
|
||||
it('specific headers disabled', async () => {
|
||||
@ -189,257 +194,6 @@ describe('Secure Headers Middleware', () => {
|
||||
'magnetometer=("https://a.example.com" "https://b.example.com")'
|
||||
)
|
||||
})
|
||||
it('CSP Setting', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
contentSecurityPolicy: {
|
||||
defaultSrc: ["'self'"],
|
||||
baseUri: ["'self'"],
|
||||
fontSrc: ["'self'", 'https:', 'data:'],
|
||||
frameAncestors: ["'self'"],
|
||||
imgSrc: ["'self'", 'data:'],
|
||||
objectSrc: ["'none'"],
|
||||
scriptSrc: ["'self'"],
|
||||
scriptSrcAttr: ["'none'"],
|
||||
styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
c.res.headers.set('Strict-Transport-Security', 'Hono')
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
expect(res.headers.get('Content-Security-Policy')).toEqual(
|
||||
"default-src 'self'; base-uri 'self'; font-src 'self' https: data:; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'"
|
||||
)
|
||||
})
|
||||
|
||||
it('CSP Setting one only', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
contentSecurityPolicy: {
|
||||
defaultSrc: ["'self'"],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
expect(res.headers.get('Content-Security-Policy')).toEqual("default-src 'self'")
|
||||
})
|
||||
|
||||
it('No CSP Setting', async () => {
|
||||
const app = new Hono()
|
||||
app.use('/test', secureHeaders({ contentSecurityPolicy: {} }))
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
expect(res.headers.get('Content-Security-Policy')).toEqual('')
|
||||
})
|
||||
|
||||
it('CSP with reportTo', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test1',
|
||||
secureHeaders({
|
||||
reportingEndpoints: [
|
||||
{
|
||||
name: 'endpoint-1',
|
||||
url: 'https://example.com/reports',
|
||||
},
|
||||
],
|
||||
contentSecurityPolicy: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'endpoint-1',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.use(
|
||||
'/test2',
|
||||
secureHeaders({
|
||||
reportTo: [
|
||||
{
|
||||
group: 'endpoint-1',
|
||||
max_age: 10886400,
|
||||
endpoints: [{ url: 'https://example.com/reports' }],
|
||||
},
|
||||
],
|
||||
contentSecurityPolicy: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'endpoint-1',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.use(
|
||||
'/test3',
|
||||
secureHeaders({
|
||||
reportTo: [
|
||||
{
|
||||
group: 'g1',
|
||||
max_age: 10886400,
|
||||
endpoints: [
|
||||
{ url: 'https://a.example.com/reports' },
|
||||
{ url: 'https://b.example.com/reports' },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'g2',
|
||||
max_age: 10886400,
|
||||
endpoints: [
|
||||
{ url: 'https://c.example.com/reports' },
|
||||
{ url: 'https://d.example.com/reports' },
|
||||
],
|
||||
},
|
||||
],
|
||||
contentSecurityPolicy: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'g2',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.use(
|
||||
'/test4',
|
||||
secureHeaders({
|
||||
reportingEndpoints: [
|
||||
{
|
||||
name: 'e1',
|
||||
url: 'https://a.example.com/reports',
|
||||
},
|
||||
{
|
||||
name: 'e2',
|
||||
url: 'https://b.example.com/reports',
|
||||
},
|
||||
],
|
||||
contentSecurityPolicy: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'e1',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res1 = await app.request('/test1')
|
||||
expect(res1.headers.get('Reporting-Endpoints')).toEqual(
|
||||
'endpoint-1="https://example.com/reports"'
|
||||
)
|
||||
expect(res1.headers.get('Content-Security-Policy')).toEqual(
|
||||
"default-src 'self'; report-to endpoint-1"
|
||||
)
|
||||
|
||||
const res2 = await app.request('/test2')
|
||||
expect(res2.headers.get('Report-To')).toEqual(
|
||||
'{"group":"endpoint-1","max_age":10886400,"endpoints":[{"url":"https://example.com/reports"}]}'
|
||||
)
|
||||
expect(res2.headers.get('Content-Security-Policy')).toEqual(
|
||||
"default-src 'self'; report-to endpoint-1"
|
||||
)
|
||||
|
||||
const res3 = await app.request('/test3')
|
||||
expect(res3.headers.get('Report-To')).toEqual(
|
||||
'{"group":"g1","max_age":10886400,"endpoints":[{"url":"https://a.example.com/reports"},{"url":"https://b.example.com/reports"}]}, {"group":"g2","max_age":10886400,"endpoints":[{"url":"https://c.example.com/reports"},{"url":"https://d.example.com/reports"}]}'
|
||||
)
|
||||
expect(res3.headers.get('Content-Security-Policy')).toEqual("default-src 'self'; report-to g2")
|
||||
|
||||
const res4 = await app.request('/test4')
|
||||
expect(res4.headers.get('Reporting-Endpoints')).toEqual(
|
||||
'e1="https://a.example.com/reports", e2="https://b.example.com/reports"'
|
||||
)
|
||||
expect(res4.headers.get('Content-Security-Policy')).toEqual("default-src 'self'; report-to e1")
|
||||
})
|
||||
|
||||
it('CSP nonce for script-src', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
contentSecurityPolicy: {
|
||||
scriptSrc: ["'self'", NONCE],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text(`nonce: ${c.get('secureHeadersNonce')}`)
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
const csp = res.headers.get('Content-Security-Policy')
|
||||
const nonce = csp?.match(/script-src 'self' 'nonce-([a-zA-Z0-9+/]+=*)'/)?.[1] || ''
|
||||
expect(csp).toMatch(`script-src 'self' 'nonce-${nonce}'`)
|
||||
expect(await res.text()).toEqual(`nonce: ${nonce}`)
|
||||
})
|
||||
|
||||
it('CSP nonce for script-src and style-src', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
contentSecurityPolicy: {
|
||||
scriptSrc: ["'self'", NONCE],
|
||||
styleSrc: ["'self'", NONCE],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text(`nonce: ${c.get('secureHeadersNonce')}`)
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
const csp = res.headers.get('Content-Security-Policy')
|
||||
const nonce = csp?.match(/script-src 'self' 'nonce-([a-zA-Z0-9+/]+=*)'/)?.[1] || ''
|
||||
expect(csp).toMatch(`script-src 'self' 'nonce-${nonce}'`)
|
||||
expect(csp).toMatch(`style-src 'self' 'nonce-${nonce}'`)
|
||||
expect(await res.text()).toEqual(`nonce: ${nonce}`)
|
||||
})
|
||||
|
||||
it('CSP nonce by app own function', async () => {
|
||||
const app = new Hono()
|
||||
const setNonce: ContentSecurityPolicyOptionHandler = (ctx, directive) => {
|
||||
ctx.set(`test-${directive}-nonce`, directive)
|
||||
return `'nonce-${directive}'`
|
||||
}
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
contentSecurityPolicy: {
|
||||
scriptSrc: ["'self'", setNonce],
|
||||
styleSrc: ["'self'", setNonce],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text(
|
||||
`script: ${c.get('test-scriptSrc-nonce')}, style: ${c.get('test-styleSrc-nonce')}`
|
||||
)
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
const csp = res.headers.get('Content-Security-Policy')
|
||||
expect(csp).toMatch(`script-src 'self' 'nonce-scriptSrc'`)
|
||||
expect(csp).toMatch(`style-src 'self' 'nonce-styleSrc'`)
|
||||
expect(await res.text()).toEqual('script: scriptSrc, style: styleSrc')
|
||||
})
|
||||
|
||||
it('Remove X-Powered-By', async () => {
|
||||
const app = new Hono()
|
||||
@ -469,4 +223,260 @@ describe('Secure Headers Middleware', () => {
|
||||
expect(poweredby2).toEqual('Hono')
|
||||
expect(await res2.text()).toEqual('Hono is cool')
|
||||
})
|
||||
|
||||
describe.each([
|
||||
{ cspSettingName: 'contentSecurityPolicy', cspHeaderName: 'Content-Security-Policy' },
|
||||
{
|
||||
cspSettingName: 'contentSecurityPolicyReportOnly',
|
||||
cspHeaderName: 'Content-Security-Policy-Report-Only',
|
||||
},
|
||||
])('CSP Setting ($cspSettingName)', ({ cspSettingName, cspHeaderName }) => {
|
||||
it('CSP Setting', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
[cspSettingName]: {
|
||||
defaultSrc: ["'self'"],
|
||||
baseUri: ["'self'"],
|
||||
fontSrc: ["'self'", 'https:', 'data:'],
|
||||
frameAncestors: ["'self'"],
|
||||
imgSrc: ["'self'", 'data:'],
|
||||
objectSrc: ["'none'"],
|
||||
scriptSrc: ["'self'"],
|
||||
scriptSrcAttr: ["'none'"],
|
||||
styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
c.res.headers.set('Strict-Transport-Security', 'Hono')
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
expect(res.headers.get(cspHeaderName)).toEqual(
|
||||
"default-src 'self'; base-uri 'self'; font-src 'self' https: data:; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'"
|
||||
)
|
||||
})
|
||||
|
||||
it('CSP Setting one only', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
[cspSettingName]: {
|
||||
defaultSrc: ["'self'"],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
expect(res.headers.get(cspHeaderName)).toEqual("default-src 'self'")
|
||||
})
|
||||
|
||||
it('No CSP Setting', async () => {
|
||||
const app = new Hono()
|
||||
app.use('/test', secureHeaders({ [cspSettingName]: {} }))
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
expect(res.headers.get(cspHeaderName)).toEqual('')
|
||||
})
|
||||
|
||||
it('CSP with reportTo', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test1',
|
||||
secureHeaders({
|
||||
reportingEndpoints: [
|
||||
{
|
||||
name: 'endpoint-1',
|
||||
url: 'https://example.com/reports',
|
||||
},
|
||||
],
|
||||
[cspSettingName]: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'endpoint-1',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.use(
|
||||
'/test2',
|
||||
secureHeaders({
|
||||
reportTo: [
|
||||
{
|
||||
group: 'endpoint-1',
|
||||
max_age: 10886400,
|
||||
endpoints: [{ url: 'https://example.com/reports' }],
|
||||
},
|
||||
],
|
||||
[cspSettingName]: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'endpoint-1',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.use(
|
||||
'/test3',
|
||||
secureHeaders({
|
||||
reportTo: [
|
||||
{
|
||||
group: 'g1',
|
||||
max_age: 10886400,
|
||||
endpoints: [
|
||||
{ url: 'https://a.example.com/reports' },
|
||||
{ url: 'https://b.example.com/reports' },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'g2',
|
||||
max_age: 10886400,
|
||||
endpoints: [
|
||||
{ url: 'https://c.example.com/reports' },
|
||||
{ url: 'https://d.example.com/reports' },
|
||||
],
|
||||
},
|
||||
],
|
||||
[cspSettingName]: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'g2',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.use(
|
||||
'/test4',
|
||||
secureHeaders({
|
||||
reportingEndpoints: [
|
||||
{
|
||||
name: 'e1',
|
||||
url: 'https://a.example.com/reports',
|
||||
},
|
||||
{
|
||||
name: 'e2',
|
||||
url: 'https://b.example.com/reports',
|
||||
},
|
||||
],
|
||||
[cspSettingName]: {
|
||||
defaultSrc: ["'self'"],
|
||||
reportTo: 'e1',
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text('header updated')
|
||||
})
|
||||
|
||||
const res1 = await app.request('/test1')
|
||||
expect(res1.headers.get('Reporting-Endpoints')).toEqual(
|
||||
'endpoint-1="https://example.com/reports"'
|
||||
)
|
||||
expect(res1.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to endpoint-1")
|
||||
|
||||
const res2 = await app.request('/test2')
|
||||
expect(res2.headers.get('Report-To')).toEqual(
|
||||
'{"group":"endpoint-1","max_age":10886400,"endpoints":[{"url":"https://example.com/reports"}]}'
|
||||
)
|
||||
expect(res2.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to endpoint-1")
|
||||
|
||||
const res3 = await app.request('/test3')
|
||||
expect(res3.headers.get('Report-To')).toEqual(
|
||||
'{"group":"g1","max_age":10886400,"endpoints":[{"url":"https://a.example.com/reports"},{"url":"https://b.example.com/reports"}]}, {"group":"g2","max_age":10886400,"endpoints":[{"url":"https://c.example.com/reports"},{"url":"https://d.example.com/reports"}]}'
|
||||
)
|
||||
expect(res3.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to g2")
|
||||
|
||||
const res4 = await app.request('/test4')
|
||||
expect(res4.headers.get('Reporting-Endpoints')).toEqual(
|
||||
'e1="https://a.example.com/reports", e2="https://b.example.com/reports"'
|
||||
)
|
||||
expect(res4.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to e1")
|
||||
})
|
||||
|
||||
it('CSP nonce for script-src', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
[cspSettingName]: {
|
||||
scriptSrc: ["'self'", NONCE],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text(`nonce: ${c.get('secureHeadersNonce')}`)
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
const csp = res.headers.get(cspHeaderName)
|
||||
const nonce = csp?.match(/script-src 'self' 'nonce-([a-zA-Z0-9+/]+=*)'/)?.[1] || ''
|
||||
expect(csp).toMatch(`script-src 'self' 'nonce-${nonce}'`)
|
||||
expect(await res.text()).toEqual(`nonce: ${nonce}`)
|
||||
})
|
||||
|
||||
it('CSP nonce for script-src and style-src', async () => {
|
||||
const app = new Hono()
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
[cspSettingName]: {
|
||||
scriptSrc: ["'self'", NONCE],
|
||||
styleSrc: ["'self'", NONCE],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text(`nonce: ${c.get('secureHeadersNonce')}`)
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
const csp = res.headers.get(cspHeaderName)
|
||||
const nonce = csp?.match(/script-src 'self' 'nonce-([a-zA-Z0-9+/]+=*)'/)?.[1] || ''
|
||||
expect(csp).toMatch(`script-src 'self' 'nonce-${nonce}'`)
|
||||
expect(csp).toMatch(`style-src 'self' 'nonce-${nonce}'`)
|
||||
expect(await res.text()).toEqual(`nonce: ${nonce}`)
|
||||
})
|
||||
|
||||
it('CSP nonce by app own function', async () => {
|
||||
const app = new Hono()
|
||||
const setNonce: ContentSecurityPolicyOptionHandler = (ctx, directive) => {
|
||||
ctx.set(`test-${directive}-nonce`, directive)
|
||||
return `'nonce-${directive}'`
|
||||
}
|
||||
app.use(
|
||||
'/test',
|
||||
secureHeaders({
|
||||
[cspSettingName]: {
|
||||
scriptSrc: ["'self'", setNonce],
|
||||
styleSrc: ["'self'", setNonce],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
app.all('*', async (c) => {
|
||||
return c.text(
|
||||
`script: ${c.get('test-scriptSrc-nonce')}, style: ${c.get('test-styleSrc-nonce')}`
|
||||
)
|
||||
})
|
||||
|
||||
const res = await app.request('/test')
|
||||
const csp = res.headers.get(cspHeaderName)
|
||||
expect(csp).toMatch(`script-src 'self' 'nonce-scriptSrc'`)
|
||||
expect(csp).toMatch(`style-src 'self' 'nonce-styleSrc'`)
|
||||
expect(await res.text()).toEqual('script: scriptSrc, style: styleSrc')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -65,6 +65,7 @@ type overridableHeader = boolean | string
|
||||
|
||||
interface SecureHeadersOptions {
|
||||
contentSecurityPolicy?: ContentSecurityPolicyOptions
|
||||
contentSecurityPolicyReportOnly?: ContentSecurityPolicyOptions
|
||||
crossOriginEmbedderPolicy?: overridableHeader
|
||||
crossOriginResourcePolicy?: overridableHeader
|
||||
crossOriginOpenerPolicy?: overridableHeader
|
||||
@ -148,6 +149,7 @@ export const NONCE: ContentSecurityPolicyOptionHandler = (ctx) => {
|
||||
*
|
||||
* @param {Partial<SecureHeadersOptions>} [customOptions] - The options for the secure headers middleware.
|
||||
* @param {ContentSecurityPolicyOptions} [customOptions.contentSecurityPolicy] - Settings for the Content-Security-Policy header.
|
||||
* @param {ContentSecurityPolicyOptions} [customOptions.contentSecurityPolicyReportOnly] - Settings for the Content-Security-Policy-Report-Only header.
|
||||
* @param {overridableHeader} [customOptions.crossOriginEmbedderPolicy=false] - Settings for the Cross-Origin-Embedder-Policy header.
|
||||
* @param {overridableHeader} [customOptions.crossOriginResourcePolicy=true] - Settings for the Cross-Origin-Resource-Policy header.
|
||||
* @param {overridableHeader} [customOptions.crossOriginOpenerPolicy=true] - Settings for the Cross-Origin-Opener-Policy header.
|
||||
@ -185,6 +187,14 @@ export const secureHeaders = (customOptions?: SecureHeadersOptions): MiddlewareH
|
||||
headersToSet.push(['Content-Security-Policy', value as string])
|
||||
}
|
||||
|
||||
if (options.contentSecurityPolicyReportOnly) {
|
||||
const [callback, value] = getCSPDirectives(options.contentSecurityPolicyReportOnly)
|
||||
if (callback) {
|
||||
callbacks.push(callback)
|
||||
}
|
||||
headersToSet.push(['Content-Security-Policy-Report-Only', value as string])
|
||||
}
|
||||
|
||||
if (options.permissionsPolicy && Object.keys(options.permissionsPolicy).length > 0) {
|
||||
headersToSet.push([
|
||||
'Permissions-Policy',
|
||||
@ -258,7 +268,10 @@ function getCSPDirectives(
|
||||
: [
|
||||
(ctx, headersToSet) =>
|
||||
headersToSet.map((values) => {
|
||||
if (values[0] === 'Content-Security-Policy') {
|
||||
if (
|
||||
values[0] === 'Content-Security-Policy' ||
|
||||
values[0] === 'Content-Security-Policy-Report-Only'
|
||||
) {
|
||||
const clone = values[1].slice() as unknown as string[]
|
||||
callbacks.forEach((cb) => {
|
||||
cb(ctx, clone)
|
||||
|
Loading…
Reference in New Issue
Block a user