diff --git a/packages/tsurlfilter/CHANGELOG.md b/packages/tsurlfilter/CHANGELOG.md index c22777222..72c2fcbb3 100644 --- a/packages/tsurlfilter/CHANGELOG.md +++ b/packages/tsurlfilter/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [Unreleased] + +### Added +- Added `CspReport` to RequestType enum. + ## [2.1.8] - 2023-08-14 ### Fixed diff --git a/packages/tsurlfilter/src/request-type.ts b/packages/tsurlfilter/src/request-type.ts index 6bdb04ac9..2d42f20cb 100644 --- a/packages/tsurlfilter/src/request-type.ts +++ b/packages/tsurlfilter/src/request-type.ts @@ -28,8 +28,10 @@ export const RequestType = { WebSocket: 512, // 1 << 9 /** (navigator.sendBeacon()) $ping */ Ping: 1024, // 1 << 10 + /** csp_report */ + CspReport: 2048, // 1 << 11 /** any other request type */ - Other: 2048, // 1 << 11 + Other: 4096, // 1 << 12 } as const; // intentionally naming the variable the same as the type diff --git a/packages/tsurlfilter/src/rules/declarative-converter/declarative-rule.ts b/packages/tsurlfilter/src/rules/declarative-converter/declarative-rule.ts index 2a8d9368b..96d315aba 100644 --- a/packages/tsurlfilter/src/rules/declarative-converter/declarative-rule.ts +++ b/packages/tsurlfilter/src/rules/declarative-converter/declarative-rule.ts @@ -36,6 +36,7 @@ export enum ResourceType { WebSocket = 'websocket', Other = 'other', // Temporary not using + // TODO: Add csp_report handler similar to AG-24613 but in declarative way. // CspReport = 'csp_report', // WebTransport = 'webtransport', // WebBundle = 'webbundle', diff --git a/packages/tsurlfilter/test/rules/declarative-converter/declarative-rule-converter.test.ts b/packages/tsurlfilter/test/rules/declarative-converter/declarative-rule-converter.test.ts index a4949c560..c11f7eedf 100644 --- a/packages/tsurlfilter/test/rules/declarative-converter/declarative-rule-converter.test.ts +++ b/packages/tsurlfilter/test/rules/declarative-converter/declarative-rule-converter.test.ts @@ -858,7 +858,7 @@ describe('DeclarativeRuleConverter', () => { expect(declarativeRules).toHaveLength(2); expect(declarativeRules[0]).toStrictEqual({ id: 1, - priority: 56, + priority: 55, action: { type: 'block', }, diff --git a/packages/tswebextension/CHANGELOG.md b/packages/tswebextension/CHANGELOG.md index 8e2463df5..d8d3fd21c 100644 --- a/packages/tswebextension/CHANGELOG.md +++ b/packages/tswebextension/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Blocking third-party requests with `csp_report` content-type. + ### Fixed - Do not expose JS rules in global page scope diff --git a/packages/tswebextension/src/lib/common/filtering-log.ts b/packages/tswebextension/src/lib/common/filtering-log.ts index 421ed1bf3..77d97470d 100644 --- a/packages/tswebextension/src/lib/common/filtering-log.ts +++ b/packages/tswebextension/src/lib/common/filtering-log.ts @@ -11,7 +11,6 @@ export enum FilteringEventType { TabReload = 'tabReload', ApplyBasicRule = 'applyBasicRule', ApplyCosmeticRule = 'applyCosmeticRule', - // TODO: Doesn't look like it's being used. ApplyCspRule = 'applyCspRule', ReceiveResponse = 'receiveResponse', Cookie = 'cookie', @@ -22,6 +21,7 @@ export enum FilteringEventType { ContentFilteringFinish = 'contentFilteringFinish', StealthAction = 'stealthAction', JsInject = 'jsInject', + CspReportBlocked = 'cspReportBlocked', } /** @@ -317,6 +317,29 @@ export type JsInjectEvent = { data: JsInjectEventData; }; +/** + * {@link CspReportBlockedEvent} Event data. + */ +export type CspReportBlockedEventData = { + tabId: number; + eventId: string; + cspReportBlocked: boolean; + requestUrl: string, + requestType: ContentType, + timestamp: number, + requestThirdParty: boolean, + method: string, +}; + +/** + * Dispatched by manifest v2 WebRequestApi.onBeforeCspReport handler when + * csp_report blocked in onBeforeRequest event handler. + */ +export type CspReportBlockedEvent = { + type: FilteringEventType.CspReportBlocked + data: CspReportBlockedEventData; +}; + /** * Filtering events union. * @@ -337,7 +360,8 @@ export type FilteringLogEvent = | ApplyCspRuleEvent | ApplyCosmeticRuleEvent | ReceiveResponseEvent - | JsInjectEvent; + | JsInjectEvent + | CspReportBlockedEvent; /** * Utility type for mapping {@link FilteringEventType} with specified {@link FilteringLogEvent}. diff --git a/packages/tswebextension/src/lib/common/request-type.ts b/packages/tswebextension/src/lib/common/request-type.ts index 87245377e..12f6b7d2f 100644 --- a/packages/tswebextension/src/lib/common/request-type.ts +++ b/packages/tswebextension/src/lib/common/request-type.ts @@ -99,7 +99,7 @@ export function getRequestType(resourceType: WebRequest.ResourceType): RequestTy case 'csp_report': return { contentType: ContentType.CspReport, - requestType: RequestType.Other, + requestType: RequestType.CspReport, }; default: return { diff --git a/packages/tswebextension/src/lib/mv2/background/engine-api.ts b/packages/tswebextension/src/lib/mv2/background/engine-api.ts index 53dfa1188..2e463690e 100644 --- a/packages/tswebextension/src/lib/mv2/background/engine-api.ts +++ b/packages/tswebextension/src/lib/mv2/background/engine-api.ts @@ -45,6 +45,9 @@ export class EngineApi { /** * Gets app filtering status. * + * TODO: This method is duplicated in {@link EngineApi}. Consider moving it to {@link appContext} + * itself (DRY). But appContext supposed to be deleted (v.zhelvis). + * * @returns True if filtering is enabled, otherwise returns false. */ public get isFilteringEnabled(): boolean { @@ -97,9 +100,9 @@ export class EngineApi { lists.push(new StringRuleList(USER_FILTER_ID, convertedUserRules)); } - const allowlistRules = this.allowlist.getAllowlistRules(); - if (allowlistRules) { - lists.push(allowlistRules); + const allowlistRulesList = this.allowlist.getAllowlistRules(); + if (allowlistRulesList) { + lists.push(allowlistRulesList); } const stealthModeList = this.stealthApi.getStealthModeRuleList(); diff --git a/packages/tswebextension/src/lib/mv2/background/stealth-api.ts b/packages/tswebextension/src/lib/mv2/background/stealth-api.ts index a17d2460b..a7822f2a3 100644 --- a/packages/tswebextension/src/lib/mv2/background/stealth-api.ts +++ b/packages/tswebextension/src/lib/mv2/background/stealth-api.ts @@ -64,6 +64,9 @@ export class StealthApi { /** * Gets app filtering status. * + * TODO: This method is duplicated in {@link EngineApi}. Consider moving it to {@link appContext} + * itself (DRY). But appContext supposed to be deleted (v.zhelvis). + * * @returns True if filtering is enabled, otherwise returns false. */ private get isFilteringEnabled(): boolean { diff --git a/packages/tswebextension/src/lib/mv2/background/web-request-api.ts b/packages/tswebextension/src/lib/mv2/background/web-request-api.ts index 75b653dd3..180e8427f 100644 --- a/packages/tswebextension/src/lib/mv2/background/web-request-api.ts +++ b/packages/tswebextension/src/lib/mv2/background/web-request-api.ts @@ -212,6 +212,7 @@ export class WebRequestApi { */ public static start(): void { // browser.webRequest Events + RequestEvents.onBeforeRequest.addListener(WebRequestApi.onBeforeCspReport); RequestEvents.onBeforeRequest.addListener(WebRequestApi.onBeforeRequest); RequestEvents.onBeforeSendHeaders.addListener(WebRequestApi.onBeforeSendHeaders); RequestEvents.onHeadersReceived.addListener(WebRequestApi.onHeadersReceived); @@ -228,6 +229,7 @@ export class WebRequestApi { * Removes web request event handlers. */ public static stop(): void { + RequestEvents.onBeforeRequest.removeListener(WebRequestApi.onBeforeCspReport); RequestEvents.onBeforeRequest.removeListener(WebRequestApi.onBeforeRequest); RequestEvents.onBeforeSendHeaders.removeListener(WebRequestApi.onBeforeSendHeaders); RequestEvents.onHeadersReceived.removeListener(WebRequestApi.onHeadersReceived); @@ -757,4 +759,81 @@ export class WebRequestApi { }) .catch(logger.debug); } + + /** + * Intercepts csp_report requests. + * Check the URL of the report. + * For chromium and firefox: + * If it's sent to a third party, block it right away. + * For firefox only: + * If it contains moz://extension with our extension ID, block it as well. + * @see https://github.com/AdguardTeam/AdguardBrowserExtension/issues/1792. + * + * @param details Request details. + * @param details.context Request context. + * + * @returns Web request response or void if there is nothing to do. + */ + private static onBeforeCspReport( + { context }: RequestData, + ): WebRequestEventResponse { + // If filtering is disabled - skip process request. + if (!engineApi.isFilteringEnabled) { + return undefined; + } + + if (!context) { + return undefined; + } + + const { + requestUrl, + requestType, + matchingResult, + tabId, + eventId, + timestamp, + thirdParty, + contentType, + method, + } = context; + + /** + * Checks request type here instead of creating two event listener with + * different filter types via {@link RequestEvent.init} to simplify + * event handlers flow and create only one {@link RequestEvents.onBeforeRequest} + * listener and two WebRequest listeners: {@link WebRequestApi.onBeforeCspReport} + * and {@link WebRequestApi.onBeforeRequest}. + */ + if (requestType !== RequestType.CspReport) { + return undefined; + } + + // If filtering disabled for this request. + if (matchingResult?.getBasicResult()?.isFilteringDisabled()) { + return undefined; + } + + if (thirdParty) { + defaultFilteringLog.publishEvent({ + type: FilteringEventType.CspReportBlocked, + data: { + tabId, + eventId, + cspReportBlocked: true, + requestUrl, + requestType: contentType, + timestamp, + requestThirdParty: thirdParty, + method, + }, + }); + + return { cancel: true }; + } + + // Don't check for moz://extension because it was fixed in + // https://bugzilla.mozilla.org/show_bug.cgi?id=1588957#c10. + return undefined; + } } diff --git a/packages/tswebextension/test/lib/mv2/background/request/request-type.test.ts b/packages/tswebextension/test/lib/mv2/background/request/request-type.test.ts index 1cb217955..d9dc82c18 100644 --- a/packages/tswebextension/test/lib/mv2/background/request/request-type.test.ts +++ b/packages/tswebextension/test/lib/mv2/background/request/request-type.test.ts @@ -60,7 +60,7 @@ describe('Request Type', () => { }, csp_report: { contentType: ContentType.CspReport, - requestType: RequestType.Other, + requestType: RequestType.CspReport, }, xmlhttprequest: { contentType: ContentType.XmlHttpRequest,