mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-06 06:29:45 -06:00
fix: add column, line, and method check to integrity check (#25094)
This commit is contained in:
@@ -6,7 +6,6 @@ const esbuild = require('esbuild')
|
||||
const snapshotMetadata = require('@tooling/v8-snapshot/cache/prod-darwin/snapshot-meta.cache.json')
|
||||
const tempDir = require('temp-dir')
|
||||
const workingDir = path.join(tempDir, 'binary-cleanup-workdir')
|
||||
const bytenode = require('bytenode')
|
||||
|
||||
fs.ensureDirSync(workingDir)
|
||||
|
||||
@@ -137,6 +136,8 @@ const createServerEntryPointBundle = async (buildAppDir) => {
|
||||
console.log(`compiling server entry point bundle to ${path.join(buildAppDir, 'packages', 'server', 'index.jsc')}`)
|
||||
|
||||
// Use bytenode to compile the entry point bundle. This will save time on the v8 compile step and ensure the integrity of the entry point
|
||||
const bytenode = await import('bytenode')
|
||||
|
||||
await bytenode.compileFile({
|
||||
filename: path.join(buildAppDir, 'packages', 'server', 'index.js'),
|
||||
output: path.join(buildAppDir, 'packages', 'server', 'index.jsc'),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
const Module = require('module')
|
||||
const path = require('path')
|
||||
import Module from 'module'
|
||||
import path from 'path'
|
||||
import { runBytecodeAsModule } from 'bytenode'
|
||||
|
||||
process.env.CYPRESS_INTERNAL_ENV = process.env.CYPRESS_INTERNAL_ENV || 'production'
|
||||
try {
|
||||
require('bytenode')
|
||||
const filename = path.join(__dirname, 'packages', 'server', 'index.jsc')
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
Module._extensions['.jsc']({
|
||||
runBytecodeAsModule({
|
||||
require: module.require,
|
||||
id: filename,
|
||||
filename,
|
||||
|
||||
@@ -2,6 +2,8 @@ const OrigError = Error
|
||||
const captureStackTrace = Error.captureStackTrace
|
||||
const toString = Function.prototype.toString
|
||||
const callFn = Function.call
|
||||
const filter = Array.prototype.filter
|
||||
const startsWith = 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:
|
||||
@@ -22,7 +24,7 @@ const stackIntegrityCheck = function stackIntegrityCheck (options) {
|
||||
const tempError = new OrigError
|
||||
|
||||
captureStackTrace(tempError, arguments.callee)
|
||||
const stack = tempError.stack.filter((frame) => !frame.getFileName().startsWith('node:internal') && !frame.getFileName().startsWith('node:electron'))
|
||||
const stack = filter.call(tempError.stack, (frame) => !startsWith.call(frame.getFileName(), 'node:internal') && !startsWith.call(frame.getFileName(), 'node:electron'))
|
||||
|
||||
OrigError.prepareStackTrace = originalPrepareStackTrace
|
||||
OrigError.stackTraceLimit = originalStackTraceLimit
|
||||
@@ -33,9 +35,11 @@ const stackIntegrityCheck = function stackIntegrityCheck (options) {
|
||||
}
|
||||
|
||||
for (let index = 0; index < options.stackToMatch.length; index++) {
|
||||
const { functionName: expectedFunctionName, fileName: expectedFileName } = options.stackToMatch[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}`)
|
||||
@@ -46,13 +50,37 @@ const stackIntegrityCheck = function stackIntegrityCheck (options) {
|
||||
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 (startsWith.call !== callFn) {
|
||||
console.error(`Integrity check failed for startsWith.call`)
|
||||
throw new Error(integrityErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
function validateFilter () {
|
||||
if (filter.call !== callFn) {
|
||||
console.error(`Integrity check failed for filter.call`)
|
||||
throw new Error(integrityErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
function validateToString () {
|
||||
if (toString.call !== callFn) {
|
||||
console.error(`Integrity check failed for toString.call`)
|
||||
throw new Error('Integrity check failed for toString.call')
|
||||
throw new Error(integrityErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +88,7 @@ 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 (toString.call(electron.app.getAppPath) !== 'function getAppPath() { [native code] }') {
|
||||
console.error(`Integrity check failed for toString.call(electron.app.getAppPath)`)
|
||||
throw new Error(`Integrity check failed for toString.call(electron.app.getAppPath)`)
|
||||
throw new Error(integrityErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +134,8 @@ function integrityCheck (options) {
|
||||
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)
|
||||
@@ -143,13 +173,17 @@ function integrityCheck (options) {
|
||||
fileName: 'evalmachine.<anonymous>',
|
||||
},
|
||||
{
|
||||
functionName: 'Module2._extensions.<computed>',
|
||||
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,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@@ -17,6 +17,8 @@ const getBinaryEntryPointSource = async () => {
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
write: false,
|
||||
minify: true,
|
||||
treeShaking: true,
|
||||
})
|
||||
|
||||
return esbuildResult.outputFiles[0].text
|
||||
|
||||
@@ -302,6 +302,24 @@ const runIntegrityTest = async function (buildAppExecutable, buildAppDir, e2e) {
|
||||
const allowList = ['regeneratorRuntime', '__core-js_shared__', 'getSnapshotResult', 'supportTypeScript']
|
||||
|
||||
await testAlteringEntryPoint(`(${compareGlobals.toString()})()`, `extra keys in electron process: ${allowList}\nIntegrity check failed with expected stack length 9 but got 10`)
|
||||
|
||||
const testTemporarilyRewritingEntryPoint = async () => {
|
||||
const file = path.join(buildAppDir, 'index.js')
|
||||
const backupFile = path.join(buildAppDir, 'index.js.bak')
|
||||
const contents = await fs.readFile(file)
|
||||
|
||||
// Backup state
|
||||
await fs.move(file, backupFile)
|
||||
|
||||
// Modify app
|
||||
await fs.writeFile(file, `console.log("rewritten code");const fs=require('fs');const { join } = require('path');fs.writeFileSync(join(__dirname,'index.js'),fs.readFileSync(join(__dirname,'index.js.bak')));${contents}`)
|
||||
await runErroringProjectTest(buildAppExecutable, e2e, 'temporarily rewriting index.js', 'Integrity check failed with expected column number 2573 but got')
|
||||
|
||||
// Restore original state
|
||||
await fs.move(backupFile, file, { overwrite: true })
|
||||
}
|
||||
|
||||
await testTemporarilyRewritingEntryPoint()
|
||||
}
|
||||
|
||||
const test = async function (buildAppExecutable, buildAppDir) {
|
||||
|
||||
Reference in New Issue
Block a user