-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite JS/HTML using AST-based approach (#5273)
* Add winPropAccessor to security.js, remove other replacers * Add start of Cypress.resolveWindowReference * Add regexes for dot and bracket access * Some security_spec tests pass with new injection * Add resolveWindowReference unit tests * Old security_spec now passes with resolveWindowReference * Inject stub resolveWindowReference so proxy still works outside of Cypress * wip: rewrite HTML + JS with tokenizer * Move to using esprima + hyntax to rewrite JS + HTML * remove comment; oneLine makes the whole thing commented * Fix tests, apple.com edge case * wip: add getOrSet * Revert "wip: add getOrSet" This reverts commit a5c647c. * release 3.5.0 [skip ci] * use recast to replace window property accesses * replace assignments to top properly * fix yarn.lock * bump deps * update integration tests * remove old security ts? * fix integration spec * always ignore js interception failure * use globalThis instead of window * add experimentalSourceRewriting flag * restore regex-writer spec * fix types * update config_spec * add source rewriting spec * cleanup * simplify rewriting logic, move rules into rewriter package * create threaded rewriting tool for non-streaming use * update @packages/rewriter to use threads for async * use async rewriting where convenient * add worker-shim.js * add performance info to debug logs * properly handle +=, -=, ... * add proxy, rewriter to unit-tests stage * cleanup * use parse5 to rewrite HTML, strip SRI * update tests * reorganization, cleanup * rewrite ALL parent, top identifiers except in a few cases * handle many JS edge cases * ensure parse5@5.1.1 is installed * update yarn.lock * update tests * add debugging, add tests * add attempted repro for .href issue * implement source maps + extending inline source maps * update opts passing in proxy layer * fix sourcemap naming structure * update tests to account for sourcemaps * sourcemap tests * remote source maps work * comment * update rewriter tests * clean up TODOs in resolveWindowReference * remove @types/nock * clean up todos in deferred-source-map-cache * fix rewriter build script * fix concatStream import * bump expectedresultcount * clean up js-rules * threading improvements, workaround for Electron segfault * no visit_spec for now * fix 6_visit_spec * update MAX_WORKER_THREADS * add repro for #3975 * cleanup * cleanup * make better use of namedTypes and builders * get rid of the horrific closureDetectionTernary ast-types keeps track of scope, so it is unneeded * fix #3975, #3994 * add x-sourcemap, sourcemap header support * snap-shot-it 7.9.3 * add deferred-source-map-cache-spec * add tests * Throw error in driver if AST rewriting fails * Fix "location = 'relative-url'" * fix max recursion depth * slim down some fixtures * fix window.location usage * don't mess with `frames` at all * no integration tests * skip testing apple.com for now * update wording: regex-based vs. ast-based * skip real-world tests for now * add some padding to process.exit workaround * fix resolvers_spec * fix html-spec * cleanup * Update packages/rewriter/lib/js-rules.ts * Update packages/driver/src/cypress/resolvers.ts * just import find by itself * privatize typedefs for Cypress.state, remove .gitignore, remove dead code Co-authored-by: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
- Loading branch information
Showing
70 changed files
with
2,980 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import _ from 'lodash' | ||
import $Cypress from '../..' | ||
|
||
/** | ||
* Fix property reads and writes that could potentially help the AUT to break out of its iframe. | ||
* | ||
* @param currentWindow the value of `globalThis` from the scope of the window reference in question | ||
* @param accessedObject a reference to the object being accessed | ||
* @param accessedProp the property name being accessed (Symbol/number properties are not intercepted) | ||
* @param value the right-hand side of an assignment operation (accessedObject.accessedProp = value) | ||
*/ | ||
export function resolveWindowReference (this: typeof $Cypress, currentWindow: Window, accessedObject: Window | any, accessedProp: string, value?: any) { | ||
const { dom, state } = this | ||
|
||
const getTargetValue = () => { | ||
const targetValue = accessedObject[accessedProp] | ||
|
||
if (dom.isWindow(accessedObject) && accessedProp === 'location') { | ||
const targetLocation = resolveLocationReference(accessedObject) | ||
|
||
if (isValPassed) { | ||
return targetLocation.href = value | ||
} | ||
|
||
return targetLocation | ||
} | ||
|
||
if (_.isFunction(targetValue)) { | ||
return targetValue.bind(accessedObject) | ||
} | ||
|
||
return targetValue | ||
} | ||
|
||
const setTargetValue = () => { | ||
if (dom.isWindow(accessedObject) && accessedProp === 'location') { | ||
const targetLocation = resolveLocationReference(accessedObject) | ||
|
||
return targetLocation.href = value | ||
} | ||
|
||
return (accessedObject[accessedProp] = value) | ||
} | ||
|
||
const isValPassed = arguments.length === 4 | ||
|
||
const $autIframe = state('$autIframe') | ||
|
||
if (!$autIframe) { | ||
// missing AUT iframe, resolve the property access normally | ||
if (isValPassed) { | ||
return setTargetValue() | ||
} | ||
|
||
return getTargetValue() | ||
} | ||
|
||
const contentWindow = $autIframe.prop('contentWindow') | ||
|
||
if (accessedObject === currentWindow.top) { | ||
// doing a property access on topmost window, adjust accessedObject | ||
accessedObject = contentWindow | ||
} | ||
|
||
const targetValue = getTargetValue() | ||
|
||
if (!dom.isWindow(targetValue) || dom.isJquery(targetValue)) { | ||
if (isValPassed) { | ||
return setTargetValue() | ||
} | ||
|
||
return targetValue | ||
} | ||
|
||
// targetValue is a reference to a Window object | ||
|
||
if (accessedProp === 'top') { | ||
// note: `isValPassed` is not considered here because `window.top` is readonly | ||
return contentWindow | ||
} | ||
|
||
if (accessedProp === 'parent') { | ||
// note: `isValPassed` is not considered here because `window.parent` is readonly | ||
if (targetValue === currentWindow.top) { | ||
return contentWindow | ||
} | ||
|
||
return targetValue | ||
} | ||
|
||
throw new Error('unhandled resolveWindowReference') | ||
} | ||
|
||
/** | ||
* Fix `window.location` usages that would otherwise navigate to the wrong URL. | ||
* | ||
* @param currentWindow the value of `globalThis` from the scope of the location reference in question | ||
*/ | ||
export function resolveLocationReference (currentWindow: Window) { | ||
// @ts-ignore | ||
if (currentWindow.__cypressFakeLocation) { | ||
// @ts-ignore | ||
return currentWindow.__cypressFakeLocation | ||
} | ||
|
||
function _resolveHref (href: string) { | ||
const a = currentWindow.document.createElement('a') | ||
|
||
a.href = href | ||
|
||
// a.href will be resolved into the correct fully-qualified URL | ||
return a.href | ||
} | ||
|
||
function assign (href: string) { | ||
return currentWindow.location.assign(_resolveHref(href)) | ||
} | ||
|
||
function replace (href: string) { | ||
return currentWindow.location.replace(_resolveHref(href)) | ||
} | ||
|
||
function setHref (href: string) { | ||
return currentWindow.location.href = _resolveHref(href) | ||
} | ||
|
||
const locationKeys = Object.keys(currentWindow.location) | ||
|
||
const fakeLocation = {} | ||
|
||
_.reduce(locationKeys, (acc, cur) => { | ||
// set a dummy value, the proxy will handle sets/gets | ||
acc[cur] = Symbol.for('Proxied') | ||
|
||
return acc | ||
}, {}) | ||
|
||
// @ts-ignore | ||
return currentWindow.__cypressFakeLocation = new Proxy(fakeLocation, { | ||
get (_target, prop, _receiver) { | ||
if (prop === 'assign') { | ||
return assign | ||
} | ||
|
||
if (prop === 'replace') { | ||
return replace | ||
} | ||
|
||
return currentWindow.location[prop] | ||
}, | ||
set (_obj, prop, value) { | ||
if (prop === 'href') { | ||
return setHref(value) | ||
} | ||
|
||
return currentWindow.location[prop] = value | ||
}, | ||
}) | ||
} |
Oops, something went wrong.
6960f7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circle has built the
linux x64
version of the Test Runner.You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.
You will need to use custom
CYPRESS_INSTALL_BINARY
url and install Cypress using an url instead of the version.6960f7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppVeyor has built the
win32 ia32
version of the Test Runner.You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.
You will need to use custom
CYPRESS_INSTALL_BINARY
url and install Cypress using an url instead of the version.Instructions are included below, depending on the shell you are using.
In Command Prompt (
cmd.exe
):In PowerShell:
In Git Bash:
Using
cross-env
:If the above commands do not work for you, you can also try using
cross-env
:6960f7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppVeyor has built the
win32 x64
version of the Test Runner.You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.
You will need to use custom
CYPRESS_INSTALL_BINARY
url and install Cypress using an url instead of the version.Instructions are included below, depending on the shell you are using.
In Command Prompt (
cmd.exe
):In PowerShell:
In Git Bash:
Using
cross-env
:If the above commands do not work for you, you can also try using
cross-env
:6960f7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circle has built the
darwin x64
version of the Test Runner.You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.
You will need to use custom
CYPRESS_INSTALL_BINARY
url and install Cypress using an url instead of the version.