From e7b288f62525cca83ed956ed1ded4eb5c7935a11 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Tue, 27 Dec 2022 15:11:55 -0500 Subject: [PATCH 01/35] feat: set up experimentalUseDefaultDocumentDomain to disallow document.domain overwritting --- cli/types/cypress.d.ts | 9 +++++ packages/config/src/options.ts | 6 ++++ packages/config/test/project/utils.spec.ts | 2 ++ .../proxy/lib/http/response-middleware.ts | 1 + packages/proxy/lib/http/util/inject.ts | 35 +++++++++++++++---- packages/proxy/lib/http/util/rewriter.ts | 11 ++++-- packages/server/index.d.ts | 1 + packages/server/lib/experiments.ts | 2 ++ packages/types/src/config.ts | 2 +- yarn.lock | 28 +++------------ 10 files changed, 64 insertions(+), 33 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 5291f6488358..d960ec21cdfe 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3014,6 +3014,15 @@ declare namespace Cypress { * @see https://on.cypress.io/configuration#experimentalModifyObstructiveThirdPartyCode */ experimentalModifyObstructiveThirdPartyCode: boolean + /** + * Disables setting document.domain to the document's super domain on injection. + * This experiment is to be used for sites that do not work with setting document.domain + * due to cross-origin issues. Enabling this option no longer allows for default sub domain + * navigations, and will require the use of cy.origin(). + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/domain + * @default false + */ + experimentalUseDefaultDocumentDomain: boolean /** * Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm. * @default false diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index 763cd2be343e..eb4ac79d610f 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -215,6 +215,12 @@ const driverConfigOptions: Array = [ validation: validate.isBoolean, isExperimental: true, requireRestartOnChange: 'server', + }, { + name: 'experimentalUseDefaultDocumentDomain', + defaultValue: false, + validation: validate.isBoolean, + isExperimental: true, + requireRestartOnChange: 'server', }, { name: 'experimentalOriginDependencies', defaultValue: false, diff --git a/packages/config/test/project/utils.spec.ts b/packages/config/test/project/utils.spec.ts index 450c96b1b2b2..3231eb9d2daf 100644 --- a/packages/config/test/project/utils.spec.ts +++ b/packages/config/test/project/utils.spec.ts @@ -1052,6 +1052,7 @@ describe('config/src/project/utils', () => { env: {}, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, + experimentalUseDefaultDocumentDomain: { value: false, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, @@ -1147,6 +1148,7 @@ describe('config/src/project/utils', () => { downloadsFolder: { value: 'cypress/downloads', from: 'default' }, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, + experimentalUseDefaultDocumentDomain: { value: false, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, diff --git a/packages/proxy/lib/http/response-middleware.ts b/packages/proxy/lib/http/response-middleware.ts index 83b4e7a7290c..c61ff2e0172a 100644 --- a/packages/proxy/lib/http/response-middleware.ts +++ b/packages/proxy/lib/http/response-middleware.ts @@ -544,6 +544,7 @@ const MaybeInjectHtml: ResponseMiddleware = function () { isNotJavascript: !resContentTypeIsJavaScript(this.incomingRes), useAstSourceRewriting: this.config.experimentalSourceRewriting, modifyObstructiveThirdPartyCode: this.config.experimentalModifyObstructiveThirdPartyCode && !this.remoteStates.isPrimarySuperDomainOrigin(this.req.proxiedUrl), + useDefaultDocumentDomain: this.config.experimentalUseDefaultDocumentDomain, modifyObstructiveCode: this.config.modifyObstructiveCode, url: this.req.proxiedUrl, deferSourceMapRewrite: this.deferSourceMapRewrite, diff --git a/packages/proxy/lib/http/util/inject.ts b/packages/proxy/lib/http/util/inject.ts index f6c20bfe64b9..caff25ec1b82 100644 --- a/packages/proxy/lib/http/util/inject.ts +++ b/packages/proxy/lib/http/util/inject.ts @@ -2,25 +2,42 @@ import { oneLine } from 'common-tags' import { getRunnerInjectionContents, getRunnerCrossOriginInjectionContents } from '@packages/resolve-dist' import type { AutomationCookie } from '@packages/server/lib/automation/cookies' +interface InjectionOpts { + useDefaultDocumentDomain: boolean +} interface FullCrossOriginOpts { modifyObstructiveThirdPartyCode: boolean modifyObstructiveCode: boolean simulatedCookies: AutomationCookie[] } -export function partial (domain) { +export function partial (domain, options: InjectionOpts) { + let documentDomainInjection = `document.domain = '${domain}';` + + if (options.useDefaultDocumentDomain) { + documentDomainInjection = '' + } + + // with useDefaultDocumentDomain=true we continue to inject an empty script tag in order to be consistent with our other forms of injection. + // This is also diagnostic in nature is it will allow us to debug easily to make sure injection is still occurring. return oneLine` ` } -export function full (domain) { +export function full (domain, options: InjectionOpts) { return getRunnerInjectionContents().then((contents) => { + let documentDomainInjection = `document.domain = '${domain}';` + + if (options.useDefaultDocumentDomain) { + documentDomainInjection = '' + } + return oneLine` @@ -28,12 +45,18 @@ export function full (domain) { }) } -export async function fullCrossOrigin (domain, options: FullCrossOriginOpts) { +export async function fullCrossOrigin (domain, options: InjectionOpts & FullCrossOriginOpts) { const contents = await getRunnerCrossOriginInjectionContents() + let documentDomainInjection = `document.domain = '${domain}';` + + if (options.useDefaultDocumentDomain) { + documentDomainInjection = '' + } + return oneLine` diff --git a/packages/server/lib/routes-e2e.ts b/packages/server/lib/routes-e2e.ts index 68b65ed60c88..cadce9c5eec4 100644 --- a/packages/server/lib/routes-e2e.ts +++ b/packages/server/lib/routes-e2e.ts @@ -105,7 +105,7 @@ export const createRoutesE2E = ({ // @see https://github.com/cypress-io/cypress/issues/25010 res.setHeader('Origin-Agent-Cluster', '?0') - files.handleCrossOriginIframe(req, res, config.namespace) + files.handleCrossOriginIframe(req, res, config) }) return routesE2E From 3dda26b866937980519e41d890bfee3c2a7a3e8d Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 28 Dec 2022 14:17:29 -0500 Subject: [PATCH 03/35] run ci From 805de57772a27baec8cc08069b6df5eb076a1408 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 28 Dec 2022 16:43:52 -0500 Subject: [PATCH 04/35] fix: add insertion of experimental flag where is was needed/missing --- packages/driver/src/cy/commands/origin/validator.ts | 4 +++- packages/network/lib/cors.ts | 8 ++++++-- packages/proxy/lib/http/request-middleware.ts | 4 ++-- packages/server/lib/server-e2e.ts | 9 ++++++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/driver/src/cy/commands/origin/validator.ts b/packages/driver/src/cy/commands/origin/validator.ts index 7b32dd8a8ad9..c7a214c9eb9a 100644 --- a/packages/driver/src/cy/commands/origin/validator.ts +++ b/packages/driver/src/cy/commands/origin/validator.ts @@ -85,7 +85,9 @@ export class Validator { } // Users would be better off not using cy.origin if the origin is part of the same super domain. - if (cors.urlMatchesPolicyBasedOnDomain(originLocation.href, specHref)) { + if (cors.urlMatchesPolicyBasedOnDomain(originLocation.href, specHref, { + useDefaultDocumentDomain: Cypress.config('experimentalUseDefaultDocumentDomain'), + })) { // this._isSameSuperDomainOriginWithExceptions({ originLocation, specLocation })) { const policy = cors.policyForDomain(originLocation.href, { diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index 8020f0ba7ae1..ab037aa308dd 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -179,9 +179,13 @@ export const policyForDomain = (url: string, opts?: { * @param topUrl - The url you are testing the policy in context of. * @returns boolean, true if matching, false if not. */ -export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string): boolean => { +export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, opts?: { + useDefaultDocumentDomain: boolean +}): boolean => { return urlMatchesPolicy({ - policy: policyForDomain(frameUrl), + policy: policyForDomain(frameUrl, { + useDefaultDocumentDomain: opts?.useDefaultDocumentDomain || false, + }), frameUrl, topUrl, }) diff --git a/packages/proxy/lib/http/request-middleware.ts b/packages/proxy/lib/http/request-middleware.ts index 4e676290d028..fc625221f987 100644 --- a/packages/proxy/lib/http/request-middleware.ts +++ b/packages/proxy/lib/http/request-middleware.ts @@ -162,8 +162,8 @@ const MaybeEndRequestWithBufferedResponse: RequestMiddleware = function () { if (buffer) { this.debug('ending request with buffered response') - // NOTE: Only inject fullCrossOrigin here if experimental is on and - // the super domain origins do not match in order to keep parity with cypress application reloads + + // NOTE: Only inject fullCrossOrigin here the super domain origins do not match in order to keep parity with cypress application reloads this.res.wantsInjection = buffer.urlDoesNotMatchPolicyBasedOnDomain ? 'fullCrossOrigin' : 'full' return this.onResponse(buffer.response, buffer.stream) diff --git a/packages/server/lib/server-e2e.ts b/packages/server/lib/server-e2e.ts index 704d320fd563..834e6b06336e 100644 --- a/packages/server/lib/server-e2e.ts +++ b/packages/server/lib/server-e2e.ts @@ -45,6 +45,7 @@ const isResponseHtml = function (contentType, responseBuffer) { export class ServerE2E extends ServerBase { private _urlResolver: Bluebird> | null + private useDefaultDocumentDomain: boolean = false constructor () { super() @@ -58,10 +59,10 @@ export class ServerE2E extends ServerBase { createServer (app, config, onWarning): Bluebird<[number, WarningErr?]> { return new Bluebird((resolve, reject) => { - const { port, fileServerFolder, socketIoRoute, baseUrl } = config + const { port, fileServerFolder, socketIoRoute, baseUrl, experimentalUseDefaultDocumentDomain } = config this._server = this._createHttpServer(app) - + this.useDefaultDocumentDomain = experimentalUseDefaultDocumentDomain const onError = (err) => { // if the server bombs before starting // and the err no is EADDRINUSE @@ -308,7 +309,9 @@ export class ServerE2E extends ServerBase { // TODO: think about moving this logic back into the frontend so that the driver can be in control // of when to buffer and set the remote state if (isOk && details.isHtml) { - const urlDoesNotMatchPolicyBasedOnDomain = options.hasAlreadyVisitedUrl && !cors.urlMatchesPolicyBasedOnDomain(primaryRemoteState.origin, newUrl || '') || options.isFromSpecBridge + const urlDoesNotMatchPolicyBasedOnDomain = options.hasAlreadyVisitedUrl + && !cors.urlMatchesPolicyBasedOnDomain(primaryRemoteState.origin, newUrl || '', { useDefaultDocumentDomain: this.useDefaultDocumentDomain }) + || options.isFromSpecBridge if (!handlingLocalFile) { this._remoteStates.set(newUrl as string, options, !urlDoesNotMatchPolicyBasedOnDomain) From 0b35f47c6be1f7018d631c285a319025d97f77da Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 28 Dec 2022 16:44:27 -0500 Subject: [PATCH 05/35] chore: add system test to exercise experimental flag for expected behavior --- ...tal_use_default_document_domain_spec.ts.js | 65 +++++++++++++++++++ ...rimental_use_default_document_domain.cy.ts | 41 ++++++++++++ ...mental_use_default_document_domain_spec.ts | 44 +++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js create mode 100644 system-tests/projects/e2e/cypress/e2e/experimental_use_default_document_domain.cy.ts create mode 100644 system-tests/test/experimental_use_default_document_domain_spec.ts diff --git a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js new file mode 100644 index 000000000000..ce16935409bc --- /dev/null +++ b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js @@ -0,0 +1,65 @@ +exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (experimental_use_default_document_domain.cy.ts) │ + │ Searched: cypress/e2e/experimental_use_default_document_domain.cy.ts │ + │ Experiments: experimentalUseDefaultDocumentDomain=true │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: experimental_use_default_document_domain.cy.ts (1 of 1) + + + expected behavior when experimentalUseDefaultDocumentDomain=true + ✓ Handles cross-site/cross-origin navigation the same way without the experimental flag enabled + ✓ errors appropriately when doing a sub domain navigation w/o cy.origin() + ✓ allows sub-domain navigations with the use of cy.origin() + + + 3 passing + +Warning: We failed processing this video. + +This error will not alter the exit code. + +TimeoutError: operation timed out + [stack trace lines] + + + (Results) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Tests: 3 │ + │ Passing: 3 │ + │ Failing: 0 │ + │ Pending: 0 │ + │ Skipped: 0 │ + │ Screenshots: 0 │ + │ Video: false │ + │ Duration: X seconds │ + │ Spec Ran: experimental_use_default_document_domain.cy.ts │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +==================================================================================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ✔ experimental_use_default_document_d XX:XX 3 3 - - - │ + │ omain.cy.ts │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + ✔ All specs passed! XX:XX 3 3 - - - + + +` diff --git a/system-tests/projects/e2e/cypress/e2e/experimental_use_default_document_domain.cy.ts b/system-tests/projects/e2e/cypress/e2e/experimental_use_default_document_domain.cy.ts new file mode 100644 index 000000000000..f02ad0a0ccb1 --- /dev/null +++ b/system-tests/projects/e2e/cypress/e2e/experimental_use_default_document_domain.cy.ts @@ -0,0 +1,41 @@ +describe('expected behavior when experimentalUseDefaultDocumentDomain=true', () => { + it('Handles cross-site/cross-origin navigation the same way without the experimental flag enabled', () => { + cy.visit('/primary_origin.html') + cy.get('a[data-cy="cross_origin_secondary_link"]').click() + cy.origin('http://www.foobar.com:4466', () => { + cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin') + }) + }) + + it('errors appropriately when doing a sub domain navigation w/o cy.origin()', () => { + const timeout = 500 + + cy.on('fail', (err) => { + expect(err.name).to.equal('CypressError') + expect(err.message).to.contain(`Timed out retrying after ${timeout}ms: The command was expected to run against origin \`http://app.foobar.com:4466\` but the application is at origin \`http://www.foobar.com:4466\`.`) + expect(err.message).to.contain('This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.') + expect(err.message).to.contain('Using `cy.origin()` to wrap the commands run on `http://www.foobar.com:4466` will likely fix this issue.') + expect(err.message).to.include(`cy.origin('http://www.foobar.com:4466', () => {\`\n\` \`\n\`})`) + // make sure that the secondary origin failures do NOT show up as spec failures or AUT failures + expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`) + expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`) + }) + + // with experimentalUseDefaultDocumentDomain, sub domain navigations require a cy.origin() block + cy.visit('http://app.foobar.com:4466/primary_origin.html') + cy.get('a[data-cy="cross_origin_secondary_link"]').click() + cy.get('[data-cy="dom-check"]', { + timeout, + }).should('have.text', 'From a secondary origin') + }) + + it('allows sub-domain navigations with the use of cy.origin()', () => { + cy.visit('http://app.foobar.com:4466/primary_origin.html') + cy.get('a[data-cy="cross_origin_secondary_link"]').click() + + // with experimentalUseDefaultDocumentDomain, sub domain navigations require a cy.origin() block + cy.origin('http://www.foobar.com:4466', () => { + cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin') + }) + }) +}) diff --git a/system-tests/test/experimental_use_default_document_domain_spec.ts b/system-tests/test/experimental_use_default_document_domain_spec.ts new file mode 100644 index 000000000000..5d0c5c11ae9e --- /dev/null +++ b/system-tests/test/experimental_use_default_document_domain_spec.ts @@ -0,0 +1,44 @@ +import path from 'path' +import systemTests from '../lib/system-tests' +import Fixtures from '../lib/fixtures' + +const e2ePath = Fixtures.projectPath('e2e') + +const PORT = 3500 +const onServer = function (app) { + app.get('/primary_origin.html', (_, res) => { + res.sendFile(path.join(e2ePath, `primary_origin.html`)) + }) + + app.get('/secondary_origin.html', (_, res) => { + res.sendFile(path.join(e2ePath, `secondary_origin.html`)) + }) +} + +describe('e2e experimentalUseDefaultDocumentDomain=true', () => { + systemTests.setup({ + servers: [{ + port: 4466, + onServer, + }], + settings: { + hosts: { + '*.foobar.com': '127.0.0.1', + }, + e2e: {}, + }, + }) + + systemTests.it('passes', { + browser: '!webkit', // TODO(webkit): fix+unskip (needs multidomain support) + // keep the port the same to prevent issues with the snapshot + port: PORT, + spec: 'experimental_use_default_document_domain.cy.ts', + snapshot: true, + expectedExitCode: 0, + config: { + retries: 0, + experimentalUseDefaultDocumentDomain: true, + }, + }) +}) From 9ddbd99527a14a4144092fc7bdd55bd01ec93531 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 28 Dec 2022 17:28:00 -0500 Subject: [PATCH 06/35] fix: fix issues with template updates to conform to squirrelly v7 --- packages/server/lib/controllers/files.js | 8 ++++---- packages/server/lib/html/iframe.html | 6 ++++-- packages/server/lib/html/spec-bridge-iframe.html | 4 +++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/server/lib/controllers/files.js b/packages/server/lib/controllers/files.js index 8ea2bbe204b8..9cee629f87d4 100644 --- a/packages/server/lib/controllers/files.js +++ b/packages/server/lib/controllers/files.js @@ -25,10 +25,10 @@ module.exports = { } debug('all files to send %o', _.map(allFilesToSend, 'relative')) - const superDomain = config.experimentalUseDefaultDocumentDomain ? undefined : remoteStates.getPrimary().domainName + const documentSuperDomainIfExists = config.experimentalUseDefaultDocumentDomain ? undefined : remoteStates.getPrimary().domainName const iframeOptions = { - superDomain, + documentSuperDomainIfExists, title: this.getTitle(test), scripts: JSON.stringify(allFilesToSend), } @@ -41,11 +41,11 @@ module.exports = { handleCrossOriginIframe (req, res, config) { const iframePath = cwd('lib', 'html', 'spec-bridge-iframe.html') - const superDomain = config.experimentalUseDefaultDocumentDomain ? undefined : cors.getSuperDomain(req.proxiedUrl) + const documentSuperDomainIfExists = config.experimentalUseDefaultDocumentDomain ? undefined : cors.getSuperDomain(req.proxiedUrl) const origin = cors.getOrigin(req.proxiedUrl) const iframeOptions = { - superDomain, + documentSuperDomainIfExists, title: `Cypress for ${origin}`, namespace: config.namespace, } diff --git a/packages/server/lib/html/iframe.html b/packages/server/lib/html/iframe.html index 3db531ff3910..fbf8d41e3931 100644 --- a/packages/server/lib/html/iframe.html +++ b/packages/server/lib/html/iframe.html @@ -6,8 +6,10 @@ From 3fe48f931f5bab4d50c3242cf08e81a69d73877e Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 28 Dec 2022 17:31:22 -0500 Subject: [PATCH 07/35] fix: update config tests to include new experimental flag --- packages/config/__snapshots__/index.spec.ts.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/config/__snapshots__/index.spec.ts.js b/packages/config/__snapshots__/index.spec.ts.js index c6eb06cf4c76..34aaee8df1ce 100644 --- a/packages/config/__snapshots__/index.spec.ts.js +++ b/packages/config/__snapshots__/index.spec.ts.js @@ -38,6 +38,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, + 'experimentalUseDefaultDocumentDomain': false, 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, @@ -123,6 +124,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, + 'experimentalUseDefaultDocumentDomain': false, 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, @@ -204,6 +206,7 @@ exports['config/src/index .getPublicConfigKeys returns list of public config key 'experimentalInteractiveRunEvents', 'experimentalRunAllSpecs', 'experimentalModifyObstructiveThirdPartyCode', + 'experimentalUseDefaultDocumentDomain', 'experimentalOriginDependencies', 'experimentalSourceRewriting', 'experimentalSingleTabRunMode', From d422738d6512c1b5080b8a1d57198f7189d79164 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 28 Dec 2022 17:31:30 -0500 Subject: [PATCH 08/35] run ci From 0a5750f125958365924c6eeda1395ba3f770a3bb Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 09:52:03 -0500 Subject: [PATCH 09/35] fix: trailing whitespace [run ci] --- cli/types/cypress.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index d960ec21cdfe..12779a6e2686 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3017,7 +3017,7 @@ declare namespace Cypress { /** * Disables setting document.domain to the document's super domain on injection. * This experiment is to be used for sites that do not work with setting document.domain - * due to cross-origin issues. Enabling this option no longer allows for default sub domain + * due to cross-origin issues. Enabling this option no longer allows for default sub domain * navigations, and will require the use of cy.origin(). * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/domain * @default false From 41896c8e7979120b49f0a8a71c23f2042e7d32ca Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 09:59:20 -0500 Subject: [PATCH 10/35] chore: update snapshot --- ...mental_use_default_document_domain_spec.ts.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js index ce16935409bc..f2e7e32bb9ee 100644 --- a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js +++ b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js @@ -26,13 +26,6 @@ exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` 3 passing -Warning: We failed processing this video. - -This error will not alter the exit code. - -TimeoutError: operation timed out - [stack trace lines] - (Results) @@ -43,12 +36,19 @@ TimeoutError: operation timed out │ Pending: 0 │ │ Skipped: 0 │ │ Screenshots: 0 │ - │ Video: false │ + │ Video: true │ │ Duration: X seconds │ │ Spec Ran: experimental_use_default_document_domain.cy.ts │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ + (Video) + + - Started processing: Compressing to 32 CRF + - Finished processing: /XXX/XXX/XXX/cypress/videos/experimental_use_default_docume (X second) + nt_domain.cy.ts.mp4 + + ==================================================================================================== (Run Finished) From 48c6b047140dd64437feda1bac3ca98b3e2c9f38 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 10:00:07 -0500 Subject: [PATCH 11/35] run ci From 03dd43923c15e8ce52d295d98eda3d1301f00f4b Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 10:57:12 -0500 Subject: [PATCH 12/35] fix: update proxy unit tests to account for experimentalUseDefaultDocumentDomain --- packages/proxy/test/unit/http/response-middleware.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/proxy/test/unit/http/response-middleware.spec.ts b/packages/proxy/test/unit/http/response-middleware.spec.ts index d00c749b86cb..a04ba4526b7e 100644 --- a/packages/proxy/test/unit/http/response-middleware.spec.ts +++ b/packages/proxy/test/unit/http/response-middleware.spec.ts @@ -1395,6 +1395,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': true, 'modifyObstructiveThirdPartyCode': true, + 'useDefaultDocumentDomain': false, 'url': 'http://www.foobar.com:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1418,6 +1419,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': true, 'modifyObstructiveThirdPartyCode': false, + 'useDefaultDocumentDomain': false, 'url': 'http://127.0.0.1:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1435,6 +1437,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: false, experimentalModifyObstructiveThirdPartyCode: false, + experimentalUseDefaultDocumentDomain: false, }, simulatedCookies: [], }) @@ -1448,6 +1451,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': false, 'modifyObstructiveThirdPartyCode': false, + 'useDefaultDocumentDomain': false, 'url': 'http://www.foobar.com:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1485,6 +1489,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: true, experimentalModifyObstructiveThirdPartyCode: true, + experimentalUseDefaultDocumentDomain: false, }, remoteStates, debug: (formatter, ...args) => { From 38dd0808f08ca25049255ffdd5c5871d16b49e7c Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 10:57:24 -0500 Subject: [PATCH 13/35] run ci From fc18ac62e2648ef66f617b1d0b1c24e8b28f11ea Mon Sep 17 00:00:00 2001 From: Mike Plummer Date: Thu, 29 Dec 2022 10:33:40 -0600 Subject: [PATCH 14/35] fix: Allow component tests with special characters in filepath (#25299) feat: cut over experimental flag to take list of known problematic domains via string/glob pattern run ci chore: update system test and fix broken config --- cli/types/cypress.d.ts | 8 +- npm/webpack-dev-server/src/loader.ts | 2 +- .../config/__snapshots__/index.spec.ts.js | 4 +- packages/config/src/options.ts | 4 +- packages/config/src/validation.ts | 2 +- packages/config/test/project/utils.spec.ts | 4 +- .../src/cy/commands/origin/validator.ts | 4 +- packages/driver/src/cypress.ts | 7 +- packages/network/lib/cors.ts | 33 +++++--- packages/network/package.json | 1 + packages/network/test/unit/cors_spec.ts | 80 ++++++++++++++++++- .../proxy/lib/http/response-middleware.ts | 8 +- packages/proxy/lib/http/util/inject.ts | 8 +- packages/proxy/lib/http/util/rewriter.ts | 10 +-- .../unit/http/response-middleware.spec.ts | 10 +-- packages/server/index.d.ts | 2 +- packages/server/lib/controllers/files.js | 14 +++- packages/server/lib/server-e2e.ts | 7 +- ...tal_use_default_document_domain_spec.ts.js | 2 +- ...mental_use_default_document_domain_spec.ts | 2 +- system-tests/test/webpack_dev_server_spec.ts | 34 ++++++++ yarn.lock | 2 +- 22 files changed, 194 insertions(+), 54 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 12779a6e2686..df2993f1fd08 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3018,11 +3018,13 @@ declare namespace Cypress { * Disables setting document.domain to the document's super domain on injection. * This experiment is to be used for sites that do not work with setting document.domain * due to cross-origin issues. Enabling this option no longer allows for default sub domain - * navigations, and will require the use of cy.origin(). + * navigations, and will require the use of cy.origin(). This option takes an array of + * strings/string globs, and applies defaults for known domains where document.domain is known + * to be an issue. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/domain - * @default false + * @default {array} ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] */ - experimentalUseDefaultDocumentDomain: boolean + experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] /** * Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm. * @default false diff --git a/npm/webpack-dev-server/src/loader.ts b/npm/webpack-dev-server/src/loader.ts index 171accbc3d0e..35e27396f25c 100644 --- a/npm/webpack-dev-server/src/loader.ts +++ b/npm/webpack-dev-server/src/loader.ts @@ -18,7 +18,7 @@ const makeImport = (file: Cypress.Cypress['spec'], filename: string, chunkName: const magicComments = chunkName ? `/* webpackChunkName: "${chunkName}" */` : '' return `"${filename}": { - shouldLoad: () => document.location.pathname.includes("${encodeURI(file.absolute)}"), + shouldLoad: () => decodeURI(document.location.pathname).includes("${file.absolute}"), load: () => import("${file.absolute}" ${magicComments}), absolute: "${file.absolute.split(path.sep).join(path.posix.sep)}", relative: "${file.relative.split(path.sep).join(path.posix.sep)}", diff --git a/packages/config/__snapshots__/index.spec.ts.js b/packages/config/__snapshots__/index.spec.ts.js index 34aaee8df1ce..330551fca3c0 100644 --- a/packages/config/__snapshots__/index.spec.ts.js +++ b/packages/config/__snapshots__/index.spec.ts.js @@ -38,7 +38,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, - 'experimentalUseDefaultDocumentDomain': false, + 'experimentalUseDefaultDocumentDomain': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, @@ -124,7 +124,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, - 'experimentalUseDefaultDocumentDomain': false, + 'experimentalUseDefaultDocumentDomain': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index eb4ac79d610f..990c3718d2f9 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -217,8 +217,8 @@ const driverConfigOptions: Array = [ requireRestartOnChange: 'server', }, { name: 'experimentalUseDefaultDocumentDomain', - defaultValue: false, - validation: validate.isBoolean, + defaultValue: () => ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + validation: (_: string, value: any) => validate.isArrayOfStrings(value), isExperimental: true, requireRestartOnChange: 'server', }, { diff --git a/packages/config/src/validation.ts b/packages/config/src/validation.ts index 589f502e93a7..92d2b28b7d6e 100644 --- a/packages/config/src/validation.ts +++ b/packages/config/src/validation.ts @@ -33,7 +33,7 @@ const _isFullyQualifiedUrl = (value: any): ErrResult | boolean => { return _.isString(value) && /^https?\:\/\//.test(value) } -const isArrayOfStrings = (value: any): ErrResult | boolean => { +export const isArrayOfStrings = (value: any): ErrResult | boolean => { return _.isArray(value) && _.every(value, _.isString) } diff --git a/packages/config/test/project/utils.spec.ts b/packages/config/test/project/utils.spec.ts index 3231eb9d2daf..4d70b77e19c2 100644 --- a/packages/config/test/project/utils.spec.ts +++ b/packages/config/test/project/utils.spec.ts @@ -1052,7 +1052,7 @@ describe('config/src/project/utils', () => { env: {}, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, - experimentalUseDefaultDocumentDomain: { value: false, from: 'default' }, + experimentalUseDefaultDocumentDomain: { value: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, @@ -1148,7 +1148,7 @@ describe('config/src/project/utils', () => { downloadsFolder: { value: 'cypress/downloads', from: 'default' }, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, - experimentalUseDefaultDocumentDomain: { value: false, from: 'default' }, + experimentalUseDefaultDocumentDomain: { value: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, diff --git a/packages/driver/src/cy/commands/origin/validator.ts b/packages/driver/src/cy/commands/origin/validator.ts index c7a214c9eb9a..055d039bfc10 100644 --- a/packages/driver/src/cy/commands/origin/validator.ts +++ b/packages/driver/src/cy/commands/origin/validator.ts @@ -86,12 +86,12 @@ export class Validator { // Users would be better off not using cy.origin if the origin is part of the same super domain. if (cors.urlMatchesPolicyBasedOnDomain(originLocation.href, specHref, { - useDefaultDocumentDomain: Cypress.config('experimentalUseDefaultDocumentDomain'), + useDefaultDocumentForDomains: Cypress.config('experimentalUseDefaultDocumentDomain'), })) { // this._isSameSuperDomainOriginWithExceptions({ originLocation, specLocation })) { const policy = cors.policyForDomain(originLocation.href, { - useDefaultDocumentDomain: Cypress.config('experimentalUseDefaultDocumentDomain'), + useDefaultDocumentForDomains: Cypress.config('experimentalUseDefaultDocumentDomain'), }) $errUtils.throwErrByPath('origin.invalid_url_argument_same_origin', { diff --git a/packages/driver/src/cypress.ts b/packages/driver/src/cypress.ts index 15585e66bd1a..af1f6af32669 100644 --- a/packages/driver/src/cypress.ts +++ b/packages/driver/src/cypress.ts @@ -44,6 +44,7 @@ import { PrimaryOriginCommunicator, SpecBridgeCommunicator } from './cross-origi import { setupAutEventHandlers } from './cypress/aut_event_handlers' import type { CachedTestState } from '@packages/types' +import * as cors from '@packages/network/lib/cors' const debug = debugFn('cypress:driver:cypress') @@ -182,7 +183,11 @@ class $Cypress { // set domainName but allow us to turn // off this feature in testing - if (domainName && config.testingType === 'e2e' && !config.experimentalUseDefaultDocumentDomain) { + const shouldInjectDocumentDomain = cors.shouldInjectDocumentDomain(window.location.origin, { + useDefaultDocumentForDomains: config.experimentalUseDefaultDocumentDomain, + }) + + if (domainName && config.testingType === 'e2e' && shouldInjectDocumentDomain) { document.domain = domainName } diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index ab037aa308dd..0df29896238f 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -1,4 +1,5 @@ import _ from 'lodash' +import minimatch from 'minimatch' import * as uri from './uri' import debugModule from 'debug' import _parseDomain from '@cypress/parse-domain' @@ -11,8 +12,6 @@ const debug = debugModule('cypress:network:cors') // match IP addresses or anything following the last . const customTldsRe = /(^[\d\.]+$|\.[^\.]+$)/ -const strictSameOriginDomains = Object.freeze(['google.com']) - export function getSuperDomain (url) { const parsed = parseUrlIntoHostProtocolDomainTldPort(url) @@ -164,11 +163,24 @@ export const urlSameSiteMatch = (frameUrl: string, topUrl: string): boolean => { * @returns a Policy string. */ export const policyForDomain = (url: string, opts?: { - useDefaultDocumentDomain: boolean + useDefaultDocumentForDomains: string[] }): Policy => { - const obj = parseUrlIntoHostProtocolDomainTldPort(url) + let { hostname } = uri.parse(url) + const hasDefaultDomainMatch = !!opts?.useDefaultDocumentForDomains.find((globPattern) => { + return minimatch(hostname || '', globPattern) + }) - return opts?.useDefaultDocumentDomain || strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`) ? 'same-origin' : 'same-super-domain-origin' + return hasDefaultDomainMatch ? + 'same-origin' : + 'same-super-domain-origin' +} + +export const shouldInjectDocumentDomain = (url: string, opts?: { + useDefaultDocumentForDomains: string[] +}) => { + return policyForDomain(url, { + useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [], + }) === 'same-super-domain-origin' } /** @@ -180,11 +192,11 @@ export const policyForDomain = (url: string, opts?: { * @returns boolean, true if matching, false if not. */ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, opts?: { - useDefaultDocumentDomain: boolean + useDefaultDocumentForDomains: string[] }): boolean => { return urlMatchesPolicy({ policy: policyForDomain(frameUrl, { - useDefaultDocumentDomain: opts?.useDefaultDocumentDomain || false, + useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [], }), frameUrl, topUrl, @@ -200,10 +212,11 @@ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, * @returns boolean, true if matching, false if not. */ export const urlMatchesPolicyBasedOnDomainProps = (frameUrl: string, topProps: ParsedHostWithProtocolAndHost, opts?: { - useDefaultDocumentDomain: boolean + useDefaultDocumentForDomains: string[] }): boolean => { - const obj = parseUrlIntoHostProtocolDomainTldPort(frameUrl) - const policy = opts?.useDefaultDocumentDomain || strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`) ? 'same-origin' : 'same-super-domain-origin' + const policy = policyForDomain(frameUrl, { + useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [], + }) return urlMatchesPolicyProps({ policy, diff --git a/packages/network/package.json b/packages/network/package.json index 886cdca638b6..e8a89e320c81 100644 --- a/packages/network/package.json +++ b/packages/network/package.json @@ -20,6 +20,7 @@ "debug": "^4.3.2", "fs-extra": "9.1.0", "lodash": "^4.17.21", + "minimatch": "^3.0.5", "node-forge": "1.3.0", "proxy-from-env": "1.0.0" }, diff --git a/packages/network/test/unit/cors_spec.ts b/packages/network/test/unit/cors_spec.ts index 348e65ddd2f3..33caa9c353b2 100644 --- a/packages/network/test/unit/cors_spec.ts +++ b/packages/network/test/unit/cors_spec.ts @@ -2,6 +2,8 @@ import { cors } from '../../lib' import { expect } from 'chai' describe('lib/cors', () => { + const defaultSameOriginPolicyUrlGlobs = ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] + context('.parseUrlIntoHostProtocolDomainTldPort', () => { const expectUrlToBeParsedCorrectly = (url, obj) => { expect(cors.parseUrlIntoHostProtocolDomainTldPort(url)).to.deep.eq(obj) @@ -323,11 +325,15 @@ describe('lib/cors', () => { context('.urlMatchesPolicyBasedOnDomain', () => { const assertsUrlsAreNotAPolicyMatch = (url1, url2) => { - expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2)).to.be.false + expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2, { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.be.false } const assertsUrlsAreAPolicyOriginMatch = (url1, url2) => { - expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2)).to.be.true + expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2, { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.be.true } describe('domain + subdomain', () => { @@ -454,11 +460,15 @@ describe('lib/cors', () => { context('.urlMatchesPolicyBasedOnDomainProps', () => { const assertsUrlsAreNotAPolicyMatch = (url1, props) => { - expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props)).to.be.false + expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props, { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.be.false } const assertsUrlsAreAPolicyOriginMatch = (url1, props) => { - expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props)).to.be.true + expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props, { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.be.true } describe('domain + subdomain', () => { @@ -657,4 +667,66 @@ describe('lib/cors', () => { expect(cors.getOrigin('http://www.app.herokuapp.com:8080')).to.equal('http://www.app.herokuapp.com:8080') }) }) + + context('.policyForDomain', () => { + context('returns "same-origin" for google domains', () => { + it('accounts.google.com', () => { + expect(cors.policyForDomain('https://accounts.google.com', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-origin') + }) + + it('www.google.com', () => { + expect(cors.policyForDomain('https://www.google.com', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-origin') + }) + }) + + context('returns "same-origin" for salesforce domains', () => { + it('https://the-host.develop.lightning.force.com', () => { + expect(cors.policyForDomain('https://the-host.develop.lightning.force.com', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-origin') + }) + + it('https://the-host.develop.my.salesforce.com', () => { + expect(cors.policyForDomain('https://the-host.develop.my.salesforce.com', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-origin') + }) + + it('https://the-host.develop.file.force.com', () => { + expect(cors.policyForDomain('https://the-host.develop.file.force.com', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-origin') + }) + + it('https://the-host.develop.my.salesforce.com', () => { + expect(cors.policyForDomain('https://the-host.develop.my.salesforce.com', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-origin') + }) + }) + + describe('returns "same-super-domain-origin" for non exception urls', () => { + it('www.cypress.io', () => { + expect(cors.policyForDomain('http://www.cypress.io', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-super-domain-origin') + }) + + it('docs.cypress.io', () => { + expect(cors.policyForDomain('http://docs.cypress.io', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-super-domain-origin') + }) + + it('stackoverflow.com', () => { + expect(cors.policyForDomain('https://stackoverflow.com', { + useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + })).to.equal('same-super-domain-origin') + }) + }) + }) }) diff --git a/packages/proxy/lib/http/response-middleware.ts b/packages/proxy/lib/http/response-middleware.ts index bd7073315b5a..e0792310d142 100644 --- a/packages/proxy/lib/http/response-middleware.ts +++ b/packages/proxy/lib/http/response-middleware.ts @@ -55,10 +55,10 @@ function getNodeCharsetFromResponse (headers: IncomingHttpHeaders, body: Buffer, return 'latin1' } -function reqMatchesPolicyBasedOnDomain (req: CypressIncomingRequest, remoteState, useDefaultDocumentDomain) { +function reqMatchesPolicyBasedOnDomain (req: CypressIncomingRequest, remoteState, useDefaultDocumentForDomains) { if (remoteState.strategy === 'http') { return cors.urlMatchesPolicyBasedOnDomainProps(req.proxiedUrl, remoteState.props, { - useDefaultDocumentDomain, + useDefaultDocumentForDomains, }) } @@ -546,7 +546,9 @@ const MaybeInjectHtml: ResponseMiddleware = function () { isNotJavascript: !resContentTypeIsJavaScript(this.incomingRes), useAstSourceRewriting: this.config.experimentalSourceRewriting, modifyObstructiveThirdPartyCode: this.config.experimentalModifyObstructiveThirdPartyCode && !this.remoteStates.isPrimarySuperDomainOrigin(this.req.proxiedUrl), - useDefaultDocumentDomain: this.config.experimentalUseDefaultDocumentDomain, + shouldInjectDocumentDomain: cors.shouldInjectDocumentDomain(this.req.proxiedUrl, { + useDefaultDocumentForDomains: this.config.experimentalUseDefaultDocumentDomain, + }), modifyObstructiveCode: this.config.modifyObstructiveCode, url: this.req.proxiedUrl, deferSourceMapRewrite: this.deferSourceMapRewrite, diff --git a/packages/proxy/lib/http/util/inject.ts b/packages/proxy/lib/http/util/inject.ts index dfd592c26ba8..830143173c6f 100644 --- a/packages/proxy/lib/http/util/inject.ts +++ b/packages/proxy/lib/http/util/inject.ts @@ -3,7 +3,7 @@ import { getRunnerInjectionContents, getRunnerCrossOriginInjectionContents } fro import type { AutomationCookie } from '@packages/server/lib/automation/cookies' interface InjectionOpts { - useDefaultDocumentDomain: boolean + shouldInjectDocumentDomain: boolean } interface FullCrossOriginOpts { modifyObstructiveThirdPartyCode: boolean @@ -14,7 +14,7 @@ interface FullCrossOriginOpts { export function partial (domain, options: InjectionOpts) { let documentDomainInjection = `document.domain = '${domain}';` - if (options.useDefaultDocumentDomain) { + if (!options.shouldInjectDocumentDomain) { documentDomainInjection = '' } @@ -31,7 +31,7 @@ export function full (domain, options: InjectionOpts) { return getRunnerInjectionContents().then((contents) => { let documentDomainInjection = `document.domain = '${domain}';` - if (options.useDefaultDocumentDomain) { + if (!options.shouldInjectDocumentDomain) { documentDomainInjection = '' } @@ -50,7 +50,7 @@ export async function fullCrossOrigin (domain, options: InjectionOpts & FullCros let documentDomainInjection = `document.domain = '${domain}';` - if (options.useDefaultDocumentDomain) { + if (!options.shouldInjectDocumentDomain) { documentDomainInjection = '' } diff --git a/packages/proxy/lib/http/util/rewriter.ts b/packages/proxy/lib/http/util/rewriter.ts index 7409053af4dc..df6ab269b562 100644 --- a/packages/proxy/lib/http/util/rewriter.ts +++ b/packages/proxy/lib/http/util/rewriter.ts @@ -18,7 +18,7 @@ export type InjectionOpts = { wantsInjection: CypressWantsInjection wantsSecurityRemoved: any simulatedCookies: AutomationCookie[] - useDefaultDocumentDomain: boolean + shouldInjectDocumentDomain: boolean } const doctypeRe = /<\!doctype.*?>/i @@ -37,24 +37,24 @@ function getHtmlToInject (opts: InjectionOpts & SecurityOpts) { modifyObstructiveThirdPartyCode, modifyObstructiveCode, simulatedCookies, - useDefaultDocumentDomain, + shouldInjectDocumentDomain, } = opts switch (wantsInjection) { case 'full': return inject.full(domainName, { - useDefaultDocumentDomain, + shouldInjectDocumentDomain, }) case 'fullCrossOrigin': return inject.fullCrossOrigin(domainName, { modifyObstructiveThirdPartyCode, modifyObstructiveCode, simulatedCookies, - useDefaultDocumentDomain, + shouldInjectDocumentDomain, }) case 'partial': return inject.partial(domainName, { - useDefaultDocumentDomain, + shouldInjectDocumentDomain, }) default: return diff --git a/packages/proxy/test/unit/http/response-middleware.spec.ts b/packages/proxy/test/unit/http/response-middleware.spec.ts index a04ba4526b7e..15a279330bc8 100644 --- a/packages/proxy/test/unit/http/response-middleware.spec.ts +++ b/packages/proxy/test/unit/http/response-middleware.spec.ts @@ -1395,7 +1395,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': true, 'modifyObstructiveThirdPartyCode': true, - 'useDefaultDocumentDomain': false, + 'useDefaultDocumentForDomains': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], 'url': 'http://www.foobar.com:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1419,7 +1419,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': true, 'modifyObstructiveThirdPartyCode': false, - 'useDefaultDocumentDomain': false, + 'useDefaultDocumentForDomains': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], 'url': 'http://127.0.0.1:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1437,7 +1437,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: false, experimentalModifyObstructiveThirdPartyCode: false, - experimentalUseDefaultDocumentDomain: false, + experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], }, simulatedCookies: [], }) @@ -1451,7 +1451,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': false, 'modifyObstructiveThirdPartyCode': false, - 'useDefaultDocumentDomain': false, + 'useDefaultDocumentForDomains': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], 'url': 'http://www.foobar.com:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1489,7 +1489,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: true, experimentalModifyObstructiveThirdPartyCode: true, - experimentalUseDefaultDocumentDomain: false, + experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], }, remoteStates, debug: (formatter, ...args) => { diff --git a/packages/server/index.d.ts b/packages/server/index.d.ts index a43e23d523f1..1a694a9c5ead 100644 --- a/packages/server/index.d.ts +++ b/packages/server/index.d.ts @@ -22,7 +22,7 @@ export namespace CyServer { experimentalSourceRewriting: boolean modifyObstructiveCode: boolean experimentalModifyObstructiveThirdPartyCode: boolean - experimentalUseDefaultDocumentDomain: boolean + experimentalUseDefaultDocumentDomain: string[] /** * URL to Cypress's runner. */ diff --git a/packages/server/lib/controllers/files.js b/packages/server/lib/controllers/files.js index 9cee629f87d4..ca7f34646ea7 100644 --- a/packages/server/lib/controllers/files.js +++ b/packages/server/lib/controllers/files.js @@ -25,7 +25,12 @@ module.exports = { } debug('all files to send %o', _.map(allFilesToSend, 'relative')) - const documentSuperDomainIfExists = config.experimentalUseDefaultDocumentDomain ? undefined : remoteStates.getPrimary().domainName + + const documentSuperDomainIfExists = cors.shouldInjectDocumentDomain(req.proxiedUrl, { + useDefaultDocumentForDomains: config.experimentalUseDefaultDocumentDomain, + }) ? + remoteStates.getPrimary().domainName : + undefined const iframeOptions = { documentSuperDomainIfExists, @@ -41,7 +46,12 @@ module.exports = { handleCrossOriginIframe (req, res, config) { const iframePath = cwd('lib', 'html', 'spec-bridge-iframe.html') - const documentSuperDomainIfExists = config.experimentalUseDefaultDocumentDomain ? undefined : cors.getSuperDomain(req.proxiedUrl) + const documentSuperDomainIfExists = cors.shouldInjectDocumentDomain(req.proxiedUrl, { + useDefaultDocumentForDomains: config.experimentalUseDefaultDocumentDomain, + }) ? + cors.getSuperDomain(req.proxiedUrl) : + undefined + const origin = cors.getOrigin(req.proxiedUrl) const iframeOptions = { diff --git a/packages/server/lib/server-e2e.ts b/packages/server/lib/server-e2e.ts index 834e6b06336e..a9c9ff32a493 100644 --- a/packages/server/lib/server-e2e.ts +++ b/packages/server/lib/server-e2e.ts @@ -45,7 +45,8 @@ const isResponseHtml = function (contentType, responseBuffer) { export class ServerE2E extends ServerBase { private _urlResolver: Bluebird> | null - private useDefaultDocumentDomain: boolean = false + // the initialization of this variable is only precautionary as the actual config value is applied when the server is created + private useDefaultDocumentForDomains: string[] = ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] constructor () { super() @@ -62,7 +63,7 @@ export class ServerE2E extends ServerBase { const { port, fileServerFolder, socketIoRoute, baseUrl, experimentalUseDefaultDocumentDomain } = config this._server = this._createHttpServer(app) - this.useDefaultDocumentDomain = experimentalUseDefaultDocumentDomain + this.useDefaultDocumentForDomains = experimentalUseDefaultDocumentDomain const onError = (err) => { // if the server bombs before starting // and the err no is EADDRINUSE @@ -310,7 +311,7 @@ export class ServerE2E extends ServerBase { // of when to buffer and set the remote state if (isOk && details.isHtml) { const urlDoesNotMatchPolicyBasedOnDomain = options.hasAlreadyVisitedUrl - && !cors.urlMatchesPolicyBasedOnDomain(primaryRemoteState.origin, newUrl || '', { useDefaultDocumentDomain: this.useDefaultDocumentDomain }) + && !cors.urlMatchesPolicyBasedOnDomain(primaryRemoteState.origin, newUrl || '', { useDefaultDocumentForDomains: this.useDefaultDocumentForDomains }) || options.isFromSpecBridge if (!handlingLocalFile) { diff --git a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js index f2e7e32bb9ee..3c4de8773157 100644 --- a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js +++ b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js @@ -9,7 +9,7 @@ exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` │ Browser: FooBrowser 88 │ │ Specs: 1 found (experimental_use_default_document_domain.cy.ts) │ │ Searched: cypress/e2e/experimental_use_default_document_domain.cy.ts │ - │ Experiments: experimentalUseDefaultDocumentDomain=true │ + │ Experiments: experimentalUseDefaultDocumentDomain=*.foobar.com,*.force.com,*.google.com,goog… │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ diff --git a/system-tests/test/experimental_use_default_document_domain_spec.ts b/system-tests/test/experimental_use_default_document_domain_spec.ts index 5d0c5c11ae9e..5103db62031a 100644 --- a/system-tests/test/experimental_use_default_document_domain_spec.ts +++ b/system-tests/test/experimental_use_default_document_domain_spec.ts @@ -38,7 +38,7 @@ describe('e2e experimentalUseDefaultDocumentDomain=true', () => { expectedExitCode: 0, config: { retries: 0, - experimentalUseDefaultDocumentDomain: true, + experimentalUseDefaultDocumentDomain: ['*.foobar.com'], }, }) }) diff --git a/system-tests/test/webpack_dev_server_spec.ts b/system-tests/test/webpack_dev_server_spec.ts index 6d6c4cc433ea..98e2d43c9fd0 100644 --- a/system-tests/test/webpack_dev_server_spec.ts +++ b/system-tests/test/webpack_dev_server_spec.ts @@ -1,14 +1,48 @@ import systemTests from '../lib/system-tests' +import Fixtures from '../lib/fixtures' +import path from 'path' +import globby from 'globby' +import { escapeRegExp } from 'lodash' describe('@cypress/webpack-dev-server', function () { systemTests.setup() + const wdsPath = Fixtures.projectPath('webpack-dev-server') + systemTests.it('successfully loads and runs all specs', { project: 'webpack-dev-server', testingType: 'component', spec: 'src/**/*', browser: 'chrome', expectedExitCode: 0, + onRun: async (exec) => { + // We do not expect any failures in this suite, but we need to check that we actually ran + // the tests that we expected to run to validate that WDS is properly parsing & loading + // tests with special filepaths + + const { stdout } = await exec() + + // Find all specs that should have been run as part of this system test + // by scanning filesystem using spec pattern + const files = await globby(path.join(wdsPath, 'src', '**', '*')) + + // sanity check that we actually found some spec files + expect(files).to.have.length.greaterThan(0) + + files.forEach((fileName) => { + // Parse out the subpath under 'src' that we expect to appear in the output results table + const expectedFileName = fileName + .replace(path.join(wdsPath, 'src'), '') + .replace(/^\/|\\/, '') // Remove leading path separator if one exists + + // Pattern to match final output table entry + // Should include checkmark, filename, # of tests (1), and # of passes (1) + // ✔ [...bar].cy.js 17ms 1 1 + const expectedPattern = new RegExp(`✔\\s+${escapeRegExp(expectedFileName)}\\s+\\d+ms\\s+1\\s+1`) + + expect(stdout).to.match(expectedPattern) + }) + }, }) systemTests.it('successfully loads and runs all specs with typescript config', { diff --git a/yarn.lock b/yarn.lock index 5baedd9f4356..cc86db508f44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23840,7 +23840,7 @@ minimatch@0.3: lru-cache "2" sigmund "~1.0.0" -"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1: +"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== From 6538e130e107cae45a5f58f599e8ea23874ecba8 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 15:36:03 -0500 Subject: [PATCH 15/35] fix: fix server unit and integration tests. integration tests should no longer use google to test against injection as we do not inject document.domain on google domains --- .../unit/http/response-middleware.spec.ts | 6 +- .../test/integration/http_requests_spec.js | 62 +++++++++---------- .../fixtures/server/expected_head_inject.html | 2 +- .../server/expected_no_head_tag_inject.html | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/proxy/test/unit/http/response-middleware.spec.ts b/packages/proxy/test/unit/http/response-middleware.spec.ts index 15a279330bc8..ae1bd035f718 100644 --- a/packages/proxy/test/unit/http/response-middleware.spec.ts +++ b/packages/proxy/test/unit/http/response-middleware.spec.ts @@ -1395,7 +1395,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': true, 'modifyObstructiveThirdPartyCode': true, - 'useDefaultDocumentForDomains': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + 'shouldInjectDocumentDomain': true, 'url': 'http://www.foobar.com:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1419,7 +1419,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': true, 'modifyObstructiveThirdPartyCode': false, - 'useDefaultDocumentForDomains': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + 'shouldInjectDocumentDomain': true, 'url': 'http://127.0.0.1:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', @@ -1451,7 +1451,7 @@ describe('http/response-middleware', function () { 'isNotJavascript': true, 'modifyObstructiveCode': false, 'modifyObstructiveThirdPartyCode': false, - 'useDefaultDocumentForDomains': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + 'shouldInjectDocumentDomain': true, 'url': 'http://www.foobar.com:3501/primary-origin.html', 'useAstSourceRewriting': undefined, 'wantsInjection': 'full', diff --git a/packages/server/test/integration/http_requests_spec.js b/packages/server/test/integration/http_requests_spec.js index 9a202258d16c..f42b567b1418 100644 --- a/packages/server/test/integration/http_requests_spec.js +++ b/packages/server/test/integration/http_requests_spec.js @@ -2199,7 +2199,7 @@ describe('Routes', () => { context('content injection', () => { beforeEach(function () { - return this.setup('http://www.google.com') + return this.setup('http://www.cypress.io') }) it('injects when head has attributes', async function () { @@ -2212,7 +2212,7 @@ describe('Routes', () => { const injection = await getRunnerInjectionContents() const contents = removeWhitespace(Fixtures.get('server/expected_head_inject.html').replace('{{injection}}', injection)) const res = await this.rp({ - url: 'http://www.google.com/bar', + url: 'http://www.cypress.io/bar', headers: { 'Cookie': '__cypress.initial=true', }, @@ -2234,7 +2234,7 @@ describe('Routes', () => { const contents = removeWhitespace(Fixtures.get('server/expected_no_head_tag_inject.html').replace('{{injection}}', injection)) const res = await this.rp({ - url: 'http://www.google.com/bar', + url: 'http://www.cypress.io/bar', headers: { 'Cookie': '__cypress.initial=true', }, @@ -2253,7 +2253,7 @@ describe('Routes', () => { }) return this.rp({ - url: 'http://www.google.com/bar', + url: 'http://www.cypress.io/bar', headers: { 'Cookie': '__cypress.initial=true', }, @@ -2261,7 +2261,7 @@ describe('Routes', () => { .then((res) => { expect(res.statusCode).to.eq(200) - expect(res.body).to.include(' hello from bar! ') + expect(res.body).to.eq(' hello from bar! ') }) }) @@ -2397,7 +2397,7 @@ describe('Routes', () => { .get('/bar') .reply(302, undefined, { // redirect us to google.com! - 'Location': 'http://www.google.com/foo', + 'Location': 'http://www.cypress.io/foo', }) nock(this.server.remoteStates.current().origin) @@ -2407,14 +2407,14 @@ describe('Routes', () => { }) return this.rp({ - url: 'http://www.google.com/bar', + url: 'http://www.cypress.io/bar', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(302) - expect(res.headers['location']).to.eq('http://www.google.com/foo') + expect(res.headers['location']).to.eq('http://www.cypress.io/foo') expect(res.headers['set-cookie']).to.match(/initial=true/) return this.rp(res.headers['location']) @@ -2437,7 +2437,7 @@ describe('Routes', () => { }) return this.rp({ - url: 'http://www.google.com/elements.html', + url: 'http://www.cypress.io/elements.html', headers: { 'Cookie': '__cypress.initial=true', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', @@ -2446,7 +2446,7 @@ describe('Routes', () => { .then((res) => { expect(res.statusCode).to.eq(200) - expect(res.body).to.include('document.domain = \'google.com\';') + expect(res.body).to.include('document.domain = \'cypress.io\';') }) }) @@ -2479,7 +2479,7 @@ describe('Routes', () => { }) return this.rp({ - url: 'http://www.google.com/bar', + url: 'http://www.cypress.io/bar', headers: { 'Cookie': '__cypress.initial=false', }, @@ -2534,10 +2534,10 @@ describe('Routes', () => { }) it('injects even on 5xx responses', function () { - return this.setup('https://www.google.com') + return this.setup('https://www.cypress.io') .then(() => { this.server.onRequest((req, res) => { - return nock('https://www.google.com') + return nock('https://www.cypress.io') .get('/') .reply(500, 'google', { 'Content-Type': 'text/html', @@ -2545,7 +2545,7 @@ describe('Routes', () => { }) return this.rp({ - url: 'https://www.google.com/', + url: 'https://www.cypress.io/', headers: { 'Accept': 'text/html, application/xhtml+xml, */*', }, @@ -2553,7 +2553,7 @@ describe('Routes', () => { .then((res) => { expect(res.statusCode).to.eq(500) - expect(res.body).to.include('document.domain = \'google.com\'') + expect(res.body).to.include('document.domain = \'cypress.io\'') }) }) }) @@ -2624,7 +2624,7 @@ describe('Routes', () => { }) return this.rp({ - url: 'http://www.google.com/iframe', + url: 'http://www.cypress.io/iframe', headers: { 'Cookie': '__cypress.initial=false', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', @@ -2635,19 +2635,19 @@ describe('Routes', () => { const body = cleanResponseBody(res.body) - expect(body).to.eq(' ') + expect(body).to.eq(' ') }) }) it('does not inject document.domain on matching super domains but different subdomain - when the domain is set to strict same origin (google)', function () { - nock('http://mail.google.com') + nock('http://www.google.com') .get('/iframe') .reply(200, '', { 'Content-Type': 'text/html', }) return this.rp({ - url: 'http://mail.google.com/iframe', + url: 'http://www.google.com/iframe', headers: { 'Cookie': '__cypress.initial=false', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', @@ -2694,7 +2694,7 @@ describe('Routes', () => { }) return this.rp({ - url: 'http://www.google.com/json', + url: 'http://www.cypress.io/json', json: true, headers: { 'Cookie': '__cypress.initial=false', @@ -2734,7 +2734,7 @@ describe('Routes', () => { .reply(200, { foo: 'bar' }) return this.rp({ - url: 'http://www.google.com/json', + url: 'http://www.cypress.io/json', headers: { 'Cookie': '__cypress.initial=true', 'Accept': 'application/json', @@ -2757,7 +2757,7 @@ describe('Routes', () => { }) return this.rp({ - url: 'http://www.google.com/iframe', + url: 'http://www.cypress.io/iframe', headers: { 'Cookie': '__cypress.initial=false', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', @@ -2788,7 +2788,7 @@ describe('Routes', () => { headers['Accept'] = type return this.rp({ - url: 'http://www.google.com/iframe', + url: 'http://www.cypress.io/iframe', headers, }) .then((res) => { diff --git a/packages/server/test/support/fixtures/server/expected_head_inject.html b/packages/server/test/support/fixtures/server/expected_head_inject.html index 7574cf2b730f..7848f4547c9b 100644 --- a/packages/server/test/support/fixtures/server/expected_head_inject.html +++ b/packages/server/test/support/fixtures/server/expected_head_inject.html @@ -1,7 +1,7 @@ diff --git a/packages/server/test/support/fixtures/server/expected_no_head_tag_inject.html b/packages/server/test/support/fixtures/server/expected_no_head_tag_inject.html index c825225cd8c8..226da67811da 100644 --- a/packages/server/test/support/fixtures/server/expected_no_head_tag_inject.html +++ b/packages/server/test/support/fixtures/server/expected_no_head_tag_inject.html @@ -1,7 +1,7 @@ From d07248ce62ebf2c2a211f38f27b383593a9ebd11 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 15:36:35 -0500 Subject: [PATCH 16/35] run ci From 3ec938fbd1889d35bd2490c6a48a0062eda9da3e Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 29 Dec 2022 22:00:30 -0500 Subject: [PATCH 17/35] run ci From 55b947c7ca83c0760c969dd76b09968f1c10018b Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 30 Dec 2022 09:55:34 -0500 Subject: [PATCH 18/35] fix: server integration tests where google documents are expected to receive document.domain injection. Kept test same by changing URL --- .../server/test/integration/server_spec.js | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/server/test/integration/server_spec.js b/packages/server/test/integration/server_spec.js index ae39a68996fe..06bd9ff986c1 100644 --- a/packages/server/test/integration/server_spec.js +++ b/packages/server/test/integration/server_spec.js @@ -823,7 +823,7 @@ describe('Server', () => { }) it('can serve non 2xx status code requests when option set', function () { - nock('http://google.com') + nock('http://cypress.io') .matchHeader('user-agent', 'foobarbaz') .matchHeader('accept', 'text/html,*/*') .get('/foo') @@ -837,29 +837,29 @@ describe('Server', () => { headers['user-agent'] = 'foobarbaz' - return this.server._onResolveUrl('http://google.com/foo', headers, this.automationRequest, { failOnStatusCode: false }) + return this.server._onResolveUrl('http://cypress.io/foo', headers, this.automationRequest, { failOnStatusCode: false }) .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, isPrimarySuperDomainOrigin: true, isHtml: true, contentType: 'text/html', - url: 'http://google.com/foo', - originalUrl: 'http://google.com/foo', + url: 'http://cypress.io/foo', + originalUrl: 'http://cypress.io/foo', status: 404, statusText: 'Not Found', redirects: [], cookies: [], }) }).then(() => { - return this.rp('http://google.com/foo') + return this.rp('http://cypress.io/foo') .then((res) => { expect(res.statusCode).to.eq(404) expect(res.headers['set-cookie']).not.to.match(/initial=;/) expect(res.headers['x-foo-bar']).to.eq('true') expect(res.headers['cache-control']).to.eq('no-cache, no-store, must-revalidate') expect(res.body).to.include('content') - expect(res.body).to.include('document.domain = \'google.com\'') + expect(res.body).to.include('document.domain = \'cypress.io\'') expect(res.body).to.include('.action("app:window:before:load",window)') expect(res.body).to.include('content') @@ -1132,7 +1132,7 @@ describe('Server', () => { }) it('can go from file -> http -> file', function () { - nock('http://www.google.com') + nock('http://www.cypress.io') .get('/') .reply(200, 'content page', { 'Content-Type': 'text/html', @@ -1159,35 +1159,35 @@ describe('Server', () => { expect(res.statusCode).to.eq(200) }) }).then(() => { - return this.server._onResolveUrl('http://www.google.com/', {}, this.automationRequest) + return this.server._onResolveUrl('http://www.cypress.io/', {}, this.automationRequest) }).then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, isPrimarySuperDomainOrigin: true, isHtml: true, contentType: 'text/html', - url: 'http://www.google.com/', - originalUrl: 'http://www.google.com/', + url: 'http://www.cypress.io/', + originalUrl: 'http://www.cypress.io/', status: 200, statusText: 'OK', redirects: [], cookies: [], }) }).then(() => { - return this.rp('http://www.google.com/') + return this.rp('http://www.cypress.io/') .then((res) => { expect(res.statusCode).to.eq(200) }) }).then(() => { expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, - origin: 'http://www.google.com', + origin: 'http://www.cypress.io', strategy: 'http', - domainName: 'google.com', + domainName: 'cypress.io', fileServer: null, props: { - domain: 'google', - tld: 'com', + domain: 'cypress', + tld: 'io', port: '80', subdomain: 'www', protocol: 'http:', @@ -1228,7 +1228,7 @@ describe('Server', () => { }) it('can go from http -> file -> http', function () { - nock('http://www.google.com') + nock('http://www.cypress.io') .get('/') .reply(200, 'google', { 'Content-Type': 'text/html', @@ -1238,22 +1238,22 @@ describe('Server', () => { 'Content-Type': 'text/html', }) - return this.server._onResolveUrl('http://www.google.com/', {}, this.automationRequest) + return this.server._onResolveUrl('http://www.cypress.io/', {}, this.automationRequest) .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, isPrimarySuperDomainOrigin: true, isHtml: true, contentType: 'text/html', - url: 'http://www.google.com/', - originalUrl: 'http://www.google.com/', + url: 'http://www.cypress.io/', + originalUrl: 'http://www.cypress.io/', status: 200, statusText: 'OK', redirects: [], cookies: [], }) }).then(() => { - return this.rp('http://www.google.com/') + return this.rp('http://www.cypress.io/') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('document.domain') @@ -1265,7 +1265,7 @@ describe('Server', () => { }).then(() => { expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, - origin: 'http://www.google.com', + origin: 'http://www.cypress.io', strategy: 'http', domainName: 'google.com', fileServer: null, @@ -1313,22 +1313,22 @@ describe('Server', () => { props: null, }) }).then(() => { - return this.server._onResolveUrl('http://www.google.com/', {}, this.automationRequest) + return this.server._onResolveUrl('http://www.cypress.io/', {}, this.automationRequest) .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, isPrimarySuperDomainOrigin: true, isHtml: true, contentType: 'text/html', - url: 'http://www.google.com/', - originalUrl: 'http://www.google.com/', + url: 'http://www.cypress.io/', + originalUrl: 'http://www.cypress.io/', status: 200, statusText: 'OK', redirects: [], cookies: [], }) }).then(() => { - return this.rp('http://www.google.com/') + return this.rp('http://www.cypress.io/') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('document.domain') @@ -1340,7 +1340,7 @@ describe('Server', () => { }).then(() => { expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, - origin: 'http://www.google.com', + origin: 'http://www.cypress.io', strategy: 'http', domainName: 'google.com', fileServer: null, From b261683420a914a5f8956c6b41c060893574155a Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 30 Dec 2022 09:56:04 -0500 Subject: [PATCH 19/35] run ci From 483e2cde8686b953055b065bcdbe8749d5bcb0e0 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 30 Dec 2022 11:11:33 -0500 Subject: [PATCH 20/35] fix: update server test with mssing unupdated assertions --- .../server/test/integration/server_spec.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/server/test/integration/server_spec.js b/packages/server/test/integration/server_spec.js index 06bd9ff986c1..98fc563543ce 100644 --- a/packages/server/test/integration/server_spec.js +++ b/packages/server/test/integration/server_spec.js @@ -1230,11 +1230,11 @@ describe('Server', () => { it('can go from http -> file -> http', function () { nock('http://www.cypress.io') .get('/') - .reply(200, 'google', { + .reply(200, 'cypress', { 'Content-Type': 'text/html', }) .get('/') - .reply(200, 'google', { + .reply(200, 'cypress', { 'Content-Type': 'text/html', }) @@ -1257,21 +1257,21 @@ describe('Server', () => { .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('document.domain') - expect(res.body).to.include('google.com') + expect(res.body).to.include('cypress.io') expect(res.body).to.include('.action("app:window:before:load",window)') - expect(res.body).to.include('google') + expect(res.body).to.include('cypress') }) }).then(() => { expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, origin: 'http://www.cypress.io', strategy: 'http', - domainName: 'google.com', + domainName: 'cypress.io', fileServer: null, props: { - domain: 'google', - tld: 'com', + domain: 'cypress', + tld: 'io', port: '80', subdomain: 'www', protocol: 'http:', @@ -1332,21 +1332,21 @@ describe('Server', () => { .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('document.domain') - expect(res.body).to.include('google.com') + expect(res.body).to.include('cypress.io') expect(res.body).to.include('.action("app:window:before:load",window)') - expect(res.body).to.include('google') + expect(res.body).to.include('cypress') }) }).then(() => { expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, origin: 'http://www.cypress.io', strategy: 'http', - domainName: 'google.com', + domainName: 'cypress.io', fileServer: null, props: { - domain: 'google', - tld: 'com', + domain: 'cypress', + tld: 'io', port: '80', subdomain: 'www', protocol: 'http:', From d63e526709cee1cf9158a08e1a4ed44130d78d97 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 30 Dec 2022 11:11:42 -0500 Subject: [PATCH 21/35] run ci From 04c3ba156af7af54890c5c38f10a941851451c77 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 30 Dec 2022 14:29:05 -0500 Subject: [PATCH 22/35] fix: turn off experimental flag by default while recommending sane defaults to users to configure --- cli/types/cypress.d.ts | 4 +- .../config/__snapshots__/index.spec.ts.js | 4 +- packages/config/src/options.ts | 4 +- packages/config/test/project/utils.spec.ts | 4 +- packages/network/lib/cors.ts | 46 +++++++++++++++---- packages/network/test/unit/cors_spec.ts | 40 +++++++--------- .../unit/http/response-middleware.spec.ts | 4 +- packages/server/index.d.ts | 2 +- packages/server/lib/server-e2e.ts | 2 +- ...tal_use_default_document_domain_spec.ts.js | 2 +- 10 files changed, 65 insertions(+), 47 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index df2993f1fd08..00aef5d028c8 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3022,9 +3022,9 @@ declare namespace Cypress { * strings/string globs, and applies defaults for known domains where document.domain is known * to be an issue. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/domain - * @default {array} ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] + * @default null */ - experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] + experimentalUseDefaultDocumentDomain: string[] | null /** * Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm. * @default false diff --git a/packages/config/__snapshots__/index.spec.ts.js b/packages/config/__snapshots__/index.spec.ts.js index 330551fca3c0..fcec0ffc7260 100644 --- a/packages/config/__snapshots__/index.spec.ts.js +++ b/packages/config/__snapshots__/index.spec.ts.js @@ -38,7 +38,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, - 'experimentalUseDefaultDocumentDomain': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + 'experimentalUseDefaultDocumentDomain': null, 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, @@ -124,7 +124,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, - 'experimentalUseDefaultDocumentDomain': ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + 'experimentalUseDefaultDocumentDomain': null, 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index 990c3718d2f9..5a68a22ef33d 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -217,8 +217,8 @@ const driverConfigOptions: Array = [ requireRestartOnChange: 'server', }, { name: 'experimentalUseDefaultDocumentDomain', - defaultValue: () => ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], - validation: (_: string, value: any) => validate.isArrayOfStrings(value), + defaultValue: null, + validation: (_: string, value: any) => validate.isArrayOfStrings(value) || value === null, isExperimental: true, requireRestartOnChange: 'server', }, { diff --git a/packages/config/test/project/utils.spec.ts b/packages/config/test/project/utils.spec.ts index 4d70b77e19c2..ddbb199e9827 100644 --- a/packages/config/test/project/utils.spec.ts +++ b/packages/config/test/project/utils.spec.ts @@ -1052,7 +1052,7 @@ describe('config/src/project/utils', () => { env: {}, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, - experimentalUseDefaultDocumentDomain: { value: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], from: 'default' }, + experimentalUseDefaultDocumentDomain: { value: null, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, @@ -1148,7 +1148,7 @@ describe('config/src/project/utils', () => { downloadsFolder: { value: 'cypress/downloads', from: 'default' }, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, - experimentalUseDefaultDocumentDomain: { value: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], from: 'default' }, + experimentalUseDefaultDocumentDomain: { value: null, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index 0df29896238f..102bfd8330d2 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -12,6 +12,10 @@ const debug = debugModule('cypress:network:cors') // match IP addresses or anything following the last . const customTldsRe = /(^[\d\.]+$|\.[^\.]+$)/ +// TODO: if experimentalUseDefaultDocumentDomain plans to go GA, we can likely lump this strictSameOriginDomains +// into that config option by default. @see https://github.com/cypress-io/cypress/issues/25317 +const strictSameOriginDomains = Object.freeze(['google.com']) + export function getSuperDomain (url) { const parsed = parseUrlIntoHostProtocolDomainTldPort(url) @@ -157,20 +161,37 @@ export const urlSameSiteMatch = (frameUrl: string, topUrl: string): boolean => { }) } +/** + * + * @param url - the url to check the policy against. + * @param arrayOfStringOrGlobPatterns - an array of url strings or globs to match against + * @returns {boolean} - whether or not a match was found + */ +const doesUrlHostnameMatchGlobArray = (url: string, arrayOfStringOrGlobPatterns: string[]): boolean => { + let { hostname } = uri.parse(url) + + return !!arrayOfStringOrGlobPatterns.find((globPattern) => { + return minimatch(hostname || '', globPattern) + }) +} + /** * Returns the policy that will be used for the specified url. * @param url - the url to check the policy against. * @returns a Policy string. */ export const policyForDomain = (url: string, opts?: { - useDefaultDocumentForDomains: string[] + useDefaultDocumentForDomains: string[] | null }): Policy => { - let { hostname } = uri.parse(url) - const hasDefaultDomainMatch = !!opts?.useDefaultDocumentForDomains.find((globPattern) => { - return minimatch(hostname || '', globPattern) - }) + const obj = parseUrlIntoHostProtocolDomainTldPort(url) + let shouldUseSameOriginPolicy = strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`) + + if (!shouldUseSameOriginPolicy && _.isArray(opts?.useDefaultDocumentForDomains)) { + // if the strict same origins matches came up false, we should check the user provided config value for useDefaultDocumentForDomains, if one exists + shouldUseSameOriginPolicy = doesUrlHostnameMatchGlobArray(url, opts?.useDefaultDocumentForDomains as string[]) + } - return hasDefaultDomainMatch ? + return shouldUseSameOriginPolicy ? 'same-origin' : 'same-super-domain-origin' } @@ -178,9 +199,14 @@ export const policyForDomain = (url: string, opts?: { export const shouldInjectDocumentDomain = (url: string, opts?: { useDefaultDocumentForDomains: string[] }) => { - return policyForDomain(url, { - useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [], - }) === 'same-super-domain-origin' + // When determining if we want to injection document domain, + // We need to make sure the experimentalUseDefaultDocumentDomain feature flag is off. + // If on, we need to make sure the glob pattern doesn't exist in the array so we cover possible intersections (google). + if (_.isArray(opts?.useDefaultDocumentForDomains)) { + return doesUrlHostnameMatchGlobArray(url, opts?.useDefaultDocumentForDomains as string[]) + } + + return true } /** @@ -192,7 +218,7 @@ export const shouldInjectDocumentDomain = (url: string, opts?: { * @returns boolean, true if matching, false if not. */ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, opts?: { - useDefaultDocumentForDomains: string[] + useDefaultDocumentForDomains: string[] | null }): boolean => { return urlMatchesPolicy({ policy: policyForDomain(frameUrl, { diff --git a/packages/network/test/unit/cors_spec.ts b/packages/network/test/unit/cors_spec.ts index 33caa9c353b2..64a5fa6d6220 100644 --- a/packages/network/test/unit/cors_spec.ts +++ b/packages/network/test/unit/cors_spec.ts @@ -2,8 +2,6 @@ import { cors } from '../../lib' import { expect } from 'chai' describe('lib/cors', () => { - const defaultSameOriginPolicyUrlGlobs = ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] - context('.parseUrlIntoHostProtocolDomainTldPort', () => { const expectUrlToBeParsedCorrectly = (url, obj) => { expect(cors.parseUrlIntoHostProtocolDomainTldPort(url)).to.deep.eq(obj) @@ -325,15 +323,11 @@ describe('lib/cors', () => { context('.urlMatchesPolicyBasedOnDomain', () => { const assertsUrlsAreNotAPolicyMatch = (url1, url2) => { - expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2, { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, - })).to.be.false + expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2)).to.be.false } const assertsUrlsAreAPolicyOriginMatch = (url1, url2) => { - expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2, { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, - })).to.be.true + expect(cors.urlMatchesPolicyBasedOnDomain(url1, url2)).to.be.true } describe('domain + subdomain', () => { @@ -460,15 +454,11 @@ describe('lib/cors', () => { context('.urlMatchesPolicyBasedOnDomainProps', () => { const assertsUrlsAreNotAPolicyMatch = (url1, props) => { - expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props, { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, - })).to.be.false + expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props)).to.be.false } const assertsUrlsAreAPolicyOriginMatch = (url1, props) => { - expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props, { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, - })).to.be.true + expect(cors.urlMatchesPolicyBasedOnDomainProps(url1, props)).to.be.true } describe('domain + subdomain', () => { @@ -669,16 +659,18 @@ describe('lib/cors', () => { }) context('.policyForDomain', () => { - context('returns "same-origin" for google domains', () => { + const recommendedSameOriginPolicyUrlGlobs = ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] + + context('returns "same-origin" for google domains', () => { it('accounts.google.com', () => { expect(cors.policyForDomain('https://accounts.google.com', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('www.google.com', () => { expect(cors.policyForDomain('https://www.google.com', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) }) @@ -686,25 +678,25 @@ describe('lib/cors', () => { context('returns "same-origin" for salesforce domains', () => { it('https://the-host.develop.lightning.force.com', () => { expect(cors.policyForDomain('https://the-host.develop.lightning.force.com', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('https://the-host.develop.my.salesforce.com', () => { expect(cors.policyForDomain('https://the-host.develop.my.salesforce.com', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('https://the-host.develop.file.force.com', () => { expect(cors.policyForDomain('https://the-host.develop.file.force.com', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('https://the-host.develop.my.salesforce.com', () => { expect(cors.policyForDomain('https://the-host.develop.my.salesforce.com', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) }) @@ -712,19 +704,19 @@ describe('lib/cors', () => { describe('returns "same-super-domain-origin" for non exception urls', () => { it('www.cypress.io', () => { expect(cors.policyForDomain('http://www.cypress.io', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-super-domain-origin') }) it('docs.cypress.io', () => { expect(cors.policyForDomain('http://docs.cypress.io', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-super-domain-origin') }) it('stackoverflow.com', () => { expect(cors.policyForDomain('https://stackoverflow.com', { - useDefaultDocumentForDomains: defaultSameOriginPolicyUrlGlobs, + useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-super-domain-origin') }) }) diff --git a/packages/proxy/test/unit/http/response-middleware.spec.ts b/packages/proxy/test/unit/http/response-middleware.spec.ts index ae1bd035f718..5a966a8f99ff 100644 --- a/packages/proxy/test/unit/http/response-middleware.spec.ts +++ b/packages/proxy/test/unit/http/response-middleware.spec.ts @@ -1437,7 +1437,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: false, experimentalModifyObstructiveThirdPartyCode: false, - experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + experimentalUseDefaultDocumentDomain: null, }, simulatedCookies: [], }) @@ -1489,7 +1489,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: true, experimentalModifyObstructiveThirdPartyCode: true, - experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'], + experimentalUseDefaultDocumentDomain: null, }, remoteStates, debug: (formatter, ...args) => { diff --git a/packages/server/index.d.ts b/packages/server/index.d.ts index 1a694a9c5ead..efcaad57f6b8 100644 --- a/packages/server/index.d.ts +++ b/packages/server/index.d.ts @@ -22,7 +22,7 @@ export namespace CyServer { experimentalSourceRewriting: boolean modifyObstructiveCode: boolean experimentalModifyObstructiveThirdPartyCode: boolean - experimentalUseDefaultDocumentDomain: string[] + experimentalUseDefaultDocumentDomain: string[] | null /** * URL to Cypress's runner. */ diff --git a/packages/server/lib/server-e2e.ts b/packages/server/lib/server-e2e.ts index a9c9ff32a493..d2820fe798f9 100644 --- a/packages/server/lib/server-e2e.ts +++ b/packages/server/lib/server-e2e.ts @@ -46,7 +46,7 @@ const isResponseHtml = function (contentType, responseBuffer) { export class ServerE2E extends ServerBase { private _urlResolver: Bluebird> | null // the initialization of this variable is only precautionary as the actual config value is applied when the server is created - private useDefaultDocumentForDomains: string[] = ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] + private useDefaultDocumentForDomains: string[] | null = null constructor () { super() diff --git a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js index 3c4de8773157..62230a95c0c4 100644 --- a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js +++ b/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js @@ -9,7 +9,7 @@ exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` │ Browser: FooBrowser 88 │ │ Specs: 1 found (experimental_use_default_document_domain.cy.ts) │ │ Searched: cypress/e2e/experimental_use_default_document_domain.cy.ts │ - │ Experiments: experimentalUseDefaultDocumentDomain=*.foobar.com,*.force.com,*.google.com,goog… │ + │ Experiments: experimentalUseDefaultDocumentDomain=*.foobar.com │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ From 2e9b3694e451248b7e802194212149f057e2bb0b Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 30 Dec 2022 14:29:18 -0500 Subject: [PATCH 23/35] run ci From 2cb1bd44a3c6ab0377aa85028c4eadf4b587df6b Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 30 Dec 2022 15:15:35 -0500 Subject: [PATCH 24/35] chore: fix typings [run ci] --- packages/network/lib/cors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index 102bfd8330d2..f3133c1ea1b3 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -197,7 +197,7 @@ export const policyForDomain = (url: string, opts?: { } export const shouldInjectDocumentDomain = (url: string, opts?: { - useDefaultDocumentForDomains: string[] + useDefaultDocumentForDomains: string[] | null }) => { // When determining if we want to injection document domain, // We need to make sure the experimentalUseDefaultDocumentDomain feature flag is off. From ef7701b8f3e8c936430494562d844d59eaf25cf9 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Tue, 3 Jan 2023 10:16:25 -0500 Subject: [PATCH 25/35] run ci From 671ea123ae542780176d6699c9ab5d18bd80510f Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Tue, 3 Jan 2023 13:28:09 -0500 Subject: [PATCH 26/35] chore: make experiment an e2e option only --- packages/config/src/options.ts | 11 +++++++++++ packages/errors/src/errors.ts | 13 +++++++++++++ .../errors/test/unit/visualSnapshotErrors_spec.ts | 6 ++++++ packages/graphql/schemas/schema.graphql | 1 + 4 files changed, 31 insertions(+) diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index 5a68a22ef33d..8e5e87f902f0 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -685,6 +685,12 @@ export const breakingRootOptions: Array = [ isWarning: false, testingTypes: ['e2e'], }, + { + name: 'experimentalUseDefaultDocumentDomain', + errorKey: 'EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY', + isWarning: false, + testingTypes: ['e2e'], + }, ] export const testingTypeBreakingOptions: { e2e: Array, component: Array } = { @@ -726,5 +732,10 @@ export const testingTypeBreakingOptions: { e2e: Array, component errorKey: 'EXPERIMENTAL_ORIGIN_DEPENDENCIES_E2E_ONLY', isWarning: false, }, + { + name: 'experimentalUseDefaultDocumentDomain', + errorKey: 'EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY', + isWarning: false, + }, ], } diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 605f5c069ff6..daa7613008d4 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -1185,6 +1185,19 @@ export const AllCypressErrors = { ${fmt.code(code)}` }, + EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY: () => { + const code = errPartial` + { + e2e: { + experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] + }, + }` + + return errTemplate`\ + The ${fmt.highlight(`experimentalUseDefaultDocumentDomain`)} experiment is currently only supported for End to End Testing and must be configured as an e2e testing type property: ${fmt.highlightSecondary(`e2e.experimentalUseDefaultDocumentDomain`)}. + + ${fmt.code(code)}` + }, FIREFOX_GC_INTERVAL_REMOVED: () => { return errTemplate`\ The ${fmt.highlight(`firefoxGcInterval`)} configuration option was removed in ${fmt.cypressVersion(`8.0.0`)}. It was introduced to work around a bug in Firefox 79 and below. diff --git a/packages/errors/test/unit/visualSnapshotErrors_spec.ts b/packages/errors/test/unit/visualSnapshotErrors_spec.ts index 217b82079057..57e53e441f42 100644 --- a/packages/errors/test/unit/visualSnapshotErrors_spec.ts +++ b/packages/errors/test/unit/visualSnapshotErrors_spec.ts @@ -1268,5 +1268,11 @@ describe('visual error templates', () => { default: [], } }, + + EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY: () => { + return { + default: [], + } + }, }) }) diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 1157242a77f6..fc3a10673dab 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -800,6 +800,7 @@ enum ErrorTypeEnum { EXPERIMENTAL_SINGLE_TAB_RUN_MODE EXPERIMENTAL_STUDIO_E2E_ONLY EXPERIMENTAL_STUDIO_REMOVED + EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY EXTENSION_NOT_LOADED FIREFOX_COULD_NOT_CONNECT FIREFOX_GC_INTERVAL_REMOVED From bd65043b126c04be0d95f5df522d1d78db557034 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Tue, 3 Jan 2023 13:28:29 -0500 Subject: [PATCH 27/35] run ci From dccf967ed936fefb790cda2516efa682bec6c2f5 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 4 Jan 2023 12:01:52 -0500 Subject: [PATCH 28/35] chore: address comments in code review --- cli/types/cypress.d.ts | 7 +++---- packages/config/src/options.ts | 2 +- packages/config/src/validation.ts | 10 +++++++++- packages/proxy/lib/http/request-middleware.ts | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 15afbdcc4936..a220acf5e735 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3020,12 +3020,11 @@ declare namespace Cypress { */ experimentalModifyObstructiveThirdPartyCode: boolean /** - * Disables setting document.domain to the document's super domain on injection. + * Disables setting document.domain to the application's super domain on injection. * This experiment is to be used for sites that do not work with setting document.domain - * due to cross-origin issues. Enabling this option no longer allows for default sub domain + * due to cross-origin issues. Enabling this option no longer allows for default subdomain * navigations, and will require the use of cy.origin(). This option takes an array of - * strings/string globs, and applies defaults for known domains where document.domain is known - * to be an issue. + * strings/string globs. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/domain * @default null */ diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index 8e5e87f902f0..4b3620edb5f5 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -218,7 +218,7 @@ const driverConfigOptions: Array = [ }, { name: 'experimentalUseDefaultDocumentDomain', defaultValue: null, - validation: (_: string, value: any) => validate.isArrayOfStrings(value) || value === null, + validation: validate.isNullOrArrayOfStrings, isExperimental: true, requireRestartOnChange: 'server', }, { diff --git a/packages/config/src/validation.ts b/packages/config/src/validation.ts index 92d2b28b7d6e..8f023db35515 100644 --- a/packages/config/src/validation.ts +++ b/packages/config/src/validation.ts @@ -33,7 +33,7 @@ const _isFullyQualifiedUrl = (value: any): ErrResult | boolean => { return _.isString(value) && /^https?\:\/\//.test(value) } -export const isArrayOfStrings = (value: any): ErrResult | boolean => { +const isArrayOfStrings = (value: any): ErrResult | boolean => { return _.isArray(value) && _.every(value, _.isString) } @@ -328,3 +328,11 @@ export function isStringOrArrayOfStrings (key: string, value: any): ErrResult | return errMsg(key, value, 'a string or an array of strings') } + +export function isNullOrArrayOfStrings (key: string, value: any): ErrResult | true { + if (_.isNull(value) || isArrayOfStrings(value)) { + return true + } + + return errMsg(key, value, 'an array of strings or null') +} diff --git a/packages/proxy/lib/http/request-middleware.ts b/packages/proxy/lib/http/request-middleware.ts index fc625221f987..ecd1c20a6dbc 100644 --- a/packages/proxy/lib/http/request-middleware.ts +++ b/packages/proxy/lib/http/request-middleware.ts @@ -163,7 +163,7 @@ const MaybeEndRequestWithBufferedResponse: RequestMiddleware = function () { if (buffer) { this.debug('ending request with buffered response') - // NOTE: Only inject fullCrossOrigin here the super domain origins do not match in order to keep parity with cypress application reloads + // NOTE: Only inject fullCrossOrigin here if the super domain origins do not match in order to keep parity with cypress application reloads this.res.wantsInjection = buffer.urlDoesNotMatchPolicyBasedOnDomain ? 'fullCrossOrigin' : 'full' return this.onResponse(buffer.response, buffer.stream) From 86bea9601018fe57646dd4e7dff55393dbd21d44 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 4 Jan 2023 12:28:39 -0500 Subject: [PATCH 29/35] chore: rename experimentalUseDefaultDocumentDomain to experimentalSkipDomainInjection --- cli/types/cypress.d.ts | 4 +-- .../config/__snapshots__/index.spec.ts.js | 6 ++--- packages/config/src/options.ts | 6 ++--- packages/config/test/project/utils.spec.ts | 4 +-- .../src/cy/commands/origin/validator.ts | 4 +-- packages/driver/src/cypress.ts | 2 +- packages/errors/src/errors.ts | 4 +-- packages/network/lib/cors.ts | 26 +++++++++---------- packages/network/test/unit/cors_spec.ts | 18 ++++++------- .../proxy/lib/http/response-middleware.ts | 10 +++---- .../unit/http/response-middleware.spec.ts | 4 +-- packages/server/index.d.ts | 2 +- packages/server/lib/controllers/files.js | 4 +-- packages/server/lib/experiments.ts | 4 +-- packages/server/lib/server-e2e.ts | 8 +++--- packages/types/src/config.ts | 2 +- ...rimental_skip_domain_injection_spec.ts.js} | 22 ++++++++-------- ... experimental_skip_domain_injection.cy.ts} | 6 ++--- ...xperimental_skip_domain_injection_spec.ts} | 6 ++--- 19 files changed, 71 insertions(+), 71 deletions(-) rename system-tests/__snapshots__/{experimental_use_default_document_domain_spec.ts.js => experimental_skip_domain_injection_spec.ts.js} (83%) rename system-tests/projects/e2e/cypress/e2e/{experimental_use_default_document_domain.cy.ts => experimental_skip_domain_injection.cy.ts} (88%) rename system-tests/test/{experimental_use_default_document_domain_spec.ts => experimental_skip_domain_injection_spec.ts} (82%) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index a220acf5e735..ab9bf59bd188 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3020,7 +3020,7 @@ declare namespace Cypress { */ experimentalModifyObstructiveThirdPartyCode: boolean /** - * Disables setting document.domain to the application's super domain on injection. + * Disables setting document.domain to the applications super domain on injection. * This experiment is to be used for sites that do not work with setting document.domain * due to cross-origin issues. Enabling this option no longer allows for default subdomain * navigations, and will require the use of cy.origin(). This option takes an array of @@ -3028,7 +3028,7 @@ declare namespace Cypress { * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/domain * @default null */ - experimentalUseDefaultDocumentDomain: string[] | null + experimentalSkipDomainInjection: string[] | null /** * Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm. * @default false diff --git a/packages/config/__snapshots__/index.spec.ts.js b/packages/config/__snapshots__/index.spec.ts.js index fcec0ffc7260..0f2daa44b22e 100644 --- a/packages/config/__snapshots__/index.spec.ts.js +++ b/packages/config/__snapshots__/index.spec.ts.js @@ -38,7 +38,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, - 'experimentalUseDefaultDocumentDomain': null, + 'experimentalSkipDomainInjection': null, 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, @@ -124,7 +124,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f 'experimentalInteractiveRunEvents': false, 'experimentalRunAllSpecs': false, 'experimentalModifyObstructiveThirdPartyCode': false, - 'experimentalUseDefaultDocumentDomain': null, + 'experimentalSkipDomainInjection': null, 'experimentalOriginDependencies': false, 'experimentalSourceRewriting': false, 'experimentalSingleTabRunMode': false, @@ -206,7 +206,7 @@ exports['config/src/index .getPublicConfigKeys returns list of public config key 'experimentalInteractiveRunEvents', 'experimentalRunAllSpecs', 'experimentalModifyObstructiveThirdPartyCode', - 'experimentalUseDefaultDocumentDomain', + 'experimentalSkipDomainInjection', 'experimentalOriginDependencies', 'experimentalSourceRewriting', 'experimentalSingleTabRunMode', diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index 4b3620edb5f5..664ec35040fd 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -216,7 +216,7 @@ const driverConfigOptions: Array = [ isExperimental: true, requireRestartOnChange: 'server', }, { - name: 'experimentalUseDefaultDocumentDomain', + name: 'experimentalSkipDomainInjection', defaultValue: null, validation: validate.isNullOrArrayOfStrings, isExperimental: true, @@ -686,7 +686,7 @@ export const breakingRootOptions: Array = [ testingTypes: ['e2e'], }, { - name: 'experimentalUseDefaultDocumentDomain', + name: 'experimentalSkipDomainInjection', errorKey: 'EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY', isWarning: false, testingTypes: ['e2e'], @@ -733,7 +733,7 @@ export const testingTypeBreakingOptions: { e2e: Array, component isWarning: false, }, { - name: 'experimentalUseDefaultDocumentDomain', + name: 'experimentalSkipDomainInjection', errorKey: 'EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY', isWarning: false, }, diff --git a/packages/config/test/project/utils.spec.ts b/packages/config/test/project/utils.spec.ts index ddbb199e9827..82d3d128bc3e 100644 --- a/packages/config/test/project/utils.spec.ts +++ b/packages/config/test/project/utils.spec.ts @@ -1052,7 +1052,7 @@ describe('config/src/project/utils', () => { env: {}, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, - experimentalUseDefaultDocumentDomain: { value: null, from: 'default' }, + experimentalSkipDomainInjection: { value: null, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, @@ -1148,7 +1148,7 @@ describe('config/src/project/utils', () => { downloadsFolder: { value: 'cypress/downloads', from: 'default' }, execTimeout: { value: 60000, from: 'default' }, experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' }, - experimentalUseDefaultDocumentDomain: { value: null, from: 'default' }, + experimentalSkipDomainInjection: { value: null, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalOriginDependencies: { value: false, from: 'default' }, diff --git a/packages/driver/src/cy/commands/origin/validator.ts b/packages/driver/src/cy/commands/origin/validator.ts index 055d039bfc10..35bef1d09793 100644 --- a/packages/driver/src/cy/commands/origin/validator.ts +++ b/packages/driver/src/cy/commands/origin/validator.ts @@ -86,12 +86,12 @@ export class Validator { // Users would be better off not using cy.origin if the origin is part of the same super domain. if (cors.urlMatchesPolicyBasedOnDomain(originLocation.href, specHref, { - useDefaultDocumentForDomains: Cypress.config('experimentalUseDefaultDocumentDomain'), + skipDomainInjectionForDomains: Cypress.config('experimentalSkipDomainInjection'), })) { // this._isSameSuperDomainOriginWithExceptions({ originLocation, specLocation })) { const policy = cors.policyForDomain(originLocation.href, { - useDefaultDocumentForDomains: Cypress.config('experimentalUseDefaultDocumentDomain'), + skipDomainInjectionForDomains: Cypress.config('experimentalSkipDomainInjection'), }) $errUtils.throwErrByPath('origin.invalid_url_argument_same_origin', { diff --git a/packages/driver/src/cypress.ts b/packages/driver/src/cypress.ts index 1d942214e515..2d0ded980238 100644 --- a/packages/driver/src/cypress.ts +++ b/packages/driver/src/cypress.ts @@ -184,7 +184,7 @@ class $Cypress { // set domainName but allow us to turn // off this feature in testing const shouldInjectDocumentDomain = cors.shouldInjectDocumentDomain(window.location.origin, { - useDefaultDocumentForDomains: config.experimentalUseDefaultDocumentDomain, + skipDomainInjectionForDomains: config.experimentalSkipDomainInjection, }) if (domainName && config.testingType === 'e2e' && shouldInjectDocumentDomain) { diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index daa7613008d4..29ed50d0c0a3 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -1189,12 +1189,12 @@ export const AllCypressErrors = { const code = errPartial` { e2e: { - experimentalUseDefaultDocumentDomain: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] + experimentalSkipDomainInjection: ['*.salesforce.com', '*.force.com', '*.google.com', 'google.com'] }, }` return errTemplate`\ - The ${fmt.highlight(`experimentalUseDefaultDocumentDomain`)} experiment is currently only supported for End to End Testing and must be configured as an e2e testing type property: ${fmt.highlightSecondary(`e2e.experimentalUseDefaultDocumentDomain`)}. + The ${fmt.highlight(`experimentalSkipDomainInjection`)} experiment is currently only supported for End to End Testing and must be configured as an e2e testing type property: ${fmt.highlightSecondary(`e2e.experimentalSkipDomainInjection`)}. ${fmt.code(code)}` }, diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index f3133c1ea1b3..873808675ab3 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -12,7 +12,7 @@ const debug = debugModule('cypress:network:cors') // match IP addresses or anything following the last . const customTldsRe = /(^[\d\.]+$|\.[^\.]+$)/ -// TODO: if experimentalUseDefaultDocumentDomain plans to go GA, we can likely lump this strictSameOriginDomains +// TODO: if experimentalSkipDomainInjection plans to go GA, we can likely lump this strictSameOriginDomains // into that config option by default. @see https://github.com/cypress-io/cypress/issues/25317 const strictSameOriginDomains = Object.freeze(['google.com']) @@ -181,14 +181,14 @@ const doesUrlHostnameMatchGlobArray = (url: string, arrayOfStringOrGlobPatterns: * @returns a Policy string. */ export const policyForDomain = (url: string, opts?: { - useDefaultDocumentForDomains: string[] | null + skipDomainInjectionForDomains: string[] | null }): Policy => { const obj = parseUrlIntoHostProtocolDomainTldPort(url) let shouldUseSameOriginPolicy = strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`) - if (!shouldUseSameOriginPolicy && _.isArray(opts?.useDefaultDocumentForDomains)) { - // if the strict same origins matches came up false, we should check the user provided config value for useDefaultDocumentForDomains, if one exists - shouldUseSameOriginPolicy = doesUrlHostnameMatchGlobArray(url, opts?.useDefaultDocumentForDomains as string[]) + if (!shouldUseSameOriginPolicy && _.isArray(opts?.skipDomainInjectionForDomains)) { + // if the strict same origins matches came up false, we should check the user provided config value for skipDomainInjectionForDomains, if one exists + shouldUseSameOriginPolicy = doesUrlHostnameMatchGlobArray(url, opts?.skipDomainInjectionForDomains as string[]) } return shouldUseSameOriginPolicy ? @@ -197,13 +197,13 @@ export const policyForDomain = (url: string, opts?: { } export const shouldInjectDocumentDomain = (url: string, opts?: { - useDefaultDocumentForDomains: string[] | null + skipDomainInjectionForDomains: string[] | null }) => { // When determining if we want to injection document domain, - // We need to make sure the experimentalUseDefaultDocumentDomain feature flag is off. + // We need to make sure the experimentalSkipDomainInjection feature flag is off. // If on, we need to make sure the glob pattern doesn't exist in the array so we cover possible intersections (google). - if (_.isArray(opts?.useDefaultDocumentForDomains)) { - return doesUrlHostnameMatchGlobArray(url, opts?.useDefaultDocumentForDomains as string[]) + if (_.isArray(opts?.skipDomainInjectionForDomains)) { + return doesUrlHostnameMatchGlobArray(url, opts?.skipDomainInjectionForDomains as string[]) } return true @@ -218,11 +218,11 @@ export const shouldInjectDocumentDomain = (url: string, opts?: { * @returns boolean, true if matching, false if not. */ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, opts?: { - useDefaultDocumentForDomains: string[] | null + skipDomainInjectionForDomains: string[] | null }): boolean => { return urlMatchesPolicy({ policy: policyForDomain(frameUrl, { - useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [], + skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains || [], }), frameUrl, topUrl, @@ -238,10 +238,10 @@ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, * @returns boolean, true if matching, false if not. */ export const urlMatchesPolicyBasedOnDomainProps = (frameUrl: string, topProps: ParsedHostWithProtocolAndHost, opts?: { - useDefaultDocumentForDomains: string[] + skipDomainInjectionForDomains: string[] }): boolean => { const policy = policyForDomain(frameUrl, { - useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [], + skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains || [], }) return urlMatchesPolicyProps({ diff --git a/packages/network/test/unit/cors_spec.ts b/packages/network/test/unit/cors_spec.ts index 64a5fa6d6220..18ff5f84a62a 100644 --- a/packages/network/test/unit/cors_spec.ts +++ b/packages/network/test/unit/cors_spec.ts @@ -664,13 +664,13 @@ describe('lib/cors', () => { context('returns "same-origin" for google domains', () => { it('accounts.google.com', () => { expect(cors.policyForDomain('https://accounts.google.com', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('www.google.com', () => { expect(cors.policyForDomain('https://www.google.com', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) }) @@ -678,25 +678,25 @@ describe('lib/cors', () => { context('returns "same-origin" for salesforce domains', () => { it('https://the-host.develop.lightning.force.com', () => { expect(cors.policyForDomain('https://the-host.develop.lightning.force.com', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('https://the-host.develop.my.salesforce.com', () => { expect(cors.policyForDomain('https://the-host.develop.my.salesforce.com', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('https://the-host.develop.file.force.com', () => { expect(cors.policyForDomain('https://the-host.develop.file.force.com', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) it('https://the-host.develop.my.salesforce.com', () => { expect(cors.policyForDomain('https://the-host.develop.my.salesforce.com', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-origin') }) }) @@ -704,19 +704,19 @@ describe('lib/cors', () => { describe('returns "same-super-domain-origin" for non exception urls', () => { it('www.cypress.io', () => { expect(cors.policyForDomain('http://www.cypress.io', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-super-domain-origin') }) it('docs.cypress.io', () => { expect(cors.policyForDomain('http://docs.cypress.io', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-super-domain-origin') }) it('stackoverflow.com', () => { expect(cors.policyForDomain('https://stackoverflow.com', { - useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs, + skipDomainInjectionForDomains: recommendedSameOriginPolicyUrlGlobs, })).to.equal('same-super-domain-origin') }) }) diff --git a/packages/proxy/lib/http/response-middleware.ts b/packages/proxy/lib/http/response-middleware.ts index e0792310d142..918764cbf59d 100644 --- a/packages/proxy/lib/http/response-middleware.ts +++ b/packages/proxy/lib/http/response-middleware.ts @@ -55,10 +55,10 @@ function getNodeCharsetFromResponse (headers: IncomingHttpHeaders, body: Buffer, return 'latin1' } -function reqMatchesPolicyBasedOnDomain (req: CypressIncomingRequest, remoteState, useDefaultDocumentForDomains) { +function reqMatchesPolicyBasedOnDomain (req: CypressIncomingRequest, remoteState, skipDomainInjectionForDomains) { if (remoteState.strategy === 'http') { return cors.urlMatchesPolicyBasedOnDomainProps(req.proxiedUrl, remoteState.props, { - useDefaultDocumentForDomains, + skipDomainInjectionForDomains, }) } @@ -252,7 +252,7 @@ const SetInjectionLevel: ResponseMiddleware = function () { this.debug('determine injection') - const isReqMatchSuperDomainOrigin = reqMatchesPolicyBasedOnDomain(this.req, this.remoteStates.current(), this.config.experimentalUseDefaultDocumentDomain) + const isReqMatchSuperDomainOrigin = reqMatchesPolicyBasedOnDomain(this.req, this.remoteStates.current(), this.config.experimentalSkipDomainInjection) const getInjectionLevel = () => { if (this.incomingRes.headers['x-cypress-file-server-error'] && !this.res.isInitial) { this.debug('- partial injection (x-cypress-file-server-error)') @@ -261,7 +261,7 @@ const SetInjectionLevel: ResponseMiddleware = function () { } // NOTE: Only inject fullCrossOrigin if the super domain origins do not match in order to keep parity with cypress application reloads - const urlDoesNotMatchPolicyBasedOnDomain = !reqMatchesPolicyBasedOnDomain(this.req, this.remoteStates.getPrimary(), this.config.experimentalUseDefaultDocumentDomain) + const urlDoesNotMatchPolicyBasedOnDomain = !reqMatchesPolicyBasedOnDomain(this.req, this.remoteStates.getPrimary(), this.config.experimentalSkipDomainInjection) const isAUTFrame = this.req.isAUTFrame const isHTMLLike = isHTML || isRenderedHTML @@ -547,7 +547,7 @@ const MaybeInjectHtml: ResponseMiddleware = function () { useAstSourceRewriting: this.config.experimentalSourceRewriting, modifyObstructiveThirdPartyCode: this.config.experimentalModifyObstructiveThirdPartyCode && !this.remoteStates.isPrimarySuperDomainOrigin(this.req.proxiedUrl), shouldInjectDocumentDomain: cors.shouldInjectDocumentDomain(this.req.proxiedUrl, { - useDefaultDocumentForDomains: this.config.experimentalUseDefaultDocumentDomain, + skipDomainInjectionForDomains: this.config.experimentalSkipDomainInjection, }), modifyObstructiveCode: this.config.modifyObstructiveCode, url: this.req.proxiedUrl, diff --git a/packages/proxy/test/unit/http/response-middleware.spec.ts b/packages/proxy/test/unit/http/response-middleware.spec.ts index 5a966a8f99ff..0e40bd65bb4b 100644 --- a/packages/proxy/test/unit/http/response-middleware.spec.ts +++ b/packages/proxy/test/unit/http/response-middleware.spec.ts @@ -1437,7 +1437,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: false, experimentalModifyObstructiveThirdPartyCode: false, - experimentalUseDefaultDocumentDomain: null, + experimentalSkipDomainInjection: null, }, simulatedCookies: [], }) @@ -1489,7 +1489,7 @@ describe('http/response-middleware', function () { config: { modifyObstructiveCode: true, experimentalModifyObstructiveThirdPartyCode: true, - experimentalUseDefaultDocumentDomain: null, + experimentalSkipDomainInjection: null, }, remoteStates, debug: (formatter, ...args) => { diff --git a/packages/server/index.d.ts b/packages/server/index.d.ts index efcaad57f6b8..0d8bada3a755 100644 --- a/packages/server/index.d.ts +++ b/packages/server/index.d.ts @@ -22,7 +22,7 @@ export namespace CyServer { experimentalSourceRewriting: boolean modifyObstructiveCode: boolean experimentalModifyObstructiveThirdPartyCode: boolean - experimentalUseDefaultDocumentDomain: string[] | null + experimentalSkipDomainInjection: string[] | null /** * URL to Cypress's runner. */ diff --git a/packages/server/lib/controllers/files.js b/packages/server/lib/controllers/files.js index ca7f34646ea7..804c9fd67397 100644 --- a/packages/server/lib/controllers/files.js +++ b/packages/server/lib/controllers/files.js @@ -27,7 +27,7 @@ module.exports = { debug('all files to send %o', _.map(allFilesToSend, 'relative')) const documentSuperDomainIfExists = cors.shouldInjectDocumentDomain(req.proxiedUrl, { - useDefaultDocumentForDomains: config.experimentalUseDefaultDocumentDomain, + skipDomainInjectionForDomains: config.experimentalSkipDomainInjection, }) ? remoteStates.getPrimary().domainName : undefined @@ -47,7 +47,7 @@ module.exports = { handleCrossOriginIframe (req, res, config) { const iframePath = cwd('lib', 'html', 'spec-bridge-iframe.html') const documentSuperDomainIfExists = cors.shouldInjectDocumentDomain(req.proxiedUrl, { - useDefaultDocumentForDomains: config.experimentalUseDefaultDocumentDomain, + skipDomainInjectionForDomains: config.experimentalSkipDomainInjection, }) ? cors.getSuperDomain(req.proxiedUrl) : undefined diff --git a/packages/server/lib/experiments.ts b/packages/server/lib/experiments.ts index 0794c10c3ba9..729f827467cf 100644 --- a/packages/server/lib/experiments.ts +++ b/packages/server/lib/experiments.ts @@ -54,7 +54,7 @@ const _summaries: StringValues = { experimentalFetchPolyfill: 'Polyfills `window.fetch` to enable Network spying and stubbing.', experimentalInteractiveRunEvents: 'Allows listening to the `before:run`, `after:run`, `before:spec`, and `after:spec` events in the plugins file during interactive mode.', experimentalModifyObstructiveThirdPartyCode: 'Applies `modifyObstructiveCode` to third party `.html` and `.js`, removes subresource integrity, and modifies the user agent in Electron.', - experimentalUseDefaultDocumentDomain: 'Disables setting document.domain to the document\'s super domain on injection.', + experimentalSkipDomainInjection: 'Disables setting document.domain to the document\'s super domain on injection.', experimentalSourceRewriting: 'Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm.', experimentalSingleTabRunMode: 'Runs all component specs in a single tab, trading spec isolation for faster run mode execution.', experimentalStudio: 'Generate and save commands directly to your test suite by interacting with your app as an end user would.', @@ -77,7 +77,7 @@ const _names: StringValues = { experimentalFetchPolyfill: 'Fetch Polyfill', experimentalInteractiveRunEvents: 'Interactive Mode Run Events', experimentalModifyObstructiveThirdPartyCode: 'Modify Obstructive Third Party Code', - experimentalUseDefaultDocumentDomain: 'Use Default document.domain', + experimentalSkipDomainInjection: 'Use Default document.domain', experimentalSingleTabRunMode: 'Single Tab Run Mode', experimentalSourceRewriting: 'Improved Source Rewriting', experimentalStudio: 'Studio', diff --git a/packages/server/lib/server-e2e.ts b/packages/server/lib/server-e2e.ts index d2820fe798f9..7e83a5d79fd2 100644 --- a/packages/server/lib/server-e2e.ts +++ b/packages/server/lib/server-e2e.ts @@ -46,7 +46,7 @@ const isResponseHtml = function (contentType, responseBuffer) { export class ServerE2E extends ServerBase { private _urlResolver: Bluebird> | null // the initialization of this variable is only precautionary as the actual config value is applied when the server is created - private useDefaultDocumentForDomains: string[] | null = null + private skipDomainInjectionForDomains: string[] | null = null constructor () { super() @@ -60,10 +60,10 @@ export class ServerE2E extends ServerBase { createServer (app, config, onWarning): Bluebird<[number, WarningErr?]> { return new Bluebird((resolve, reject) => { - const { port, fileServerFolder, socketIoRoute, baseUrl, experimentalUseDefaultDocumentDomain } = config + const { port, fileServerFolder, socketIoRoute, baseUrl, experimentalSkipDomainInjection } = config this._server = this._createHttpServer(app) - this.useDefaultDocumentForDomains = experimentalUseDefaultDocumentDomain + this.skipDomainInjectionForDomains = experimentalSkipDomainInjection const onError = (err) => { // if the server bombs before starting // and the err no is EADDRINUSE @@ -311,7 +311,7 @@ export class ServerE2E extends ServerBase { // of when to buffer and set the remote state if (isOk && details.isHtml) { const urlDoesNotMatchPolicyBasedOnDomain = options.hasAlreadyVisitedUrl - && !cors.urlMatchesPolicyBasedOnDomain(primaryRemoteState.origin, newUrl || '', { useDefaultDocumentForDomains: this.useDefaultDocumentForDomains }) + && !cors.urlMatchesPolicyBasedOnDomain(primaryRemoteState.origin, newUrl || '', { skipDomainInjectionForDomains: this.skipDomainInjectionForDomains }) || options.isFromSpecBridge if (!handlingLocalFile) { diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index 8588f56846e1..b67a7cfeff87 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -30,7 +30,7 @@ export interface FullConfig extends Partial - & Pick // TODO: Figure out how to type this better. + & Pick // TODO: Figure out how to type this better. export interface SettingsOptions { testingType?: 'component' |'e2e' diff --git a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js b/system-tests/__snapshots__/experimental_skip_domain_injection_spec.ts.js similarity index 83% rename from system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js rename to system-tests/__snapshots__/experimental_skip_domain_injection_spec.ts.js index 62230a95c0c4..d1d3c53d857c 100644 --- a/system-tests/__snapshots__/experimental_use_default_document_domain_spec.ts.js +++ b/system-tests/__snapshots__/experimental_skip_domain_injection_spec.ts.js @@ -1,4 +1,4 @@ -exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` +exports['e2e experimentalSkipDomainInjection=true / passes'] = ` ==================================================================================================== @@ -7,18 +7,18 @@ exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Cypress: 1.2.3 │ │ Browser: FooBrowser 88 │ - │ Specs: 1 found (experimental_use_default_document_domain.cy.ts) │ - │ Searched: cypress/e2e/experimental_use_default_document_domain.cy.ts │ - │ Experiments: experimentalUseDefaultDocumentDomain=*.foobar.com │ + │ Specs: 1 found (experimental_skip_domain_injection.cy.ts) │ + │ Searched: cypress/e2e/experimental_skip_domain_injection.cy.ts │ + │ Experiments: experimentalSkipDomainInjection=*.foobar.com │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ──────────────────────────────────────────────────────────────────────────────────────────────────── - Running: experimental_use_default_document_domain.cy.ts (1 of 1) + Running: experimental_skip_domain_injection.cy.ts (1 of 1) - expected behavior when experimentalUseDefaultDocumentDomain=true + expected behavior when experimentalSkipDomainInjection=true ✓ Handles cross-site/cross-origin navigation the same way without the experimental flag enabled ✓ errors appropriately when doing a sub domain navigation w/o cy.origin() ✓ allows sub-domain navigations with the use of cy.origin() @@ -38,15 +38,15 @@ exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` │ Screenshots: 0 │ │ Video: true │ │ Duration: X seconds │ - │ Spec Ran: experimental_use_default_document_domain.cy.ts │ + │ Spec Ran: experimental_skip_domain_injection.cy.ts │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ (Video) - Started processing: Compressing to 32 CRF - - Finished processing: /XXX/XXX/XXX/cypress/videos/experimental_use_default_docume (X second) - nt_domain.cy.ts.mp4 + - Finished processing: /XXX/XXX/XXX/cypress/videos/experimental_skip_domain_inject (X second) + ion.cy.ts.mp4 ==================================================================================================== @@ -56,8 +56,8 @@ exports['e2e experimentalUseDefaultDocumentDomain=true / passes'] = ` Spec Tests Passing Failing Pending Skipped ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ ✔ experimental_use_default_document_d XX:XX 3 3 - - - │ - │ omain.cy.ts │ + │ ✔ experimental_skip_domain_injection. XX:XX 3 3 - - - │ + │ cy.ts │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ✔ All specs passed! XX:XX 3 3 - - - diff --git a/system-tests/projects/e2e/cypress/e2e/experimental_use_default_document_domain.cy.ts b/system-tests/projects/e2e/cypress/e2e/experimental_skip_domain_injection.cy.ts similarity index 88% rename from system-tests/projects/e2e/cypress/e2e/experimental_use_default_document_domain.cy.ts rename to system-tests/projects/e2e/cypress/e2e/experimental_skip_domain_injection.cy.ts index f02ad0a0ccb1..ab6be52a8514 100644 --- a/system-tests/projects/e2e/cypress/e2e/experimental_use_default_document_domain.cy.ts +++ b/system-tests/projects/e2e/cypress/e2e/experimental_skip_domain_injection.cy.ts @@ -1,4 +1,4 @@ -describe('expected behavior when experimentalUseDefaultDocumentDomain=true', () => { +describe('expected behavior when experimentalSkipDomainInjection=true', () => { it('Handles cross-site/cross-origin navigation the same way without the experimental flag enabled', () => { cy.visit('/primary_origin.html') cy.get('a[data-cy="cross_origin_secondary_link"]').click() @@ -21,7 +21,7 @@ describe('expected behavior when experimentalUseDefaultDocumentDomain=true', () expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`) }) - // with experimentalUseDefaultDocumentDomain, sub domain navigations require a cy.origin() block + // with experimentalSkipDomainInjection, sub domain navigations require a cy.origin() block cy.visit('http://app.foobar.com:4466/primary_origin.html') cy.get('a[data-cy="cross_origin_secondary_link"]').click() cy.get('[data-cy="dom-check"]', { @@ -33,7 +33,7 @@ describe('expected behavior when experimentalUseDefaultDocumentDomain=true', () cy.visit('http://app.foobar.com:4466/primary_origin.html') cy.get('a[data-cy="cross_origin_secondary_link"]').click() - // with experimentalUseDefaultDocumentDomain, sub domain navigations require a cy.origin() block + // with experimentalSkipDomainInjection, sub domain navigations require a cy.origin() block cy.origin('http://www.foobar.com:4466', () => { cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin') }) diff --git a/system-tests/test/experimental_use_default_document_domain_spec.ts b/system-tests/test/experimental_skip_domain_injection_spec.ts similarity index 82% rename from system-tests/test/experimental_use_default_document_domain_spec.ts rename to system-tests/test/experimental_skip_domain_injection_spec.ts index 5103db62031a..1e4f6b4bab8d 100644 --- a/system-tests/test/experimental_use_default_document_domain_spec.ts +++ b/system-tests/test/experimental_skip_domain_injection_spec.ts @@ -15,7 +15,7 @@ const onServer = function (app) { }) } -describe('e2e experimentalUseDefaultDocumentDomain=true', () => { +describe('e2e experimentalSkipDomainInjection=true', () => { systemTests.setup({ servers: [{ port: 4466, @@ -33,12 +33,12 @@ describe('e2e experimentalUseDefaultDocumentDomain=true', () => { browser: '!webkit', // TODO(webkit): fix+unskip (needs multidomain support) // keep the port the same to prevent issues with the snapshot port: PORT, - spec: 'experimental_use_default_document_domain.cy.ts', + spec: 'experimental_skip_domain_injection.cy.ts', snapshot: true, expectedExitCode: 0, config: { retries: 0, - experimentalUseDefaultDocumentDomain: ['*.foobar.com'], + experimentalSkipDomainInjection: ['*.foobar.com'], }, }) }) From 861de35229ed0b6256c5c8b853ac36ee6c27680b Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 4 Jan 2023 12:52:42 -0500 Subject: [PATCH 30/35] fix regression in shouldInjectionDocumentDomain utility function and add unit tests --- packages/network/lib/cors.ts | 3 ++- packages/network/test/unit/cors_spec.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index 873808675ab3..692857d1a9f2 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -203,7 +203,8 @@ export const shouldInjectDocumentDomain = (url: string, opts?: { // We need to make sure the experimentalSkipDomainInjection feature flag is off. // If on, we need to make sure the glob pattern doesn't exist in the array so we cover possible intersections (google). if (_.isArray(opts?.skipDomainInjectionForDomains)) { - return doesUrlHostnameMatchGlobArray(url, opts?.skipDomainInjectionForDomains as string[]) + // if we match the glob, we want to return false + return !doesUrlHostnameMatchGlobArray(url, opts?.skipDomainInjectionForDomains as string[]) } return true diff --git a/packages/network/test/unit/cors_spec.ts b/packages/network/test/unit/cors_spec.ts index 18ff5f84a62a..1614e20aec9e 100644 --- a/packages/network/test/unit/cors_spec.ts +++ b/packages/network/test/unit/cors_spec.ts @@ -721,4 +721,22 @@ describe('lib/cors', () => { }) }) }) + + context('.shouldInjectDocumentDomain', () => { + it('returns false when "skipDomainInjectionForDomains" is configured and contains a matching blob pattern ', () => { + expect(cors.shouldInjectDocumentDomain('http://www.cypress.io', { + skipDomainInjectionForDomains: ['*.cypress.io'], + })).to.be.false + }) + + it('returns true when "skipDomainInjectionForDomains" exists, but doesn\'t contain a matching glob pattern', () => { + expect(cors.shouldInjectDocumentDomain('http://www.cypress.io', { + skipDomainInjectionForDomains: ['*.foobar.com'], + })).to.be.true + }) + + it('returns true otherwise', () => { + expect(cors.shouldInjectDocumentDomain('http://www.cypress.io')).to.be.true + }) + }) }) From 8a16ce6072995b47f720d59deb68c4f085c952f9 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 4 Jan 2023 12:55:57 -0500 Subject: [PATCH 31/35] run ci From 9b6938f5beed192f8bf3586cbbd4ba812015a18f Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 4 Jan 2023 13:18:43 -0500 Subject: [PATCH 32/35] chore: rename documentSuperDomainIfExists to superDomain [run ci] --- packages/server/lib/controllers/files.js | 8 ++++---- packages/server/lib/html/iframe.html | 4 ++-- packages/server/lib/html/spec-bridge-iframe.html | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/server/lib/controllers/files.js b/packages/server/lib/controllers/files.js index 804c9fd67397..81d34c19ee49 100644 --- a/packages/server/lib/controllers/files.js +++ b/packages/server/lib/controllers/files.js @@ -26,14 +26,14 @@ module.exports = { debug('all files to send %o', _.map(allFilesToSend, 'relative')) - const documentSuperDomainIfExists = cors.shouldInjectDocumentDomain(req.proxiedUrl, { + const superDomain = cors.shouldInjectDocumentDomain(req.proxiedUrl, { skipDomainInjectionForDomains: config.experimentalSkipDomainInjection, }) ? remoteStates.getPrimary().domainName : undefined const iframeOptions = { - documentSuperDomainIfExists, + superDomain, title: this.getTitle(test), scripts: JSON.stringify(allFilesToSend), } @@ -46,7 +46,7 @@ module.exports = { handleCrossOriginIframe (req, res, config) { const iframePath = cwd('lib', 'html', 'spec-bridge-iframe.html') - const documentSuperDomainIfExists = cors.shouldInjectDocumentDomain(req.proxiedUrl, { + const superDomain = cors.shouldInjectDocumentDomain(req.proxiedUrl, { skipDomainInjectionForDomains: config.experimentalSkipDomainInjection, }) ? cors.getSuperDomain(req.proxiedUrl) : @@ -55,7 +55,7 @@ module.exports = { const origin = cors.getOrigin(req.proxiedUrl) const iframeOptions = { - documentSuperDomainIfExists, + superDomain, title: `Cypress for ${origin}`, namespace: config.namespace, } diff --git a/packages/server/lib/html/iframe.html b/packages/server/lib/html/iframe.html index fbf8d41e3931..8ea4099bbfe0 100644 --- a/packages/server/lib/html/iframe.html +++ b/packages/server/lib/html/iframe.html @@ -6,8 +6,8 @@ From e27acf72e2fd8f636fb0a371b61af4b317313726 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 6 Jan 2023 15:40:01 -0500 Subject: [PATCH 33/35] chore: address comments from code review --- packages/driver/src/cypress/ensure.ts | 1 + packages/driver/src/cypress/error_messages.ts | 6 ++++-- packages/errors/src/errors.ts | 1 + packages/network/lib/cors.ts | 15 +++++++++++---- packages/network/package.json | 2 +- .../e2e/experimental_skip_domain_injection.cy.ts | 2 ++ yarn.lock | 2 +- 7 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/driver/src/cypress/ensure.ts b/packages/driver/src/cypress/ensure.ts index d398a8ee0e68..32fe258cf90f 100644 --- a/packages/driver/src/cypress/ensure.ts +++ b/packages/driver/src/cypress/ensure.ts @@ -255,6 +255,7 @@ const commandCanCommunicateWithAUT = (cy: $Cy, err?): boolean => { const crossOriginCommandError = $errUtils.errByPath('miscellaneous.cross_origin_command', { commandOrigin: window.location.origin, autOrigin: cy.state('autLocation').origin, + isSkipDomainInjectionEnabled: !!Cypress.config('experimentalSkipDomainInjection'), }) if (err) { diff --git a/packages/driver/src/cypress/error_messages.ts b/packages/driver/src/cypress/error_messages.ts index dfb61ace1c9c..9ceb81b290eb 100644 --- a/packages/driver/src/cypress/error_messages.ts +++ b/packages/driver/src/cypress/error_messages.ts @@ -912,13 +912,15 @@ export default { return `Timed out retrying after ${ms}ms: ` }, test_stopped: 'Cypress test was stopped while running this command.', - cross_origin_command ({ commandOrigin, autOrigin }) { + cross_origin_command ({ commandOrigin, autOrigin, isSkipDomainInjectionEnabled }) { return { message: stripIndent`\ The command was expected to run against origin \`${commandOrigin}\` but the application is at origin \`${autOrigin}\`. This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly. - + ${isSkipDomainInjectionEnabled ? ` + If \`experimentalSkipDomainInjection\` is enabled for this domain, a ${cmd('origin')} command is required. + ` : ''} Using ${cmd('origin')} to wrap the commands run on \`${autOrigin}\` will likely fix this issue. \`cy.origin('${autOrigin}', () => {\` diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 29ed50d0c0a3..e203995cdf90 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -1195,6 +1195,7 @@ export const AllCypressErrors = { return errTemplate`\ The ${fmt.highlight(`experimentalSkipDomainInjection`)} experiment is currently only supported for End to End Testing and must be configured as an e2e testing type property: ${fmt.highlightSecondary(`e2e.experimentalSkipDomainInjection`)}. + The suggested values are only a recommendation. ${fmt.code(code)}` }, diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index 692857d1a9f2..4f051e641f0e 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -162,7 +162,6 @@ export const urlSameSiteMatch = (frameUrl: string, topUrl: string): boolean => { } /** - * * @param url - the url to check the policy against. * @param arrayOfStringOrGlobPatterns - an array of url strings or globs to match against * @returns {boolean} - whether or not a match was found @@ -178,10 +177,11 @@ const doesUrlHostnameMatchGlobArray = (url: string, arrayOfStringOrGlobPatterns: /** * Returns the policy that will be used for the specified url. * @param url - the url to check the policy against. + * @param opts - an options object containing the skipDomainInjectionForDomains config. Default is undefined. * @returns a Policy string. */ export const policyForDomain = (url: string, opts?: { - skipDomainInjectionForDomains: string[] | null + skipDomainInjectionForDomains: string[] | null | undefined }): Policy => { const obj = parseUrlIntoHostProtocolDomainTldPort(url) let shouldUseSameOriginPolicy = strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`) @@ -196,6 +196,11 @@ export const policyForDomain = (url: string, opts?: { 'same-super-domain-origin' } +/** + * @param url - The url to check for injection + * @param opts - an options object containing the skipDomainInjectionForDomains config. Default is undefined. + * @returns {boolean} whether or not document.domain should be injected solely based on the url. + */ export const shouldInjectDocumentDomain = (url: string, opts?: { skipDomainInjectionForDomains: string[] | null }) => { @@ -216,6 +221,7 @@ export const shouldInjectDocumentDomain = (url: string, opts?: { * in which case the policy is 'same-origin' * @param frameUrl - The url you are testing the policy for. * @param topUrl - The url you are testing the policy in context of. + * @param opts - an options object containing the skipDomainInjectionForDomains config. Default is undefined. * @returns boolean, true if matching, false if not. */ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, opts?: { @@ -223,7 +229,7 @@ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, }): boolean => { return urlMatchesPolicy({ policy: policyForDomain(frameUrl, { - skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains || [], + skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains, }), frameUrl, topUrl, @@ -236,13 +242,14 @@ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, * in which case the policy is 'same-origin' * @param frameUrl - The url you are testing the policy for. * @param topProps - The props of the url you are testing the policy in context of. + * @param opts - an options object containing the skipDomainInjectionForDomains config. Default is undefined. * @returns boolean, true if matching, false if not. */ export const urlMatchesPolicyBasedOnDomainProps = (frameUrl: string, topProps: ParsedHostWithProtocolAndHost, opts?: { skipDomainInjectionForDomains: string[] }): boolean => { const policy = policyForDomain(frameUrl, { - skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains || [], + skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains, }) return urlMatchesPolicyProps({ diff --git a/packages/network/package.json b/packages/network/package.json index e8a89e320c81..bb6283bea0ce 100644 --- a/packages/network/package.json +++ b/packages/network/package.json @@ -20,7 +20,7 @@ "debug": "^4.3.2", "fs-extra": "9.1.0", "lodash": "^4.17.21", - "minimatch": "^3.0.5", + "minimatch": "3.0.5", "node-forge": "1.3.0", "proxy-from-env": "1.0.0" }, diff --git a/system-tests/projects/e2e/cypress/e2e/experimental_skip_domain_injection.cy.ts b/system-tests/projects/e2e/cypress/e2e/experimental_skip_domain_injection.cy.ts index ab6be52a8514..fb906537dd7c 100644 --- a/system-tests/projects/e2e/cypress/e2e/experimental_skip_domain_injection.cy.ts +++ b/system-tests/projects/e2e/cypress/e2e/experimental_skip_domain_injection.cy.ts @@ -16,6 +16,8 @@ describe('expected behavior when experimentalSkipDomainInjection=true', () => { expect(err.message).to.contain('This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.') expect(err.message).to.contain('Using `cy.origin()` to wrap the commands run on `http://www.foobar.com:4466` will likely fix this issue.') expect(err.message).to.include(`cy.origin('http://www.foobar.com:4466', () => {\`\n\` \`\n\`})`) + expect(err.message).to.include('If `experimentalSkipDomainInjection` is enabled for this domain, a `cy.origin()` command is required.') + // make sure that the secondary origin failures do NOT show up as spec failures or AUT failures expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`) expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`) diff --git a/yarn.lock b/yarn.lock index ae4607f5b455..5a4aed249432 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22731,7 +22731,7 @@ minimatch@4.2.1: dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== From 8c6813dbf3fa6a74d0cc5b41e0b92966e1dadabb Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 6 Jan 2023 15:41:36 -0500 Subject: [PATCH 34/35] chore: just pass opts through to policyForDomain --- packages/network/lib/cors.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/network/lib/cors.ts b/packages/network/lib/cors.ts index 4f051e641f0e..7a3e553c875d 100644 --- a/packages/network/lib/cors.ts +++ b/packages/network/lib/cors.ts @@ -228,9 +228,7 @@ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, skipDomainInjectionForDomains: string[] | null }): boolean => { return urlMatchesPolicy({ - policy: policyForDomain(frameUrl, { - skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains, - }), + policy: policyForDomain(frameUrl, opts), frameUrl, topUrl, }) @@ -248,9 +246,7 @@ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string, export const urlMatchesPolicyBasedOnDomainProps = (frameUrl: string, topProps: ParsedHostWithProtocolAndHost, opts?: { skipDomainInjectionForDomains: string[] }): boolean => { - const policy = policyForDomain(frameUrl, { - skipDomainInjectionForDomains: opts?.skipDomainInjectionForDomains, - }) + const policy = policyForDomain(frameUrl, opts) return urlMatchesPolicyProps({ policy, From 100a8cb3b842ef7399802f94ca64e634889b2be5 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 6 Jan 2023 15:42:48 -0500 Subject: [PATCH 35/35] run ci