Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: experimental skip domain injection #25307

Merged
merged 45 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e7b288f
feat: set up experimentalUseDefaultDocumentDomain to disallow documen…
AtofStryker Dec 27, 2022
3e16fff
use default domain around experimentalUseDefaultDocumentDomain in mai…
AtofStryker Dec 28, 2022
3dda26b
run ci
AtofStryker Dec 28, 2022
805de57
fix: add insertion of experimental flag where is was needed/missing
AtofStryker Dec 28, 2022
0b35f47
chore: add system test to exercise experimental flag for expected beh…
AtofStryker Dec 28, 2022
9ddbd99
fix: fix issues with template updates to conform to squirrelly v7
AtofStryker Dec 28, 2022
3fe48f9
fix: update config tests to include new experimental flag
AtofStryker Dec 28, 2022
d422738
run ci
AtofStryker Dec 28, 2022
0a5750f
fix: trailing whitespace [run ci]
AtofStryker Dec 29, 2022
41896c8
chore: update snapshot
AtofStryker Dec 29, 2022
48c6b04
run ci
AtofStryker Dec 29, 2022
7eb2d0e
Merge branch 'develop' into feat/experimentalDefaultDocumentDomain
AtofStryker Dec 29, 2022
03dd439
fix: update proxy unit tests to account for experimentalUseDefaultDoc…
AtofStryker Dec 29, 2022
38dd080
run ci
AtofStryker Dec 29, 2022
fc18ac6
fix: Allow component tests with special characters in filepath (#25299)
mike-plummer Dec 29, 2022
6538e13
fix: fix server unit and integration tests. integration tests should …
AtofStryker Dec 29, 2022
d07248c
run ci
AtofStryker Dec 29, 2022
ecffe4c
Merge branch 'develop' of github.com:cypress-io/cypress into feat/exp…
AtofStryker Dec 29, 2022
3ec938f
run ci
AtofStryker Dec 30, 2022
55b947c
fix: server integration tests where google documents are expected to …
AtofStryker Dec 30, 2022
b261683
run ci
AtofStryker Dec 30, 2022
483e2cd
fix: update server test with mssing unupdated assertions
AtofStryker Dec 30, 2022
d63e526
run ci
AtofStryker Dec 30, 2022
04c3ba1
fix: turn off experimental flag by default while recommending sane de…
AtofStryker Dec 30, 2022
2e9b369
run ci
AtofStryker Dec 30, 2022
2cb1bd4
chore: fix typings [run ci]
AtofStryker Dec 30, 2022
b98a478
Merge branch 'develop' of github.com:cypress-io/cypress into feat/exp…
AtofStryker Jan 3, 2023
ef7701b
run ci
AtofStryker Jan 3, 2023
671ea12
chore: make experiment an e2e option only
AtofStryker Jan 3, 2023
a77870d
Merge branch 'develop' of github.com:cypress-io/cypress into feat/exp…
AtofStryker Jan 3, 2023
bd65043
run ci
AtofStryker Jan 3, 2023
8e7c353
Merge branch 'develop' into feat/experimentalDefaultDocumentDomain
AtofStryker Jan 3, 2023
732581b
Merge branch 'develop' into feat/experimentalDefaultDocumentDomain
AtofStryker Jan 3, 2023
dccf967
chore: address comments in code review
AtofStryker Jan 4, 2023
86bea96
chore: rename experimentalUseDefaultDocumentDomain to experimentalSki…
AtofStryker Jan 4, 2023
b59ba86
Merge branch 'feat/experimentalDefaultDocumentDomain' of github.com:c…
AtofStryker Jan 4, 2023
0e5497a
Merge branch 'develop' of github.com:cypress-io/cypress into feat/exp…
AtofStryker Jan 4, 2023
861de35
fix regression in shouldInjectionDocumentDomain utility function and …
AtofStryker Jan 4, 2023
8a16ce6
run ci
AtofStryker Jan 4, 2023
9b6938f
chore: rename documentSuperDomainIfExists to superDomain [run ci]
AtofStryker Jan 4, 2023
e3c4f92
Merge branch 'develop' of github.com:cypress-io/cypress into feat/exp…
AtofStryker Jan 6, 2023
e27acf7
chore: address comments from code review
AtofStryker Jan 6, 2023
75934e7
Merge branch 'develop' of github.com:cypress-io/cypress into feat/exp…
AtofStryker Jan 6, 2023
8c6813d
chore: just pass opts through to policyForDomain
AtofStryker Jan 6, 2023
100a8cb
run ci
AtofStryker Jan 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cli/types/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3019,6 +3019,17 @@ 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(). 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 null
*/
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
Expand Down
3 changes: 3 additions & 0 deletions packages/config/__snapshots__/index.spec.ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1
'experimentalInteractiveRunEvents': false,
'experimentalRunAllSpecs': false,
'experimentalModifyObstructiveThirdPartyCode': false,
'experimentalUseDefaultDocumentDomain': null,
'experimentalOriginDependencies': false,
'experimentalSourceRewriting': false,
'experimentalSingleTabRunMode': false,
Expand Down Expand Up @@ -123,6 +124,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f
'experimentalInteractiveRunEvents': false,
'experimentalRunAllSpecs': false,
'experimentalModifyObstructiveThirdPartyCode': false,
'experimentalUseDefaultDocumentDomain': null,
'experimentalOriginDependencies': false,
'experimentalSourceRewriting': false,
'experimentalSingleTabRunMode': false,
Expand Down Expand Up @@ -204,6 +206,7 @@ exports['config/src/index .getPublicConfigKeys returns list of public config key
'experimentalInteractiveRunEvents',
'experimentalRunAllSpecs',
'experimentalModifyObstructiveThirdPartyCode',
'experimentalUseDefaultDocumentDomain',
'experimentalOriginDependencies',
'experimentalSourceRewriting',
'experimentalSingleTabRunMode',
Expand Down
17 changes: 17 additions & 0 deletions packages/config/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ const driverConfigOptions: Array<DriverConfigOption> = [
validation: validate.isBoolean,
isExperimental: true,
requireRestartOnChange: 'server',
}, {
name: 'experimentalUseDefaultDocumentDomain',
defaultValue: null,
validation: (_: string, value: any) => validate.isArrayOfStrings(value) || value === null,
isExperimental: true,
requireRestartOnChange: 'server',
}, {
name: 'experimentalOriginDependencies',
defaultValue: false,
Expand Down Expand Up @@ -679,6 +685,12 @@ export const breakingRootOptions: Array<BreakingOption> = [
isWarning: false,
testingTypes: ['e2e'],
},
{
name: 'experimentalUseDefaultDocumentDomain',
errorKey: 'EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY',
isWarning: false,
testingTypes: ['e2e'],
},
]

export const testingTypeBreakingOptions: { e2e: Array<BreakingOption>, component: Array<BreakingOption> } = {
Expand Down Expand Up @@ -720,5 +732,10 @@ export const testingTypeBreakingOptions: { e2e: Array<BreakingOption>, component
errorKey: 'EXPERIMENTAL_ORIGIN_DEPENDENCIES_E2E_ONLY',
isWarning: false,
},
{
name: 'experimentalUseDefaultDocumentDomain',
errorKey: 'EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY',
isWarning: false,
},
],
}
2 changes: 1 addition & 1 deletion packages/config/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
2 changes: 2 additions & 0 deletions packages/config/test/project/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,7 @@ describe('config/src/project/utils', () => {
env: {},
execTimeout: { value: 60000, from: 'default' },
experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' },
experimentalUseDefaultDocumentDomain: { value: null, from: 'default' },
experimentalFetchPolyfill: { value: false, from: 'default' },
experimentalInteractiveRunEvents: { value: false, from: 'default' },
experimentalOriginDependencies: { value: false, from: 'default' },
Expand Down Expand Up @@ -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: null, from: 'default' },
experimentalFetchPolyfill: { value: false, from: 'default' },
experimentalInteractiveRunEvents: { value: false, from: 'default' },
experimentalOriginDependencies: { value: false, from: 'default' },
Expand Down
8 changes: 6 additions & 2 deletions packages/driver/src/cy/commands/origin/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,14 @@ 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, {
useDefaultDocumentForDomains: Cypress.config('experimentalUseDefaultDocumentDomain'),
})) {
// this._isSameSuperDomainOriginWithExceptions({ originLocation, specLocation })) {

const policy = cors.policyForDomain(originLocation.href)
const policy = cors.policyForDomain(originLocation.href, {
useDefaultDocumentForDomains: Cypress.config('experimentalUseDefaultDocumentDomain'),
})

$errUtils.throwErrByPath('origin.invalid_url_argument_same_origin', {
onFail: this.log,
Expand Down
7 changes: 6 additions & 1 deletion packages/driver/src/cypress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down Expand Up @@ -182,7 +183,11 @@ class $Cypress {

// set domainName but allow us to turn
// off this feature in testing
if (domainName && config.testingType === 'e2e') {
const shouldInjectDocumentDomain = cors.shouldInjectDocumentDomain(window.location.origin, {
useDefaultDocumentForDomains: config.experimentalUseDefaultDocumentDomain,
})

if (domainName && config.testingType === 'e2e' && shouldInjectDocumentDomain) {
document.domain = domainName
}

Expand Down
13 changes: 13 additions & 0 deletions packages/errors/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions packages/errors/test/unit/visualSnapshotErrors_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1268,5 +1268,11 @@ describe('visual error templates', () => {
default: [],
}
},

EXPERIMENTAL_USE_DEFAULT_DOCUMENT_DOMAIN_E2E_ONLY: () => {
return {
default: [],
}
},
})
})
1 change: 1 addition & 0 deletions packages/graphql/schemas/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 54 additions & 7 deletions packages/network/lib/cors.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -11,6 +12,8 @@ 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) {
Expand Down Expand Up @@ -158,15 +161,52 @@ 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): Policy => {
export const policyForDomain = (url: string, opts?: {
useDefaultDocumentForDomains: string[] | null
}): Policy => {
const obj = parseUrlIntoHostProtocolDomainTldPort(url)
let shouldUseSameOriginPolicy = strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`)

return strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`) ? 'same-origin' : 'same-super-domain-origin'
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 shouldUseSameOriginPolicy ?
'same-origin' :
'same-super-domain-origin'
}

export const shouldInjectDocumentDomain = (url: string, opts?: {
useDefaultDocumentForDomains: string[] | null
}) => {
// 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
}

/**
Expand All @@ -177,9 +217,13 @@ export const policyForDomain = (url: string): Policy => {
* @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?: {
useDefaultDocumentForDomains: string[] | null
}): boolean => {
return urlMatchesPolicy({
policy: policyForDomain(frameUrl),
policy: policyForDomain(frameUrl, {
useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [],
}),
frameUrl,
topUrl,
})
Expand All @@ -193,9 +237,12 @@ export const urlMatchesPolicyBasedOnDomain = (frameUrl: string, topUrl: string):
* @param topProps - The props of the url you are testing the policy in context of.
* @returns boolean, true if matching, false if not.
*/
export const urlMatchesPolicyBasedOnDomainProps = (frameUrl: string, topProps: ParsedHostWithProtocolAndHost): boolean => {
const obj = parseUrlIntoHostProtocolDomainTldPort(frameUrl)
const policy = strictSameOriginDomains.includes(`${obj.domain}.${obj.tld}`) ? 'same-origin' : 'same-super-domain-origin'
export const urlMatchesPolicyBasedOnDomainProps = (frameUrl: string, topProps: ParsedHostWithProtocolAndHost, opts?: {
useDefaultDocumentForDomains: string[]
}): boolean => {
const policy = policyForDomain(frameUrl, {
useDefaultDocumentForDomains: opts?.useDefaultDocumentForDomains || [],
})

return urlMatchesPolicyProps({
policy,
Expand Down
1 change: 1 addition & 0 deletions packages/network/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
64 changes: 64 additions & 0 deletions packages/network/test/unit/cors_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,68 @@ describe('lib/cors', () => {
expect(cors.getOrigin('http://www.app.herokuapp.com:8080')).to.equal('http://www.app.herokuapp.com:8080')
})
})

context('.policyForDomain', () => {
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: recommendedSameOriginPolicyUrlGlobs,
})).to.equal('same-origin')
})

it('www.google.com', () => {
expect(cors.policyForDomain('https://www.google.com', {
useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs,
})).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: 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,
})).to.equal('same-origin')
})

it('https://the-host.develop.file.force.com', () => {
expect(cors.policyForDomain('https://the-host.develop.file.force.com', {
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: recommendedSameOriginPolicyUrlGlobs,
})).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: recommendedSameOriginPolicyUrlGlobs,
})).to.equal('same-super-domain-origin')
})

it('docs.cypress.io', () => {
expect(cors.policyForDomain('http://docs.cypress.io', {
useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs,
})).to.equal('same-super-domain-origin')
})

it('stackoverflow.com', () => {
expect(cors.policyForDomain('https://stackoverflow.com', {
useDefaultDocumentForDomains: recommendedSameOriginPolicyUrlGlobs,
})).to.equal('same-super-domain-origin')
})
})
})
})
4 changes: 2 additions & 2 deletions packages/proxy/lib/http/request-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading