diff --git a/packages/service-worker/worker/src/assets.ts b/packages/service-worker/worker/src/assets.ts index cec901050b97..82b19de9433a 100644 --- a/packages/service-worker/worker/src/assets.ts +++ b/packages/service-worker/worker/src/assets.ts @@ -501,10 +501,10 @@ export abstract class AssetGroup { * Create a new `Request` based on the specified URL and `RequestInit` options, preserving only * metadata that are known to be safe. * - * Currently, headers, redirect policy, an explicit `credentials: 'omit'`, and the HTTP cache - * mode are preserved. On cross-origin redirects, sensitive headers are removed. This includes - * `Authorization`, as required by the Fetch redirect algorithm, and forbidden request headers - * that could contain credentials. + * Currently, headers, referrer, referrer policy, redirect policy, an explicit + * `credentials: 'omit'`, and the HTTP cache mode are preserved. On cross-origin redirects, + * sensitive headers are removed. This includes `Authorization`, as required by the Fetch redirect + * algorithm, and forbidden request headers that could contain credentials. * * NOTE: * `credentials: 'same-origin'` and `credentials: 'include'` are intentionally not preserved. @@ -532,6 +532,8 @@ export abstract class AssetGroup { const init: RequestInit = { headers, + referrer: options.referrer, + referrerPolicy: options.referrerPolicy, redirect: options.redirect, }; diff --git a/packages/service-worker/worker/test/happy_spec.ts b/packages/service-worker/worker/test/happy_spec.ts index 2af08b1b8be5..9d2b6b480c40 100644 --- a/packages/service-worker/worker/test/happy_spec.ts +++ b/packages/service-worker/worker/test/happy_spec.ts @@ -1672,6 +1672,30 @@ import {envIsSupported} from '../testing/utils'; expect(bazReq.credentials).toBe('omit'); }); + it(`passes 'referrer' through to the server`, async () => { + const reqInit = {referrer: 'http://localhost/profile?token=secret'}; + expect(await makeRequest(scope, '/baz.txt', undefined, reqInit)).toBe('this is baz'); + + const [bazReq] = server.getRequestsFor('/baz.txt'); + expect(bazReq.referrer).toBe('http://localhost/profile?token=secret'); + }); + + it(`passes 'referrer: ""' through to the server`, async () => { + const reqInit = {referrer: ''}; + expect(await makeRequest(scope, '/baz.txt', undefined, reqInit)).toBe('this is baz'); + + const [bazReq] = server.getRequestsFor('/baz.txt'); + expect(bazReq.referrer).toBe(''); + }); + + it(`passes 'referrerPolicy' through to the server`, async () => { + const reqInit = {referrerPolicy: 'no-referrer'}; + expect(await makeRequest(scope, '/baz.txt', undefined, reqInit)).toBe('this is baz'); + + const [bazReq] = server.getRequestsFor('/baz.txt'); + expect(bazReq.referrerPolicy).toBe('no-referrer'); + }); + it(`passes 'cache' through to the server`, async () => { // Request a lazy-cached asset (so that it is fetched from the network) and provide an // explicit HTTP cache mode. @@ -1765,6 +1789,36 @@ import {envIsSupported} from '../testing/utils'; expect(redirectReq.credentials).toBe('omit'); }); + it(`passes 'referrer' through to the server`, async () => { + const reqInit = {referrer: 'http://localhost/profile?token=secret'}; + expect(await makeRequest(scope, '/lazy/redirected.txt', undefined, reqInit)).toBe( + 'this was a redirect too', + ); + + const [redirectReq] = server.getRequestsFor('/lazy/redirect-target.txt'); + expect(redirectReq.referrer).toBe('http://localhost/profile?token=secret'); + }); + + it(`passes 'referrer: ""' through to the server`, async () => { + const reqInit = {referrer: ''}; + expect(await makeRequest(scope, '/lazy/redirected.txt', undefined, reqInit)).toBe( + 'this was a redirect too', + ); + + const [redirectReq] = server.getRequestsFor('/lazy/redirect-target.txt'); + expect(redirectReq.referrer).toBe(''); + }); + + it(`passes 'referrerPolicy' through to the server`, async () => { + const reqInit = {referrerPolicy: 'no-referrer'}; + expect(await makeRequest(scope, '/lazy/redirected.txt', undefined, reqInit)).toBe( + 'this was a redirect too', + ); + + const [redirectReq] = server.getRequestsFor('/lazy/redirect-target.txt'); + expect(redirectReq.referrerPolicy).toBe('no-referrer'); + }); + it(`passes 'cache' through to the server`, async () => { // Request a redirected, lazy-cached asset (so that it is fetched from the network) and // provide an explicit HTTP cache mode. diff --git a/packages/service-worker/worker/testing/fetch.ts b/packages/service-worker/worker/testing/fetch.ts index 3e5997ba744c..0e6be61f6758 100644 --- a/packages/service-worker/worker/testing/fetch.ts +++ b/packages/service-worker/worker/testing/fetch.ts @@ -130,8 +130,8 @@ export class MockRequest extends MockBody implements Request { readonly method: string = 'GET'; readonly mode: RequestMode = 'cors'; readonly redirect: RequestRedirect = 'follow'; - readonly referrer: string = ''; - readonly referrerPolicy: ReferrerPolicy = 'no-referrer'; + readonly referrer: string = 'about:client'; + readonly referrerPolicy: ReferrerPolicy = ''; readonly signal: AbortSignal = null as any; url: string; @@ -163,6 +163,12 @@ export class MockRequest extends MockBody implements Request { if (init.redirect !== undefined) { this.redirect = init.redirect; } + if (init.referrer !== undefined) { + this.referrer = init.referrer; + } + if (init.referrerPolicy !== undefined) { + this.referrerPolicy = init.referrerPolicy; + } if (init.destination !== undefined) { this.destination = init.destination; } @@ -179,6 +185,8 @@ export class MockRequest extends MockBody implements Request { credentials: this.credentials, headers: this.headers, redirect: this.redirect, + referrer: this.referrer, + referrerPolicy: this.referrerPolicy, }); } }