Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/service-worker/worker/src/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ export class Driver implements Debuggable, UpdateSource {
);
this.scope.addEventListener('messageerror', (event) => this.onMessageError(event));
this.scope.addEventListener('unhandledrejection', (event) => this.onUnhandledRejection(event));
this.scope.addEventListener('securitypolicyviolation', (event) =>
// It seems that typescript has not registered the event as indicated in the documentation.
// Issue: https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/2123
this.onSecurityPolicyViolation(event as SecurityPolicyViolationEvent),
);

// The debugger generates debug pages in response to debugging requests.
this.debugger = new DebugHandler(this, this.adapter);
Expand Down Expand Up @@ -373,6 +378,15 @@ export class Driver implements Debuggable, UpdateSource {
);
}

private onSecurityPolicyViolation(event: SecurityPolicyViolationEvent): void {
// Handle security policy violations in the service worker.
// This is for debugging and preventing silent failures.
this.debugger.log(
`Security Policy Violation`,
`Driver.onSecurityPolicyViolation(reason: ${JSON.stringify(event)})`,
);
}

private async ensureInitialized(event: ExtendableEvent): Promise<void> {
// Since the SW may have just been started, it may or may not have been initialized already.
// `this.initialized` will be `null` if initialization has not yet been attempted, or will be a
Expand Down
21 changes: 21 additions & 0 deletions packages/service-worker/worker/test/happy_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,27 @@ import {envIsSupported} from '../testing/utils';
});
});

describe('securitypolicyviolation events', () => {
it('broadcasts security policy violation events', async () => {
await driver.initialized;
const debuggerLogSpy = spyOn(driver.debugger, 'log');

const event = {
violatedDirective: 'script-src',
blockedURI: 'http://example.com',
originalPolicy: "default-src 'self'",
statusCode: 500,
} as SecurityPolicyViolationEvent;

scope.handleSecurityPolicyViolation(event);

expect(debuggerLogSpy).toHaveBeenCalledWith(
'Security Policy Violation',
jasmine.stringMatching(/^Driver\.onSecurityPolicyViolation\(reason: .*?\)$/),
);
});
});

describe('notification close events', () => {
it('broadcasts notification close events', async () => {
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
Expand Down
8 changes: 8 additions & 0 deletions packages/service-worker/worker/testing/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,14 @@ export class SwTestHarnessImpl
this.eventHandlers.get('unhandledrejection')!.call(this, event);
}

handleSecurityPolicyViolation(event: SecurityPolicyViolationEvent): void {
if (!this.eventHandlers.has('securitypolicyviolation')) {
throw new Error('No securitypolicyviolation handler registered');
}

this.eventHandlers.get('securitypolicyviolation')!.call(this, event);
}

override timeout(ms: number): Promise<void> {
const promise = new Promise<void>((resolve) => {
this.timers.push({
Expand Down