From 088d2ecd2a58ee70369025a1412b72056613cefa Mon Sep 17 00:00:00 2001 From: Artem Lavrov Date: Fri, 17 Dec 2021 15:05:18 +0300 Subject: [PATCH 1/4] fixed the referrer for cross-domain iframes --- src/client/sandbox/node/document/index.ts | 11 +++++++---- src/client/utils/destination-location.ts | 7 +++++++ src/client/utils/url.ts | 11 +++++++++++ src/request-pipeline/context.ts | 3 +++ src/utils/url.ts | 2 ++ test/playground/server.js | 1 + test/server/data/page/expected-https.html | 1 + test/server/data/page/expected.html | 1 + test/server/data/page/src.html | 1 + test/server/proxy/file-protocol-test.js | 3 ++- test/server/proxy/regression-test.js | 2 +- 11 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/client/sandbox/node/document/index.ts b/src/client/sandbox/node/document/index.ts index 8333ae217..f8a4dd59a 100644 --- a/src/client/sandbox/node/document/index.ts +++ b/src/client/sandbox/node/document/index.ts @@ -2,7 +2,6 @@ import SandboxBase from '../../base'; import IframeSandbox from '../../iframe'; import nativeMethods from '../../native-methods'; import domProcessor from '../../../dom-processor'; -import * as urlUtils from '../../../utils/url'; import settings from '../../../settings'; import { isIE } from '../../../utils/browser'; import { isIframeWithoutSrc, getFrameElement, isImgElement, isShadowUIElement } from '../../../utils/dom'; @@ -12,7 +11,8 @@ import INTERNAL_PROPS from '../../../../processing/dom/internal-properties'; import LocationAccessorsInstrumentation from '../../code-instrumentation/location'; import { overrideDescriptor, createOverriddenDescriptor, overrideFunction } from '../../../utils/overriding'; import NodeSandbox from '../index'; -import { getDestinationUrl, isSpecialPage } from '../../../utils/url'; +import { getDestinationUrl, isSpecialPage, convertToProxyUrl, getCrossDomainProxyOrigin } from '../../../utils/url'; +import { getReferrer } from '../../../utils/destination-location'; import DocumentTitleStorageInitializer from './title-storage-initializer'; import CookieSandbox from '../../cookie'; @@ -204,7 +204,7 @@ export default class DocumentSandbox extends SandboxBase { const el = nativeMethods.createElement.apply(this, args); DocumentSandbox.forceProxySrcForImageIfNecessary(el); - domProcessor.processElement(el, urlUtils.convertToProxyUrl); + domProcessor.processElement(el, convertToProxyUrl); documentSandbox._nodeSandbox.processNodes(el); return el; @@ -214,7 +214,7 @@ export default class DocumentSandbox extends SandboxBase { const el = nativeMethods.createElementNS.apply(this, args); DocumentSandbox.forceProxySrcForImageIfNecessary(el); - domProcessor.processElement(el, urlUtils.convertToProxyUrl); + domProcessor.processElement(el, convertToProxyUrl); documentSandbox._nodeSandbox.processNodes(el); return el; @@ -243,6 +243,9 @@ export default class DocumentSandbox extends SandboxBase { getter: function () { const referrer = getDestinationUrl(nativeMethods.documentReferrerGetter.call(this)); + if (referrer === getCrossDomainProxyOrigin() + '/') + return getReferrer(); + return isSpecialPage(referrer) ? '' : referrer; } }); diff --git a/src/client/utils/destination-location.ts b/src/client/utils/destination-location.ts index 24ef5c358..e9d638410 100644 --- a/src/client/utils/destination-location.ts +++ b/src/client/utils/destination-location.ts @@ -65,6 +65,13 @@ export let get = function (): string { return parsedProxyUrl ? parsedProxyUrl.destUrl : location; } +export function getReferrer () { + const location = getLocation(); + const parsedProxyUrl = sharedUrlUtils.parseProxyUrl(location); + + return parsedProxyUrl?.reqOrigin ? parsedProxyUrl.reqOrigin + '/' : ''; +} + export function overrideGet (func: typeof get) { get = func; } diff --git a/src/client/utils/url.ts b/src/client/utils/url.ts index eba2300b9..17ae8054b 100644 --- a/src/client/utils/url.ts +++ b/src/client/utils/url.ts @@ -134,6 +134,9 @@ export let getProxyUrl = function (url: string, opts?): string { reqOrigin = reqOrigin || destLocation.getOriginHeader(); } + if (parsedResourceType.isIframe) + reqOrigin = reqOrigin || destLocation.getOriginHeader(); + return sharedUrlUtils.getProxyUrl(resolvedUrl, { proxyProtocol, proxyHostname, @@ -248,6 +251,14 @@ export let convertToProxyUrl = function (url: string, resourceType, charset, isC }); } +export function getCrossDomainProxyOrigin () { + return sharedUrlUtils.getDomain({ + protocol: location.protocol, // eslint-disable-line no-restricted-properties + hostname: location.hostname, // eslint-disable-line no-restricted-properties + port: settings.get().crossDomainProxyPort, + }); +} + export function overrideConvertToProxyUrl (func: typeof convertToProxyUrl): void { convertToProxyUrl = func; } diff --git a/src/request-pipeline/context.ts b/src/request-pipeline/context.ts index 939c6705b..56c674bd7 100644 --- a/src/request-pipeline/context.ts +++ b/src/request-pipeline/context.ts @@ -436,6 +436,9 @@ export default class RequestPipelineContext { const sessionId = this.session.id; const windowId = this.windowId; + if (isCrossDomain) + reqOrigin = this.dest.domain; + return urlUtils.getProxyUrl(url, { proxyHostname, proxyProtocol, diff --git a/src/utils/url.ts b/src/utils/url.ts index 8bd592857..f2bfc5ed8 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -206,6 +206,8 @@ function parseRequestDescriptor (desc: string): RequestDescriptor | null { parsedDesc.charset = resourceData[0]; else if (parsedResourceType.isWebSocket) parsedDesc.reqOrigin = decodeURIComponent(restoreShortOrigin(resourceData[0])); + else if (parsedResourceType.isIframe && resourceData[0]) + parsedDesc.reqOrigin = decodeURIComponent(restoreShortOrigin(resourceData[0])); else if (parsedResourceType.isAjax) { parsedDesc.credentials = parseInt(resourceData[0]); diff --git a/test/playground/server.js b/test/playground/server.js index 7c36bd53a..7d700e772 100644 --- a/test/playground/server.js +++ b/test/playground/server.js @@ -59,6 +59,7 @@ exports.start = (options = {}) => { res .status(301) + .set('referrer-policy', 'no-referrer') .set('location', proxy.openSession(url, createSession())) .end(); } diff --git a/test/server/data/page/expected-https.html b/test/server/data/page/expected-https.html index 0881a4080..eadae6eaa 100644 --- a/test/server/data/page/expected-https.html +++ b/test/server/data/page/expected-https.html @@ -121,4 +121,5 @@ + diff --git a/test/server/data/page/expected.html b/test/server/data/page/expected.html index 833709c38..d7809a379 100644 --- a/test/server/data/page/expected.html +++ b/test/server/data/page/expected.html @@ -121,4 +121,5 @@ + diff --git a/test/server/data/page/src.html b/test/server/data/page/src.html index f257da04e..adaf85ec8 100644 --- a/test/server/data/page/src.html +++ b/test/server/data/page/src.html @@ -126,5 +126,6 @@ + diff --git a/test/server/proxy/file-protocol-test.js b/test/server/proxy/file-protocol-test.js index 4ec097af7..b416e1de9 100644 --- a/test/server/proxy/file-protocol-test.js +++ b/test/server/proxy/file-protocol-test.js @@ -54,7 +54,8 @@ describe('file:// protocol', () => { // NOTE: The host property is empty in url with file: protocol. // The expected.html template is used for both tests with http: and file: protocol. const expected = fs.readFileSync('test/server/data/page/expected.html').toString() - .replace(/(hammerhead\|storage-wrapper\|sessionId\|)127\.0\.0\.1:2000/g, '$1'); + .replace(/(hammerhead\|storage-wrapper\|sessionId\|)127\.0\.0\.1:2000/g, '$1') + .replace('!127.0.0.1%3A2000', '!'); compareCode(body, expected); }); diff --git a/test/server/proxy/regression-test.js b/test/server/proxy/regression-test.js index f16919f1e..fd1f5db61 100644 --- a/test/server/proxy/regression-test.js +++ b/test/server/proxy/regression-test.js @@ -802,7 +802,7 @@ describe('Regression', () => { it('Should process "x-frame-options" header (GH-1017)', () => { const getIframeProxyUrl = url => getProxyUrl(url, { isIframe: true }); - const getCrossDomainIframeProxyUrl = url => getProxyUrl(url, { isIframe: true }, void 0, void 0, true); + const getCrossDomainIframeProxyUrl = url => getProxyUrl(url, { isIframe: true }, 'http://127.0.0.1:2000', void 0, true); proxy.openSession('http://127.0.0.1:2000/', session); From 0feb20ece023d63e6e2a65eafb9493d331e3df67 Mon Sep 17 00:00:00 2001 From: Artem Lavrov Date: Fri, 17 Dec 2021 15:52:51 +0300 Subject: [PATCH 2/4] tests --- package.json | 2 +- src/client/utils/url.ts | 2 +- .../data/cross-domain/get-referrer.html | 21 +++++++++++++++++++ .../fixtures/sandbox/iframe-flag-test.js | 2 +- test/client/fixtures/sandbox/iframe-test.js | 14 ++++++++++++- test/client/fixtures/utils/html-test.js | 4 ++-- test/client/fixtures/utils/url-test.js | 2 +- 7 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 test/client/data/cross-domain/get-referrer.html diff --git a/package.json b/package.json index 3a7a8958e..7752f154e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "testcafe-hammerhead", "description": "A powerful web-proxy used as a core for the TestCafe testing framework (https://github.com/DevExpress/testcafe).", - "version": "24.5.9", + "version": "24.5.10", "homepage": "https://github.com/DevExpress/testcafe-hammerhead", "bugs": { "url": "https://github.com/DevExpress/testcafe-hammerhead/issues" diff --git a/src/client/utils/url.ts b/src/client/utils/url.ts index 17ae8054b..a35adf1aa 100644 --- a/src/client/utils/url.ts +++ b/src/client/utils/url.ts @@ -134,7 +134,7 @@ export let getProxyUrl = function (url: string, opts?): string { reqOrigin = reqOrigin || destLocation.getOriginHeader(); } - if (parsedResourceType.isIframe) + if (parsedResourceType.isIframe && proxyPort === settings.get().crossDomainProxyPort) reqOrigin = reqOrigin || destLocation.getOriginHeader(); return sharedUrlUtils.getProxyUrl(resolvedUrl, { diff --git a/test/client/data/cross-domain/get-referrer.html b/test/client/data/cross-domain/get-referrer.html new file mode 100644 index 000000000..418fffbf6 --- /dev/null +++ b/test/client/data/cross-domain/get-referrer.html @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/test/client/fixtures/sandbox/iframe-flag-test.js b/test/client/fixtures/sandbox/iframe-flag-test.js index 6038d3de8..8f37959b8 100644 --- a/test/client/fixtures/sandbox/iframe-flag-test.js +++ b/test/client/fixtures/sandbox/iframe-flag-test.js @@ -430,7 +430,7 @@ module('regression'); test('setAttribute: frame src (GH-1070)', function () { var frame = document.createElement('frame'); var proxyUrl = 'http://' + location.hostname + ':' + settings.get().crossDomainProxyPort + - '/sessionId!i/https://example.com/'; + '/sessionId!i!s*example.com/https://example.com/'; frame.setAttribute('src', 'https://example.com/'); diff --git a/test/client/fixtures/sandbox/iframe-test.js b/test/client/fixtures/sandbox/iframe-test.js index 89c2d95d6..b106bc5bf 100644 --- a/test/client/fixtures/sandbox/iframe-test.js +++ b/test/client/fixtures/sandbox/iframe-test.js @@ -187,9 +187,10 @@ if (nativeMethods.iframeSrcdocGetter) { iframe.contentDocument.close(); return new Promise(function (resolve) { - window.addEventListener('message', function (e) { + window.addEventListener('message', function onMessage (e) { strictEqual(e.data, 'message'); + window.removeEventListener('message', onMessage); document.body.removeChild(iframe); resolve(); }); @@ -502,6 +503,8 @@ test('self-removing script shouldn\'t throw an error (GH-TC-2469)', function () }; iframeDocument.write(''); iframeDocument.close(); + + document.body.removeChild(iframe); }); test('write "doctype" markup without head and body tags (GH-TC-2639)', function () { @@ -677,3 +680,12 @@ test('Uninitialized iframes should correctly behave when they are rewritten (GH- strictEqual(iframe2.contentDocument.body.lastChild.textContent, 'Second iframe!'); }); }); + +test('the correct referrer for cross-domain iframes', function () { + createTestIframe({ src: getCrossDomainPageUrl('../../data/cross-domain/get-referrer.html') }); + + return waitForMessage(window) + .then(function (referrer) { + strictEqual(referrer, 'https://example.com/'); + }); +}); diff --git a/test/client/fixtures/utils/html-test.js b/test/client/fixtures/utils/html-test.js index 2aad016d0..04e1936f6 100644 --- a/test/client/fixtures/utils/html-test.js +++ b/test/client/fixtures/utils/html-test.js @@ -95,9 +95,9 @@ test('form', function () { module('process html'); test('iframe', function () { - var processedHtml = htmlUtils.processHtml('