Skip to content

Commit

Permalink
feat: allow for spec bridge to either match specific origin policy or…
Browse files Browse the repository at this point in the history
… super domain origin policy
  • Loading branch information
AtofStryker committed Sep 12, 2022
1 parent 8eca005 commit cb8c26d
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 17 deletions.
12 changes: 7 additions & 5 deletions packages/network/lib/cors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,20 @@ export function getDomainNameFromParsedHost (parsedHost: ParsedHost) {
return _.compact([parsedHost.domain, parsedHost.tld]).join('.')
}

export function urlMatchesOriginPolicyProps (urlStr, props) {
export function urlMatchesSuperDomainOriginPolicyProps (urlStr, props) {
// take a shortcut here in the case
// where remoteHostAndPort is null
if (!props) {
return false
}

const parsedUrl = parseUrlIntoHostProtocolDomainTldPort(urlStr)
const { subdomain: sub1, ...parsedUrl } = parseUrlIntoHostProtocolDomainTldPort(urlStr)
const { subdomain: sub2, ...propsOmittedSubDomain } = props

// does the parsedUrl match the parsedHost?
// To fully match origin policy, the full host (including subdomain) and port is required to match. @see https://developer.mozilla.org/en-US/docs/Glossary/Origin
return _.isEqual(parsedUrl, props)
// To fully match the super domain origin policy, the full host (excluding subdomain) and port is required to match. @see https://developer.mozilla.org/en-US/docs/Glossary/Origin
const doSubDomainsFitSuperDomainPolicy = (sub1 === null || sub2 === null) ? true : sub1 === sub2

return _.isEqual(parsedUrl, propsOmittedSubDomain) && doSubDomainsFitSuperDomainPolicy
}

export function urlMatchesSameSitePolicyProps (urlStr, props) {
Expand Down
12 changes: 6 additions & 6 deletions packages/proxy/lib/http/response-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ function getNodeCharsetFromResponse (headers: IncomingHttpHeaders, body: Buffer,
return 'latin1'
}

function reqMatchesOriginPolicy (req: CypressIncomingRequest, remoteState) {
function reqMatchesSuperDomainOriginPolicy (req: CypressIncomingRequest, remoteState) {
if (remoteState.strategy === 'http') {
return cors.urlMatchesOriginPolicyProps(req.proxiedUrl, remoteState.props)
return cors.urlMatchesSuperDomainOriginPolicyProps(req.proxiedUrl, remoteState.props)
}

if (remoteState.strategy === 'file') {
Expand Down Expand Up @@ -236,7 +236,7 @@ const PatchExpressSetHeader: ResponseMiddleware = function () {
}

const MaybeDelayForCrossOrigin: ResponseMiddleware = function () {
const isCrossOrigin = !reqMatchesOriginPolicy(this.req, this.remoteStates.current())
const isCrossOrigin = !reqMatchesSuperDomainOriginPolicy(this.req, this.remoteStates.current())
const isPreviousOrigin = this.remoteStates.isInOriginStack(this.req.proxiedUrl)
const isHTML = resContentTypeIs(this.incomingRes, 'text/html')
const isRenderedHTML = reqWillRenderHtml(this.req)
Expand Down Expand Up @@ -276,7 +276,7 @@ const SetInjectionLevel: ResponseMiddleware = function () {

this.debug('determine injection')

const isReqMatchOriginPolicy = reqMatchesOriginPolicy(this.req, this.remoteStates.current())
const isReqMatchSuperDomainOriginPolicy = reqMatchesSuperDomainOriginPolicy(this.req, this.remoteStates.current())
const getInjectionLevel = () => {
if (this.incomingRes.headers['x-cypress-file-server-error'] && !this.res.isInitial) {
this.debug('- partial injection (x-cypress-file-server-error)')
Expand All @@ -293,7 +293,7 @@ const SetInjectionLevel: ResponseMiddleware = function () {
return 'fullCrossOrigin'
}

if (!isHTML || (!isReqMatchOriginPolicy && !isAUTFrame)) {
if (!isHTML || (!isReqMatchSuperDomainOriginPolicy && !isAUTFrame)) {
this.debug('- no injection (not html)')

return false
Expand Down Expand Up @@ -342,7 +342,7 @@ const SetInjectionLevel: ResponseMiddleware = function () {
this.res.wantsInjection === 'full' ||
this.res.wantsInjection === 'fullCrossOrigin' ||
// only modify JavasScript if matching the current origin policy or if experimentalModifyObstructiveThirdPartyCode is enabled (above)
(resContentTypeIsJavaScript(this.incomingRes) && isReqMatchOriginPolicy))
(resContentTypeIsJavaScript(this.incomingRes) && isReqMatchSuperDomainOriginPolicy))

this.debug('injection levels: %o', _.pick(this.res, 'isInitial', 'wantsInjection', 'wantsSecurityRemoved'))

Expand Down
13 changes: 12 additions & 1 deletion packages/runner/injection/cross-origin.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import { createTimers } from './timers'

const findCypress = () => {
let mostSpecificSpecBridgeCypress = undefined

for (let index = 0; index < window.parent.frames.length; index++) {
const frame = window.parent.frames[index]

Expand All @@ -27,7 +29,14 @@ const findCypress = () => {
if (window.location.port === frame.location.port
&& window.location.protocol === frame.location.protocol
&& frameHostRegex.test(window.location.host)) {
return frame.Cypress
// we found a matching cypress instance. If we have a spec bridge containing a sub domain that is specific to the injection, use that spec bridge
// and overwrite and preexisting Cypress reference if applicable
if (window.location.host === frame.location.host) {
mostSpecificSpecBridgeCypress = frame.Cypress
} else if (!mostSpecificSpecBridgeCypress) {
// otherwise, set the spec bridge Cypress
mostSpecificSpecBridgeCypress = frame.Cypress
}
}
}
} catch (error) {
Expand All @@ -37,6 +46,8 @@ const findCypress = () => {
}
}
}

return mostSpecificSpecBridgeCypress
}

const Cypress = findCypress()
Expand Down
10 changes: 5 additions & 5 deletions packages/server/lib/remote_states.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class RemoteStates {
}

get (url: string) {
const state = this.remoteStates.get(cors.getOriginPolicy(url))
const state = this.remoteStates.get(cors.getSuperDomainOriginPolicy(url))

debug('getting remote state: %o for: %s', state, url)

Expand All @@ -68,16 +68,16 @@ export class RemoteStates {
}

isInOriginStack (url: string): boolean {
return this.originStack.includes(cors.getOriginPolicy(url))
return this.originStack.includes(cors.getOriginPolicy(url)) || this.originStack.includes(cors.getSuperDomainOriginPolicy(url))
}

isSecondaryOrigin (url: string): boolean {
// start at 1 to exclude the primary origin
return this.originStack.indexOf(cors.getOriginPolicy(url), 1) !== -1
return this.originStack.indexOf(cors.getOriginPolicy(url), 1) !== -1 || this.originStack.indexOf(cors.getSuperDomainOriginPolicy(url), 1) !== -1
}

isPrimaryOrigin (url: string): boolean {
return this.originStack[0] === cors.getOriginPolicy(url)
return this.originStack[0] === cors.getOriginPolicy(url) || this.originStack[0] === cors.getSuperDomainOriginPolicy(url)
}

reset () {
Expand Down Expand Up @@ -124,7 +124,7 @@ export class RemoteStates {
state = urlOrState
}

const remoteOriginPolicy = cors.getOriginPolicy(state.origin)
const remoteOriginPolicy = cors.getSuperDomainOriginPolicy(state.origin)

if (options.isCrossOrigin) {
this.remoteStates.set(remoteOriginPolicy, state)
Expand Down

0 comments on commit cb8c26d

Please sign in to comment.