diff --git a/CHANGELOG.md b/CHANGELOG.md index daf32725f..4db3c7bd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- issue with `trusted-replace-fetch-response` scriptlet in case if data URL was used and properties was set by + `Object.defineProperty` to deceive scriptlet [#367](https://github.com/AdguardTeam/Scriptlets/issues/367) - issue with adding the same header value in `trusted-replace-xhr-response` scriptlet when it is used multiple times for the same request [#359](https://github.com/AdguardTeam/Scriptlets/issues/359) - issue with not pruning in `m3u-prune` scriptlet if file contains carriage return diff --git a/src/helpers/request-utils.ts b/src/helpers/request-utils.ts index 644a6e9b4..946dbaeed 100644 --- a/src/helpers/request-utils.ts +++ b/src/helpers/request-utils.ts @@ -78,13 +78,17 @@ export const getRequestData = (request: Request): Partial => { * @returns data object */ export const getFetchData = (args: [FetchResource, RequestInit]) => { + const requestClone = Request.prototype.clone; const fetchPropsObj: Record = {}; let fetchUrl: FetchResource; let fetchInit: RequestInit; if (args[0] instanceof Request) { + // Get real properties in case if data URL was used properties were set by Object.defineProperty + // https://github.com/AdguardTeam/Scriptlets/issues/367 + const realData = requestClone.call(args[0]); // if Request passed to fetch, it will be in array - const requestData = getRequestData(args[0]); + const requestData = getRequestData(realData); fetchUrl = requestData.url as string; fetchInit = requestData; } else { diff --git a/tests/scriptlets/trusted-replace-fetch-response.test.js b/tests/scriptlets/trusted-replace-fetch-response.test.js index aa63427dd..084ea0c8c 100644 --- a/tests/scriptlets/trusted-replace-fetch-response.test.js +++ b/tests/scriptlets/trusted-replace-fetch-response.test.js @@ -253,4 +253,50 @@ if (!isSupported) { assert.strictEqual(url, urlExpected, 'response prop is copied'); done(); }); + + test('Data URL request, URL set by Object.defineProperty, content should NOT be replaced', async (assert) => { + const JSON_CONTENT = '{"adPlacements":true,"playerAds":true,}'; + const BASE64 = btoa(JSON_CONTENT); + const DATA_REQUEST = `data:application/json;base64,${BASE64}`; + const REQUEST = new Request(DATA_REQUEST); + Object.defineProperty(REQUEST, 'url', { + get() { + return 'https://www.example.org/ad_url_test'; + }, + }); + Object.defineProperty(REQUEST, 'method', { + get() { + return 'POST'; + }, + }); + Object.defineProperty(REQUEST, 'bodyUsed', { + get() { + return !0; + }, + }); + Object.defineProperty(REQUEST, 'mode', { + get() { + return 'same-origin'; + }, + }); + Object.defineProperty(REQUEST, 'body', { + get() { + return new ReadableStream(); + }, + }); + + const done = assert.async(); + + const PATTERN = 'adPlacements'; + const REPLACEMENT = ''; + const PROPS_TO_MATCH = 'ad_url_test'; + runScriptlet(name, [PATTERN, REPLACEMENT, PROPS_TO_MATCH]); + + const response = await fetch(REQUEST); + const actualTextContent = await response.text(); + + assert.strictEqual(window.hit, undefined, 'hit should NOT fire'); + assert.strictEqual(actualTextContent, JSON_CONTENT, 'Content is intact'); + done(); + }); }