mirror of
https://github.com/cypress-io/cypress.git
synced 2025-12-30 19:19:53 -06:00
* chore: upgrade electron to 32.2.0 -- run ci * fix node versions * fix build * fix evil-dns * various fixes * fix srcdoc * various fixes * update package.json * update yarn.lock * various fixes * fix integrity check * fix binary verification * various fixes * update yarn.lock * fix typo * fix lock file * fix tests * fix tests * various fixes * various fixes * various fixes * fix stuff * fix things * try to fix errors * fix * updates * add yarn berry * upgrade electron * attempt with gcc * blank * update arm64 executor * try to fix the arm64 issue * fix arm64 * bump cache * attempt to fix arm64 again * attempt to fix arm64 again * fix darwin problems * merge release/14.0.0 * update yarn.lock * blank -- run ci * blank * blank * blank * try something * try something * clean up * blank * try to fix sessions issue * fixes * get more info * get more info * try something * fix * try something * try something * try something * tweak * one more thing * let us see if this works * blank * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * Apply suggestions from code review * Apply suggestions from code review * Update cli/CHANGELOG.md Co-authored-by: Jennifer Shehane <jennifer@cypress.io> * Update cli/CHANGELOG.md Co-authored-by: Jennifer Shehane <jennifer@cypress.io> * Update CHANGELOG.md * Update cache-version.txt * fix nx (maybe) * Update package.json * try something * Update packages/app/src/runner/aut-iframe.ts * bump version * try to bust cache * try to invalidate cache * Update cli/CHANGELOG.md * suppress benign warnings * Apply suggestions from code review * PR comments * PR comment * Apply suggestions from code review * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * Update cli/CHANGELOG.md Co-authored-by: Matt Schile <mschile@cypress.io> * update debug scenario 4 regex * Update scripts/binary/trigger-publish-binary-pipeline.js --------- Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Jennifer Shehane <jennifer@cypress.io> Co-authored-by: Matt Schile <mschile@cypress.io>
215 lines
8.6 KiB
JavaScript
215 lines
8.6 KiB
JavaScript
const OrigError = Error
|
|
const originalCaptureStackTrace = Error.captureStackTrace
|
|
const originalToString = Function.prototype.toString
|
|
const originalCallFn = Function.call
|
|
const originalFilter = Array.prototype.filter
|
|
const originalStartsWith = String.prototype.startsWith
|
|
|
|
const integrityErrorMessage = `
|
|
We detected an issue with the integrity of the Cypress binary. It may have been modified and cannot run. We recommend re-installing the Cypress binary with:
|
|
|
|
\`cypress cache clear && cypress install\`
|
|
`
|
|
|
|
const stackIntegrityCheck = function stackIntegrityCheck (options) {
|
|
const originalStackTraceLimit = OrigError.stackTraceLimit
|
|
const originalPrepareStackTrace = OrigError.prepareStackTrace
|
|
|
|
OrigError.stackTraceLimit = Infinity
|
|
|
|
OrigError.prepareStackTrace = function (_, stack) {
|
|
return stack
|
|
}
|
|
|
|
const tempError = new OrigError
|
|
|
|
originalCaptureStackTrace(tempError, arguments.callee)
|
|
const stack = originalFilter.call(tempError.stack, (frame) => !originalStartsWith.call(frame.getFileName(), 'node:internal') && !originalStartsWith.call(frame.getFileName(), 'node:electron'))
|
|
|
|
OrigError.prepareStackTrace = originalPrepareStackTrace
|
|
OrigError.stackTraceLimit = originalStackTraceLimit
|
|
|
|
if (stack.length !== options.stackToMatch.length) {
|
|
console.error(`Integrity check failed with expected stack length ${options.stackToMatch.length} but got ${stack.length}`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
|
|
for (let index = 0; index < options.stackToMatch.length; index++) {
|
|
const { functionName: expectedFunctionName, fileName: expectedFileName, line: expectedLineNumber, column: expectedColumnNumber } = options.stackToMatch[index]
|
|
const actualFunctionName = stack[index].getFunctionName()
|
|
const actualFileName = stack[index].getFileName()
|
|
const actualColumnNumber = stack[index].getColumnNumber()
|
|
const actualLineNumber = stack[index].getLineNumber()
|
|
|
|
if (expectedFunctionName && actualFunctionName !== expectedFunctionName) {
|
|
console.error(`Integrity check failed with expected function name ${expectedFunctionName} but got ${actualFunctionName}`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
|
|
if (expectedFileName && actualFileName !== expectedFileName) {
|
|
console.error(`Integrity check failed with expected file name ${expectedFileName} but got ${actualFileName}`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
|
|
if (expectedLineNumber && actualLineNumber !== expectedLineNumber) {
|
|
console.error(`Integrity check failed with expected line number ${expectedLineNumber} but got ${actualLineNumber}`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
|
|
if (expectedColumnNumber && actualColumnNumber !== expectedColumnNumber) {
|
|
console.error(`Integrity check failed with expected column number ${expectedColumnNumber} but got ${actualColumnNumber}`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
}
|
|
|
|
function validateStartsWith () {
|
|
if (originalStartsWith.call !== originalCallFn) {
|
|
console.error(`Integrity check failed for startsWith.call`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
|
|
function validateFilter () {
|
|
if (originalFilter.call !== originalCallFn) {
|
|
console.error(`Integrity check failed for filter.call`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
|
|
function validateToString () {
|
|
if (originalToString.call !== originalCallFn) {
|
|
console.error(`Integrity check failed for toString.call`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
|
|
function validateElectron (electron) {
|
|
// Hard coded function as this is electron code and there's not an easy way to get the function string at package time. If this fails on an updated version of electron, we'll need to update this.
|
|
if (originalToString.call(electron.app.getAppPath) !== 'function getAppPath() { [native code] }') {
|
|
console.error(`Integrity check failed for toString.call(electron.app.getAppPath)`, originalToString.call(electron.app.getAppPath))
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
|
|
function validateFs (fs) {
|
|
// Hard coded function as this is electron code and there's not an easy way to get the function string at package time. If this fails on an updated version of electron, we'll need to update this.
|
|
if (originalToString.call(fs.readFileSync) !== `function(e,r){const n=splitPath(e);if(!n.isAsar)return w.apply(this,arguments);const{asarPath:i,filePath:a}=n,o=getOrCreateArchive(i);if(!o)throw createError("INVALID_ARCHIVE",{asarPath:i});const c=o.getFileInfo(a);if(!c)throw createError("NOT_FOUND",{asarPath:i,filePath:a});if(0===c.size)return r?"":s.Buffer.alloc(0);if(c.unpacked){const e=o.copyFileOut(a);return t.readFileSync(e,r)}if(r){if("string"==typeof r)r={encoding:r};else if("object"!=typeof r)throw new TypeError("Bad arguments")}else r={encoding:null};const{encoding:f}=r,l=s.Buffer.alloc(c.size),u=o.getFdAndValidateIntegrityLater();if(!(u>=0))throw createError("NOT_FOUND",{asarPath:i,filePath:a});return logASARAccess(i,a,c.offset),t.readSync(u,l,0,c.size,c.offset),validateBufferIntegrity(l,c.integrity),f?l.toString(f):l}`) {
|
|
console.error(`Integrity check failed for toString.call(fs.readFileSync)`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
|
|
function validateCrypto (crypto) {
|
|
if (originalToString.call(crypto.createHmac) !== `CRYPTO_CREATE_HMAC_TO_STRING`) {
|
|
console.error(`Integrity check failed for toString.call(crypto.createHmac)`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
|
|
if (originalToString.call(crypto.Hmac.prototype.update) !== `CRYPTO_HMAC_UPDATE_TO_STRING`) {
|
|
console.error(`Integrity check failed for toString.call(crypto.Hmac.prototype.update)`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
|
|
if (originalToString.call(crypto.Hmac.prototype.digest) !== `CRYPTO_HMAC_DIGEST_TO_STRING`) {
|
|
console.error(`Integrity check failed for toString.call(crypto.Hmac.prototype.digest)`)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
|
|
function validateFile ({ filePath, crypto, fs, expectedHash, errorMessage }) {
|
|
const hash = crypto.createHmac('md5', 'HMAC_SECRET').update(fs.readFileSync(filePath, 'utf8')).digest('hex')
|
|
|
|
if (hash !== expectedHash) {
|
|
console.error(errorMessage)
|
|
throw new Error(integrityErrorMessage)
|
|
}
|
|
}
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
function integrityCheck (options) {
|
|
const require = options.require
|
|
const electron = require('electron')
|
|
const fs = require('fs')
|
|
const crypto = require('crypto')
|
|
|
|
// 1. Validate that the native functions we are using haven't been tampered with
|
|
validateStartsWith()
|
|
validateFilter()
|
|
validateToString()
|
|
validateElectron(electron)
|
|
validateFs(fs)
|
|
validateCrypto(crypto)
|
|
|
|
const appPath = electron.app.getAppPath()
|
|
|
|
// 2. Validate that the stack trace is what we expect
|
|
stackIntegrityCheck({ stackToMatch:
|
|
[
|
|
{
|
|
functionName: 'integrityCheck',
|
|
fileName: '<embedded>',
|
|
},
|
|
{
|
|
fileName: '<embedded>',
|
|
},
|
|
{
|
|
functionName: 'snapshotRequire',
|
|
// eslint-disable-next-line no-undef
|
|
fileName: [appPath, 'packages', 'server', 'index.jsc'].join(PATH_SEP),
|
|
},
|
|
{
|
|
functionName: 'runWithSnapshot',
|
|
// eslint-disable-next-line no-undef
|
|
fileName: [appPath, 'packages', 'server', 'index.jsc'].join(PATH_SEP),
|
|
},
|
|
{
|
|
functionName: 'hookRequire',
|
|
// eslint-disable-next-line no-undef
|
|
fileName: [appPath, 'packages', 'server', 'index.jsc'].join(PATH_SEP),
|
|
},
|
|
{
|
|
functionName: 'startCypress',
|
|
// eslint-disable-next-line no-undef
|
|
fileName: [appPath, 'packages', 'server', 'index.jsc'].join(PATH_SEP),
|
|
},
|
|
{
|
|
// eslint-disable-next-line no-undef
|
|
fileName: [appPath, 'packages', 'server', 'index.jsc'].join(PATH_SEP),
|
|
},
|
|
{
|
|
functionName: 'v',
|
|
// eslint-disable-next-line no-undef
|
|
fileName: [appPath, 'index.js'].join(PATH_SEP),
|
|
line: 1,
|
|
column: 2573,
|
|
},
|
|
{
|
|
// eslint-disable-next-line no-undef
|
|
fileName: [appPath, 'index.js'].join(PATH_SEP),
|
|
line: 1,
|
|
column: 2764,
|
|
},
|
|
],
|
|
})
|
|
|
|
// 3. Validate the three pieces of the entry point: the main index file, the bundled jsc file, and the bytenode node module
|
|
validateFile({
|
|
// eslint-disable-next-line no-undef
|
|
filePath: [appPath, 'index.js'].join(PATH_SEP),
|
|
crypto,
|
|
fs,
|
|
expectedHash: 'MAIN_INDEX_HASH',
|
|
errorMessage: 'Integrity check failed for main index.js file',
|
|
})
|
|
|
|
validateFile({
|
|
// eslint-disable-next-line no-undef
|
|
filePath: [appPath, 'packages', 'server', 'index.jsc'].join(PATH_SEP),
|
|
crypto,
|
|
fs,
|
|
expectedHash: 'INDEX_JSC_HASH',
|
|
errorMessage: 'Integrity check failed for main server index.jsc file',
|
|
})
|
|
}
|