mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-04 06:10:30 -06:00
* 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 a5c647c00f.
* 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>
128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
import _ from 'lodash'
|
|
import Debug from 'debug'
|
|
import { rewriteJsSourceMapAsync } from './async-rewriters'
|
|
import * as sourceMaps from './util/source-maps'
|
|
import url from 'url'
|
|
|
|
const debug = Debug('cypress:rewriter:deferred-source-map-cache')
|
|
|
|
export type DeferredSourceMapRequest = {
|
|
uniqueId: string
|
|
url: string
|
|
js?: string
|
|
sourceMap?: any
|
|
resHeaders?: any
|
|
}
|
|
|
|
const caseInsensitiveGet = (obj, lowercaseProperty) => {
|
|
for (let key of Object.keys(obj)) {
|
|
if (key.toLowerCase() === lowercaseProperty) {
|
|
return obj[key]
|
|
}
|
|
}
|
|
}
|
|
|
|
const getSourceMapHeader = (headers) => {
|
|
// sourcemap has precedence
|
|
// @see https://searchfox.org/mozilla-central/rev/dc4560dcaafd79375b9411fdbbaaebb0a59a93ac/devtools/shared/DevToolsUtils.js#611-619
|
|
return caseInsensitiveGet(headers, 'sourcemap') || caseInsensitiveGet(headers, 'x-sourcemap')
|
|
}
|
|
|
|
/**
|
|
* Holds on to data necessary to rewrite user JS to maybe generate a sourcemap at a later time,
|
|
* potentially composed with the user's own sourcemap if one is present.
|
|
*
|
|
* The purpose of this is to avoid wasting CPU time and network I/O on generating, composing, and
|
|
* sending a sourcemap along with every single rewritten JS snippet, since the source maps are
|
|
* going to be unused and discarded most of the time.
|
|
*/
|
|
export class DeferredSourceMapCache {
|
|
_idCounter = 0
|
|
requests: DeferredSourceMapRequest[] = []
|
|
requestLib: any
|
|
|
|
constructor (requestLib) {
|
|
this.requestLib = requestLib
|
|
}
|
|
|
|
defer = (request: DeferredSourceMapRequest) => {
|
|
if (this._getRequestById(request.uniqueId)) {
|
|
// prevent duplicate uniqueIds from ever existing
|
|
throw new Error(`Deferred sourcemap key "${request.uniqueId}" is not unique`)
|
|
}
|
|
|
|
// remove existing requests for this URL since they will not be loaded again
|
|
this._removeRequestsByUrl(request.url)
|
|
|
|
this.requests.push(request)
|
|
}
|
|
|
|
_removeRequestsByUrl (url: string) {
|
|
_.remove(this.requests, { url })
|
|
}
|
|
|
|
_getRequestById (uniqueId: string) {
|
|
return _.find(this.requests, { uniqueId })
|
|
}
|
|
|
|
async _getInputSourceMap (request: DeferredSourceMapRequest, headers: any) {
|
|
// prefer inline sourceMappingURL over headers
|
|
const sourceMapUrl = sourceMaps.getMappingUrl(request.js!) || getSourceMapHeader(request.resHeaders)
|
|
|
|
if (!sourceMapUrl) {
|
|
return
|
|
}
|
|
|
|
// try to decode it as a base64 string
|
|
const inline = sourceMaps.tryDecodeInlineUrl(sourceMapUrl)
|
|
|
|
if (inline) {
|
|
return inline
|
|
}
|
|
|
|
// try to load it from the web
|
|
const req = {
|
|
url: url.resolve(request.url, sourceMapUrl),
|
|
// TODO: this assumes that the sourcemap is on the same base domain, so it's safe to send the same headers
|
|
// the browser sent for this sourcemap request - but if sourcemap is on a different domain, this will not
|
|
// be true. need to use browser's cookiejar instead.
|
|
headers,
|
|
timeout: 5000,
|
|
}
|
|
|
|
try {
|
|
const { body } = await this.requestLib(req, true)
|
|
|
|
return body
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
debug('got an error loading user-provided sourcemap, serving proxy-generated sourcemap only %o', { url: request.url, headers, error })
|
|
}
|
|
}
|
|
|
|
async resolve (uniqueId: string, headers: any) {
|
|
const request = this._getRequestById(uniqueId)
|
|
|
|
if (!request) {
|
|
throw new Error(`Missing request with ID '${uniqueId}'`)
|
|
}
|
|
|
|
if (request.sourceMap) {
|
|
return request.sourceMap
|
|
}
|
|
|
|
if (!request.js) {
|
|
throw new Error('Missing JS for source map rewrite')
|
|
}
|
|
|
|
const inputSourceMap = await this._getInputSourceMap(request, headers)
|
|
|
|
// cache the sourceMap so we don't need to regenerate it
|
|
request.sourceMap = await rewriteJsSourceMapAsync(request.url, request.js, inputSourceMap)
|
|
delete request.js // won't need this again
|
|
delete request.resHeaders
|
|
|
|
return request.sourceMap
|
|
}
|
|
}
|