Skip to content

Commit 48214e2

Browse files
gkalpakAndrewKushnir
authored andcommitted
fix(service-worker): ignore passive mixed content requests (angular#25994)
Although [passive mixed content][1] requests (like images) only produce a warning without a ServiceWorker, fetching it via a ServiceWorker results in an error. See angular#23012 (comment) for more details. This commit makes the ServiceWorker ignore such requests and let them be handled by the browser directly to avoid breaking apps that would work without the ServiceWorker. [1]: https://developers.google.com/web/fundamentals/security/prevent-mixed-content/what-is-mixed-content#passive_mixed_content Fixes angular#23012 PR Close angular#25994
1 parent 95989a1 commit 48214e2

3 files changed

Lines changed: 48 additions & 7 deletions

File tree

packages/service-worker/worker/src/driver.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,12 @@ export class Driver implements Debuggable, UpdateSource {
176176
*/
177177
private onFetch(event: FetchEvent): void {
178178
const req = event.request;
179+
const scopeUrl = this.scope.registration.scope;
180+
const requestUrlObj = this.adapter.parseUrl(req.url, scopeUrl);
179181

180182
// The only thing that is served unconditionally is the debug page.
181-
if (this.adapter.parseUrl(req.url, this.scope.registration.scope).path === '/ngsw/state') {
182-
// Allow the debugger to handle the request, but don't affect SW state in any
183-
// other way.
183+
if (requestUrlObj.path === '/ngsw/state') {
184+
// Allow the debugger to handle the request, but don't affect SW state in any other way.
184185
event.respondWith(this.debugger.handleFetch(req));
185186
return;
186187
}
@@ -197,14 +198,24 @@ export class Driver implements Debuggable, UpdateSource {
197198
return;
198199
}
199200

201+
// Although "passive mixed content" (like images) only produces a warning without a
202+
// ServiceWorker, fetching it via a ServiceWorker results in an error. Let such requests be
203+
// handled by the browser, since handling with the ServiceWorker would fail anyway.
204+
// See https://github.com/angular/angular/issues/23012#issuecomment-376430187 for more details.
205+
if (requestUrlObj.origin.startsWith('http:') && scopeUrl.startsWith('https:')) {
206+
// Still, log the incident for debugging purposes.
207+
this.debugger.log(`Ignoring passive mixed content request: Driver.fetch(${req.url})`);
208+
return;
209+
}
210+
200211
// When opening DevTools in Chrome, a request is made for the current URL (and possibly related
201212
// resources, e.g. scripts) with `cache: 'only-if-cached'` and `mode: 'no-cors'`. These request
202213
// will eventually fail, because `only-if-cached` is only allowed to be used with
203214
// `mode: 'same-origin'`.
204215
// This is likely a bug in Chrome DevTools. Avoid handling such requests.
205216
// (See also https://github.com/angular/angular/issues/22362.)
206217
// TODO(gkalpak): Remove once no longer necessary (i.e. fixed in Chrome DevTools).
207-
if ((req.cache as string) === 'only-if-cached' && req.mode !== 'same-origin') {
218+
if (req.cache === 'only-if-cached' && req.mode !== 'same-origin') {
208219
// Log the incident only the first time it happens, to avoid spamming the logs.
209220
if (!this.loggedInvalidOnlyIfCachedRequest) {
210221
this.loggedInvalidOnlyIfCachedRequest = true;

packages/service-worker/worker/test/happy_spec.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,6 @@ const brokenServer =
204204

205205
const server404 = new MockServerStateBuilder().withStaticFiles(dist).build();
206206

207-
const scope = new SwTestHarnessBuilder().withServerState(server).build();
208-
209207
const manifestHash = sha1(JSON.stringify(manifest));
210208
const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
211209

@@ -1008,6 +1006,38 @@ const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
10081006
expect(await requestFoo('only-if-cached', 'no-cors')).toBeNull();
10091007
});
10101008

1009+
async_it('ignores passive mixed content requests ', async() => {
1010+
const scopeFetchSpy = spyOn(scope, 'fetch').and.callThrough();
1011+
const getRequestUrls = () => scopeFetchSpy.calls.allArgs().map(args => args[0].url);
1012+
1013+
const httpScopeUrl = 'http://mock.origin.dev';
1014+
const httpsScopeUrl = 'https://mock.origin.dev';
1015+
const httpRequestUrl = 'http://other.origin.sh/unknown.png';
1016+
const httpsRequestUrl = 'https://other.origin.sh/unknown.pnp';
1017+
1018+
// Registration scope: `http:`
1019+
(scope.registration.scope as string) = httpScopeUrl;
1020+
1021+
await makeRequest(scope, httpRequestUrl);
1022+
await makeRequest(scope, httpsRequestUrl);
1023+
const requestUrls1 = getRequestUrls();
1024+
1025+
expect(requestUrls1).toContain(httpRequestUrl);
1026+
expect(requestUrls1).toContain(httpsRequestUrl);
1027+
1028+
scopeFetchSpy.calls.reset();
1029+
1030+
// Registration scope: `https:`
1031+
(scope.registration.scope as string) = httpsScopeUrl;
1032+
1033+
await makeRequest(scope, httpRequestUrl);
1034+
await makeRequest(scope, httpsRequestUrl);
1035+
const requestUrls2 = getRequestUrls();
1036+
1037+
expect(requestUrls2).not.toContain(httpRequestUrl);
1038+
expect(requestUrls2).toContain(httpsRequestUrl);
1039+
});
1040+
10111041
describe('Backwards compatibility with v5', () => {
10121042
beforeEach(() => {
10131043
const serverV5 = new MockServerStateBuilder()

packages/service-worker/worker/testing/scope.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export class SwTestHarness implements ServiceWorkerGlobalScope, Adapter, Context
181181
return {origin: obj.origin, path: obj.pathname};
182182
} else {
183183
const obj = require('url').parse(url);
184-
return {origin: obj.origin, path: obj.pathname};
184+
return {origin: `${obj.protocol}//${obj.host}`, path: obj.pathname};
185185
}
186186
}
187187

0 commit comments

Comments
 (0)