From 8add1b760126de9d9395a5272706d0e4023b1526 Mon Sep 17 00:00:00 2001 From: Wabu-K Date: Mon, 9 Sep 2024 14:19:56 +0900 Subject: [PATCH] refactor(secure-headers): optimize getPermissionsPolicyDirectives function --- src/middleware/secure-headers/index.test.ts | 49 +++++++++++-------- .../secure-headers/secure-headers.ts | 12 +++-- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/middleware/secure-headers/index.test.ts b/src/middleware/secure-headers/index.test.ts index 2f3d9ad5a..1b3d93c6d 100644 --- a/src/middleware/secure-headers/index.test.ts +++ b/src/middleware/secure-headers/index.test.ts @@ -160,27 +160,36 @@ describe('Secure Headers Middleware', () => { expect(res.headers.get('X-XSS-Protection')).toEqual('1') }) - it('should set Permission-Policy header', async () => { - const app = new Hono() - app.use( - '/test', - secureHeaders({ - permissionsPolicy: { - fullscreen: ['self'], - bluetooth: ['none'], - payment: ['self', 'example.com'], - syncXhr: [], - camera: false, - microphone: true, - geolocation: ['*'], - }, - }) - ) + describe('secureHeaders middleware', () => { + it('should set Permission-Policy header', async () => { + const app = new Hono() + app.use( + '/test', + secureHeaders({ + permissionsPolicy: { + fullscreen: ['self'], + bluetooth: ['none'], + payment: ['self', 'https://example.com'], + syncXhr: [], + camera: false, + microphone: true, + geolocation: ['*'], + usb: ['self', 'https://a.example.com', 'https://b.example.com'], + accelerometer: ['https://*.example.com'], + gyroscope: ['src'], + magnetometer: ['https://a.example.com', 'https://b.example.com'], + }, + }) + ) - const res = await app.request('/test') - expect(res.headers.get('Permissions-Policy')).toEqual( - 'fullscreen=(self), bluetooth=(none), payment=(self example.com), sync-xhr=(), camera=none, microphone=(), geolocation=(*)' - ) + const res = await app.request('/test') + expect(res.headers.get('Permissions-Policy')).toEqual( + 'fullscreen=(self), bluetooth=(none), payment=(self "https://example.com"), sync-xhr=(), camera=none, microphone=*, ' + + 'geolocation=(*), usb=(self "https://a.example.com" "https://b.example.com"), ' + + 'accelerometer=("https://*.example.com"), gyroscope=(src), ' + + 'magnetometer=("https://a.example.com" "https://b.example.com")' + ) + }) }) it('CSP Setting', async () => { const app = new Hono() diff --git a/src/middleware/secure-headers/secure-headers.ts b/src/middleware/secure-headers/secure-headers.ts index 902b45f7a..63e5f50ff 100644 --- a/src/middleware/secure-headers/secure-headers.ts +++ b/src/middleware/secure-headers/secure-headers.ts @@ -278,12 +278,18 @@ function getPermissionsPolicyDirectives(policy: PermissionsPolicyOptions): strin const kebabDirective = camelToKebab(directive) if (typeof value === 'boolean') { - return `${kebabDirective}=${value ? '()' : 'none'}` + return `${kebabDirective}=${value ? '*' : 'none'}` } if (Array.isArray(value)) { - const allowlist = value.length === 0 ? '()' : `(${value.join(' ')})` - return `${kebabDirective}=${allowlist}` + const allowlist = value.map((item) => { + if (item === 'self' || item === 'src' || item === 'none' || item === '*') { + return item + } + return `"${item}"` + }) + const allowlistString = allowlist.length === 0 ? '()' : `(${allowlist.join(' ')})` + return `${kebabDirective}=${allowlistString}` } return ''