From d26d877db86048c4e1392415574967863a052a15 Mon Sep 17 00:00:00 2001 From: Tim Griesser Date: Wed, 15 Feb 2023 08:20:57 -0500 Subject: [PATCH] chore: internal request preflight (#25772) --------- Co-authored-by: Emily Rohrbough Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: cypress-bot[bot] <2f0651858c6e38e0+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel Co-authored-by: Matt Henkes Co-authored-by: Zach Bloomquist --- .circleci/workflows.yml | 12 +- packages/errors/src/errors.ts | 8 +- packages/server/config/app.json | 2 +- packages/server/lib/cloud/api.ts | 218 ++++++++++++++---- packages/server/lib/cloud/encryption.ts | 88 +++++++ packages/server/lib/cloud/routes.ts | 2 + packages/server/package.json | 4 +- .../server/test/integration/cypress_spec.js | 7 + packages/server/test/unit/cloud/api_spec.js | 76 ++++++ .../server/test/unit/cloud/encryption_spec.js | 66 ++++++ .../server/test/unit/modes/record_spec.js | 10 + scripts/after-pack-hook.js | 19 +- system-tests/__snapshots__/record_spec.js | 120 ++++++++++ system-tests/lib/serverStub.ts | 57 ++++- system-tests/package.json | 4 +- system-tests/test/record_spec.js | 180 ++++++++++++++- .../cache/darwin/snapshot-meta.json | 88 +++++++ .../cache/linux/snapshot-meta.json | 89 ++++++- .../cache/win32/snapshot-meta.json | 91 +++++++- tooling/v8-snapshot/package.json | 2 +- yarn.lock | 10 + 21 files changed, 1077 insertions(+), 76 deletions(-) create mode 100644 packages/server/lib/cloud/encryption.ts create mode 100644 packages/server/test/unit/cloud/encryption_spec.js diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 8ef2152d5aa1..939160e8b4e8 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -28,8 +28,7 @@ mainBuildFilters: &mainBuildFilters only: - develop - /^release\/\d+\.\d+\.\d+$/ - # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - - 'update-v8-snapshot-cache-on-develop' + - 'tgriesser/spike/spike' # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -38,8 +37,7 @@ macWorkflowFilters: &darwin-workflow-filters when: or: - equal: [ develop, << pipeline.git.branch >> ] - # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] + - equal: [ 'tgriesser/spike/spike', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -48,8 +46,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters when: or: - equal: [ develop, << pipeline.git.branch >> ] - # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] + - equal: [ 'tgriesser/spike/spike', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -134,8 +131,7 @@ commands: - run: name: Check current branch to persist artifacts command: | - # Keep update-v8-snapshot-cache-on-develop to ensure that we fully test v8 snapshot cache updates - if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" ]]; then + if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "tgriesser/spike/spike" ]]; then echo "Not uploading artifacts or posting install comment for this branch." circleci-agent step halt fi diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 4cc53bfa50d6..42f46ac6b9ec 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -205,7 +205,13 @@ export const AllCypressErrors = { ${fmt.highlightSecondary(arg1.response)}` }, - CLOUD_UNKNOWN_CREATE_RUN_WARNING: (arg1: {props: any, message: string}) => { + CLOUD_UNKNOWN_CREATE_RUN_WARNING: (arg1: {props?: any, message: string}) => { + if (!Object.keys(arg1.props).length) { + return errTemplate`\ + Warning from Cypress Cloud: ${fmt.highlight(arg1.message)} + ` + } + return errTemplate`\ Warning from Cypress Cloud: ${fmt.highlight(arg1.message)} diff --git a/packages/server/config/app.json b/packages/server/config/app.json index 5136054b1175..dc1e65a8d026 100644 --- a/packages/server/config/app.json +++ b/packages/server/config/app.json @@ -11,4 +11,4 @@ "production": { "api_url": "https://api.cypress.io/" } -} +} \ No newline at end of file diff --git a/packages/server/lib/cloud/api.ts b/packages/server/lib/cloud/api.ts index 2082484deeab..179ae5cbdfa2 100644 --- a/packages/server/lib/cloud/api.ts +++ b/packages/server/lib/cloud/api.ts @@ -2,7 +2,6 @@ const _ = require('lodash') const os = require('os') const debug = require('debug')('cypress:server:cloud:api') const request = require('@cypress/request-promise') -const Promise = require('bluebird') const humanInterval = require('human-interval') const RequestErrors = require('@cypress/request-promise/errors') @@ -11,13 +10,17 @@ const pkg = require('@packages/root') const machineId = require('./machine_id') const errors = require('../errors') -const { apiRoutes } = require('./routes') +const { apiUrl, apiRoutes, makeRoutes } = require('./routes') + +import Bluebird from 'bluebird' +import type { OptionsWithUrl } from 'request-promise' +import * as enc from './encryption' const THIRTY_SECONDS = humanInterval('30 seconds') const SIXTY_SECONDS = humanInterval('60 seconds') const TWO_MINUTES = humanInterval('2 minutes') -const DELAYS = process.env.API_RETRY_INTERVALS ? process.env.API_RETRY_INTERVALS.split(',').map(_.toNumber) : [ +const DELAYS: number[] = process.env.API_RETRY_INTERVALS ? process.env.API_RETRY_INTERVALS.split(',').map(_.toNumber) : [ THIRTY_SECONDS, SIXTY_SECONDS, TWO_MINUTES, @@ -30,13 +33,27 @@ const runnerCapabilities = { let responseCache = {} -const rp = request.defaults((params, callback) => { +class DecryptionError extends Error { + isDecryptionError = true + constructor (message: string) { + super(message) + this.name = 'DecryptionError' + } +} + +export interface CypressRequestOptions extends OptionsWithUrl { + encrypt?: boolean | 'always' + method: string + cacheable?: boolean +} + +const rp = request.defaults((params: CypressRequestOptions, callback) => { let resp if (params.cacheable && (resp = getCachedResponse(params))) { debug('resolving with cached response for ', params.url) - return Promise.resolve(resp) + return Bluebird.resolve(resp) } _.defaults(params, { @@ -44,6 +61,7 @@ const rp = request.defaults((params, callback) => { proxy: null, gzip: true, cacheable: false, + encrypt: false, rejectUnauthorized: true, }) @@ -64,8 +82,41 @@ const rp = request.defaults((params, callback) => { params.auth && params.auth.bearer, ) - return request[method](params, callback) - .promise() + return Bluebird.try(async () => { + // If we're encrypting the request, we generate the JWE + // and set it to the JSON body for the request + if (params.encrypt === true || params.encrypt === 'always') { + const { secretKey, jwe } = await enc.encryptRequest(params) + + params.transform = async function (body, response) { + if (response.headers['x-cypress-encrypted'] || params.encrypt === 'always' && response.statusCode < 500) { + let decryptedBody + + try { + decryptedBody = await enc.decryptResponse(body, secretKey) + } catch (e) { + throw new DecryptionError(e.message) + } + + // If we've hit an encrypted payload error case, we need to re-constitute the error + // as it would happen normally, with the body as an error property + if (response.statusCode > 400) { + throw new RequestErrors.StatusCodeError(response.statusCode, decryptedBody, {}, decryptedBody) + } + + return decryptedBody + } + + return body + } + + params.body = jwe + + headers['x-cypress-encrypted'] = '1' + } + + return request[method](params, callback).promise() + }) .tap((resp) => { if (params.cacheable) { debug('caching response for ', params.url) @@ -91,11 +142,11 @@ const retryWithBackoff = (fn) => { if (process.env.DISABLE_API_RETRIES) { debug('api retries disabled') - return Promise.try(() => fn(0)) + return Bluebird.try(() => fn(0)) } return (attempt = (retryIndex) => { - return Promise + return Bluebird .try(() => fn(retryIndex)) .catch(isRetriableError, (err) => { if (retryIndex > DELAYS.length) { @@ -114,7 +165,7 @@ const retryWithBackoff = (fn) => { retryIndex++ - return Promise + return Bluebird .delay(delay) .then(() => { debug(`retry #${retryIndex} after ${delay}ms`) @@ -122,6 +173,9 @@ const retryWithBackoff = (fn) => { return attempt(retryIndex) }) }) + .catch(RequestErrors.TransformError, (err) => { + throw err.cause + }) })(0) } @@ -144,9 +198,15 @@ const tagError = function (err) { } // retry on timeouts, 5xx errors, or any error without a status code +// do not retry on decryption errors const isRetriableError = (err) => { - return (err instanceof Promise.TimeoutError) || - (500 <= err.statusCode && err.statusCode < 600) || + // TransformError means something failed in decryption handling + if (err instanceof RequestErrors.TransformError) { + return false + } + + return err instanceof Bluebird.TimeoutError || + (err.statusCode >= 500 && err.statusCode < 600) || (err.statusCode == null) } @@ -166,9 +226,32 @@ export type CreateRunOptions = { timeout?: number } +let preflightResult = { + encrypt: true, + apiUrl, +} + +let recordRoutes = apiRoutes + module.exports = { rp, + // For internal testing + setPreflightResult (toSet) { + preflightResult = { + ...preflightResult, + ...toSet, + } + }, + + resetPreflightResult () { + recordRoutes = apiRoutes + preflightResult = { + encrypt: true, + apiUrl, + } + }, + ping () { return rp.get(apiRoutes.ping()) .catch(tagError) @@ -187,39 +270,52 @@ module.exports = { }, createRun (options: CreateRunOptions) { - return retryWithBackoff((attemptIndex) => { - const body = { - ..._.pick(options, [ - 'autoCancelAfterFailures', - 'ci', - 'specs', - 'commit', - 'group', - 'platform', - 'parallel', - 'ciBuildId', - 'projectId', - 'recordKey', - 'specPattern', - 'tags', - 'testingType', - ]), - runnerCapabilities, - } + const preflightOptions = _.pick(options, ['projectId', 'ciBuildId', 'browser', 'testingType', 'parallel']) + + return this.preflight(preflightOptions).then((result) => { + const { warnings } = result + + return retryWithBackoff((attemptIndex) => { + const body = { + ..._.pick(options, [ + 'autoCancelAfterFailures', + 'ci', + 'specs', + 'commit', + 'group', + 'platform', + 'parallel', + 'ciBuildId', + 'projectId', + 'recordKey', + 'specPattern', + 'tags', + 'testingType', + ]), + runnerCapabilities, + } - return rp.post({ - body, - url: apiRoutes.runs(), - json: true, - timeout: options.timeout != null ? options.timeout : SIXTY_SECONDS, - headers: { - 'x-route-version': '4', - 'x-cypress-request-attempt': attemptIndex, - }, + return rp.post({ + body, + url: recordRoutes.runs(), + json: true, + encrypt: preflightResult.encrypt, + timeout: options.timeout != null ? options.timeout : SIXTY_SECONDS, + headers: { + 'x-route-version': '4', + 'x-cypress-request-attempt': attemptIndex, + }, + }) + .tap((result) => { + // Tack on any preflight warnings prior to run warnings + if (warnings) { + result.warnings = warnings.concat(result.warnings ?? []) + } + }) }) - .catch(RequestErrors.StatusCodeError, formatResponseBody) - .catch(tagError) }) + .catch(RequestErrors.StatusCodeError, formatResponseBody) + .catch(tagError) }, createInstance (options) { @@ -235,8 +331,9 @@ module.exports = { return retryWithBackoff((attemptIndex) => { return rp.post({ body, - url: apiRoutes.instances(runId), + url: recordRoutes.instances(runId), json: true, + encrypt: preflightResult.encrypt, timeout: timeout != null ? timeout : SIXTY_SECONDS, headers: { 'x-route-version': '5', @@ -254,8 +351,9 @@ module.exports = { return retryWithBackoff((attemptIndex) => { return rp.post({ - url: apiRoutes.instanceTests(instanceId), + url: recordRoutes.instanceTests(instanceId), json: true, + encrypt: preflightResult.encrypt, timeout: timeout || SIXTY_SECONDS, headers: { 'x-route-version': '1', @@ -272,7 +370,7 @@ module.exports = { updateInstanceStdout (options) { return retryWithBackoff((attemptIndex) => { return rp.put({ - url: apiRoutes.instanceStdout(options.instanceId), + url: recordRoutes.instanceStdout(options.instanceId), json: true, timeout: options.timeout != null ? options.timeout : SIXTY_SECONDS, body: { @@ -292,8 +390,9 @@ module.exports = { postInstanceResults (options) { return retryWithBackoff((attemptIndex) => { return rp.post({ - url: apiRoutes.instanceResults(options.instanceId), + url: recordRoutes.instanceResults(options.instanceId), json: true, + encrypt: preflightResult.encrypt, timeout: options.timeout != null ? options.timeout : SIXTY_SECONDS, headers: { 'x-route-version': '1', @@ -329,7 +428,7 @@ module.exports = { }, postLogout (authToken) { - return Promise.join( + return Bluebird.join( this.getAuthUrls(), machineId.machineId(), (urls, machineId) => { @@ -353,5 +452,30 @@ module.exports = { responseCache = {} }, + preflight (preflightInfo) { + return retryWithBackoff(async (attemptIndex) => { + const preflightBase = process.env.CYPRESS_API_URL ? apiUrl.replace('api', 'api-proxy') : apiUrl + const result = await rp.post({ + url: `${preflightBase}preflight`, + body: { + apiUrl, + envUrl: process.env.CYPRESS_API_URL, + ...preflightInfo, + }, + headers: { + 'x-route-version': '1', + 'x-cypress-request-attempt': attemptIndex, + }, + json: true, + encrypt: 'always', + }) + + preflightResult = result // { encrypt: boolean, apiUrl: string } + recordRoutes = makeRoutes(result.apiUrl) + + return result + }) + }, + retryWithBackoff, } diff --git a/packages/server/lib/cloud/encryption.ts b/packages/server/lib/cloud/encryption.ts new file mode 100644 index 000000000000..77a7c99ad54f --- /dev/null +++ b/packages/server/lib/cloud/encryption.ts @@ -0,0 +1,88 @@ +import crypto from 'crypto' +import { TextEncoder, promisify } from 'util' +import { generalDecrypt, GeneralJWE } from 'jose' +import base64Url from 'base64url' +import type { CypressRequestOptions } from './api' +import { deflateRaw as deflateRawCb } from 'zlib' + +const deflateRaw = promisify(deflateRawCb) + +const CY_TEST = `LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFyZ0pGb2FuMFROTUxWSUNvZWF2WQpJVWtqNWJaM2QxTlVJdVU4WjM1b2hzcUFGWHY1eGhRRzd5MWdkeTR1SVZOSFU0a1RmUERBZEk1MnRWcGE5UG1yCm5OSDhsZE5kMjRwemVFNm1CeE91MlVDQ1d3VEY5eS9BUGZqNjVkczJSSTMwR09oZm95Q1pyQndxRU1zdWJ1MTUKVVNqbFBUcEFoWlFZN2Y2bHA4TTZMYU55SUhLMzYyMDgvZFp6aWs4Q1NLWmwya0E0TUN4eWxhUElWNVVWZG0rRQpkdjJhdm1Hcy9vQzVUV1VRNzlVSmJyMDllem1oZWExcS81VnpvajRvODlKSkNnelFhcllvL1QrNVlreWJ0Z2hkCjN3NnNPSjBCQ2NrUUV5MGpXVWJWUnhSa084VGJsckxXbC9Rd0Ryd1EvRERQaEZaSGhIbCtCL3JRTldsYTFXdEoKclFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t` +const CY_STAGING = `LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFxZE1OazZYVkFhV3VlT0lXZ3V2aQpDTlhPRGVtMHRINmo0NnFTWUhJZFcyU1N5NHU0ZFpGd1VHQldlZjBEbDVmeGhZa1BFczBxUDJHUnlUZnY5YjNXCk9xWEJFQmFyNXMyYzJSMGd1RzdqNGtidTlZZklRQWpWejZndUtQMTIzd3VKSjFmcEU5T3pXWlZmUi9pQWl4b1gKTi82aEFhSHNMT1RlNXROdTVESzNOQnUxa3VJTTExcDZScEo5bGgvbWVFK3JObzRZVWUvZ2Jzam1mZmJiODRGeQpqWk1sQW9YSnYxU3lKK2phdTNMa3JkdzMybWYrczMyd2NLUnNpbmp1STgrZndDT2lEb2xnZW9NdEhta2tXVS8xCnJvUnVEcGd6d2FZemxLbUhRODNWQTlOTUhvNmMwVU40MzlBMnVtRFQ4ek94SjJjQUY0U0RiWTV3RnBnd3cvUVgKd1FJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t` +const CY_DEVELOPMENT = `LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3aEpiaG1pbHJsT012V3V4WHdWcAp2MWZBSUZWNGlrcS8vOGhNdFpCbVk1WDRaNjVwQ3pqZjJzYVNXSnJVWExMd1R4d3VpOGlFd3hCb2Jlelc0d1ZsCk9xYjlEWE5RbnFvd2hrcGZNUmlhTjM0ZEtYZHZLZi9RaFAvU2tJbVFCYi9EQzFISWdhYjRXZGpJOFlwZnhHQWoKdkJhbU1TcEErVGdDekVGaWRuR3p6OUk5SmY0bUVBTHdIT3NGaVh3NkpqMmlXeDZBT1JtR3FBL3FYWStPY25wdQo1dmxISEl3cllxU1hQZjU2OHRUV2dZOVNvK0ZmYVhlK2s0MnZGeDAyRjdGRXlCZmJxMnBNdjhJWWJnbDViaE0rClRjUFVEYnpBZ0FWNUhlRENKKzQ2RklLNW9JZHV5WjhXQUQxRGFJRTQyWHBGNjlhWjRkcE11YnR5VDBIbXBHeHIKM3dJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t` +const CY_PRODUCTION = `LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUE5dm5nWG1mdU51dnpnckJ3bHRLdApOZVQwYy9WYlZCWTRkZlZIaVBiQXF1Z3l2dndSUWFseVJ4SjNUdDgxL28vMXhyTXIyUUNtbXRkOVllTXA5dmVzClVqd1BuQnZucEIrWGo0ZXVoR2VVY0Q4Unhxb0cxdmFFd294ajFsSWFIckwrUVBBVGVPNFJ0Q3EweFM4TlduaGoKK1NYYm13NjNjWGtYZGthYzdlQVJmdmt2QjVMU1VqR290eXhPZGpOZmp2SmtvTU1GYXlqUGpSMGpFQldBL3NPZApFOENUTFRKdkYzakF0ODVpSDBHYWNpQVhJUG1uUnJ2eEh0SS9seDNWNkk0T1dNeG45SDRRWStoMnFmV2N2bWlQCmsrL1FNMjQ5cW4zKzgvQnNRWHNEVXZOMWdwZk9nRUIxRTFRaWJMZVV1YVducE05YWRWZENzWEVvRS8xUHduZTIKMFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t` + +const keys: Record = { + test: CY_TEST, + development: CY_DEVELOPMENT, + staging: CY_STAGING, + production: CY_PRODUCTION, +} + +const keyObjects: Record = {} + +function getPublicKey () { + const env = process.env.CYPRESS_CONFIG_ENV || process.env.CYPRESS_INTERNAL_ENV || 'development' + + if (!keyObjects[env]) { + keyObjects[env] = crypto.createPublicKey(Buffer.from(keys[env], 'base64').toString('utf8').trim()) + } + + return keyObjects[env] +} + +export interface EncryptRequestData { + jwe: GeneralJWE + secretKey: crypto.KeyObject +} + +// Implements the https://www.rfc-editor.org/rfc/rfc7516 spec +// Functionally equivalent to the behavior for AES-256-GCM encryption +// in the jose library (https://github.com/panva/jose/blob/main/src/jwe/general/encrypt.ts), +// but allows us to keep track of the encrypting key locally, to optionally use it for decryption +// of encrypted payloads coming back in the response body. +export async function encryptRequest (params: CypressRequestOptions, publicKey?: crypto.KeyObject): Promise { + const key = publicKey || getPublicKey() + const header = base64Url(JSON.stringify({ alg: 'RSA-OAEP', enc: 'A256GCM', zip: 'DEF' })) + const deflated = await deflateRaw(JSON.stringify(params.body)) + const iv = crypto.randomBytes(12) + const secretKey = crypto.createSecretKey(crypto.randomBytes(32)) + const cipher = crypto.createCipheriv('aes-256-gcm', secretKey, iv, { authTagLength: 16 }) + const aad = new TextEncoder().encode(header) + + cipher.setAAD(aad, { + plaintextLength: deflated.length, + }) + + const encrypted = cipher.update(deflated) + + cipher.final() + + // Returns the payload in JWE format, as well as the secretKey so we can use it + // for decrypting the payload sent in response + return { + secretKey, + jwe: { + iv: base64Url(iv), + ciphertext: base64Url(encrypted), + recipients: [ + { + encrypted_key: base64Url(crypto.publicEncrypt(key, secretKey.export())), + }, + ], + tag: base64Url(cipher.getAuthTag()), + protected: header, + }, + } +} + +/** + * Given the returned JWE and the symmetric symmetric key used in the original request, + * decrypts the repsonse payload, which is assumed to be JSON + */ +export async function decryptResponse (jwe: GeneralJWE, encryptionKey: crypto.KeyObject): Promise { + const result = await generalDecrypt(jwe, encryptionKey) + const plaintext = Buffer.from(result.plaintext).toString('utf8') + + return JSON.parse(plaintext) +} diff --git a/packages/server/lib/cloud/routes.ts b/packages/server/lib/cloud/routes.ts index 2aea517a4464..33b14ef6ca49 100644 --- a/packages/server/lib/cloud/routes.ts +++ b/packages/server/lib/cloud/routes.ts @@ -57,5 +57,7 @@ const makeRoutes = (baseUrl: string, routes: typeof CLOUD_ENDPOINTS) => { const apiRoutes = makeRoutes(apiUrl, CLOUD_ENDPOINTS) module.exports = { + apiUrl, apiRoutes, + makeRoutes: (baseUrl) => makeRoutes(baseUrl, CLOUD_ENDPOINTS), } diff --git a/packages/server/package.json b/packages/server/package.json index f41b03e53c6e..59d1c07826f2 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -36,6 +36,7 @@ "@packages/icons": "0.0.0-development", "ansi_up": "5.0.0", "ast-types": "0.13.3", + "base64url": "^3.0.1", "black-hole-stream": "0.0.1", "bluebird": "3.7.2", "bundle-require": "3.0.4", @@ -78,6 +79,7 @@ "is-fork-pr": "2.5.0", "is-html": "2.0.0", "jimp": "0.14.0", + "jose": "^4.11.2", "jsonlint": "1.6.3", "launch-editor": "2.3.0", "lazy-ass": "1.6.0", @@ -215,4 +217,4 @@ "fsevents": "^2", "registry-js": "1.15.0" } -} +} \ No newline at end of file diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index f092cb0226e9..ff060586b67f 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -1196,9 +1196,12 @@ describe('lib/cypress', () => { beforeEach(async function () { await clearCtx() + sinon.stub(api, 'preflight').resolves() sinon.stub(api, 'createRun').resolves() const createInstanceStub = sinon.stub(api, 'createInstance') + api.setPreflightResult({ encrypt: false, apiUrl: 'http://localhost:1234' }) + createInstanceStub.onFirstCall().resolves({ spec: 'cypress/e2e/app.cy.js', runs: [{}], @@ -1258,6 +1261,10 @@ describe('lib/cypress', () => { ]) }) + afterEach(() => { + api.resetPreflightResult() + }) + it('uses process.env.CYPRESS_PROJECT_ID', function () { sinon.stub(env, 'get').withArgs('CYPRESS_PROJECT_ID').returns(this.projectId) diff --git a/packages/server/test/unit/cloud/api_spec.js b/packages/server/test/unit/cloud/api_spec.js index 082f29e4edd4..8bb4347075dd 100644 --- a/packages/server/test/unit/cloud/api_spec.js +++ b/packages/server/test/unit/cloud/api_spec.js @@ -1,7 +1,13 @@ +const crypto = require('crypto') +const jose = require('jose') +const base64Url = require('base64url') + require('../../spec_helper') const _ = require('lodash') const os = require('os') +const encryption = require('../../../lib/cloud/encryption') + const { agent, } = require('@packages/network') @@ -23,8 +29,59 @@ const makeError = (details = {}) => { return _.extend(new Error(details.message || 'Some error'), details) } +const preflightNock = (encrypted = false) => { + const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, + }) + const encryptRequest = encryption.encryptRequest + + /** + * @type {crypto.KeyObject} + */ + let _secretKey + + sinon.stub(encryption, 'encryptRequest').callsFake(async (params) => { + const { secretKey, jwe } = await encryptRequest(params, publicKey) + + _secretKey = secretKey + + return { secretKey, jwe } + }) + + nock(API_BASEURL) + .defaultReplyHeaders({ 'x-cypress-encrypted': 'true' }) + .matchHeader('x-route-version', '1') + .matchHeader('x-os-name', 'linux') + .matchHeader('x-cypress-version', pkg.version) + .post('/preflight', () => true) + .reply(200, async (uri, requestBody) => { + const decryptedSecretKey = crypto.createSecretKey( + crypto.privateDecrypt( + privateKey, + Buffer.from(base64Url.toBase64(requestBody.recipients[0].encrypted_key), 'base64'), + ), + ) + const decrypted = await encryption.decryptResponse(requestBody, privateKey) + + expect(_secretKey.export().toString('utf8')).to.eq(decryptedSecretKey.export().toString('utf8')) + + const enc = new jose.GeneralEncrypt( + Buffer.from(JSON.stringify({ encrypted, apiUrl: decrypted.apiUrl })), + ) + + enc.setProtectedHeader({ alg: 'A256GCMKW', enc: 'A256GCM', zip: 'DEF' }).addRecipient(decryptedSecretKey) + + const jweResponse = await enc.encrypt() + + return jweResponse + }) +} + describe('lib/cloud/api', () => { beforeEach(() => { + api.setPreflightResult({ encrypt: false }) + preflightNock(false) + nock(API_BASEURL) .matchHeader('x-route-version', '2') .get('/auth') @@ -48,6 +105,10 @@ describe('lib/cloud/api', () => { }) }) + afterEach(() => { + api.resetPreflightResult() + }) + context('.rp', () => { beforeEach(() => { sinon.spy(agent, 'addRequest') @@ -135,6 +196,21 @@ describe('lib/cloud/api', () => { }) }) + context('.preflight', () => { + it('POST /preflight + returns encryption', function () { + nock.cleanAll() + sinon.restore() + + sinon.stub(os, 'platform').returns('linux') + preflightNock(true) + + return api.preflight({ projectId: 'abc123' }) + .then((ret) => { + expect(ret).to.deep.eq({ encrypted: true, apiUrl: `${API_BASEURL}/` }) + }) + }) + }) + context('.createRun', () => { beforeEach(function () { this.buildProps = { diff --git a/packages/server/test/unit/cloud/encryption_spec.js b/packages/server/test/unit/cloud/encryption_spec.js new file mode 100644 index 000000000000..10136e02375f --- /dev/null +++ b/packages/server/test/unit/cloud/encryption_spec.js @@ -0,0 +1,66 @@ +require('../../spec_helper') +const jose = require('jose') +const crypto = require('crypto') +const encryption = require('../../../lib/cloud/encryption') +const { expect } = require('chai') + +const TEST_BODY = { + test: 'string', + array: [ + { + a: 1, + }, + { + a: 2, + }, + { + a: 3, + }, + ], +} + +const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, +}) + +describe('encryption', () => { + it('encrypts payloads with encryptRequest', async () => { + const { jwe, secretKey } = await encryption.encryptRequest({ + encrypt: true, + body: TEST_BODY, + }, publicKey) + + const { plaintext } = await jose.generalDecrypt(jwe, privateKey) + + expect(JSON.parse(plaintext)).to.eql(TEST_BODY) + + const unwrappedKey = crypto.privateDecrypt(privateKey, Buffer.from(jwe.recipients[0].encrypted_key, 'base64')) + + expect( + unwrappedKey.toString('base64'), + ).to.eql(secretKey.export().toString('base64')) + }) + + it('is possible to use the secretKey to decrypt future responses', async () => { + const { jwe, secretKey } = await encryption.encryptRequest({ + encrypt: true, + body: TEST_BODY, + }, publicKey) + + const RESPONSE_BODY = { runId: 123 } + + const unwrappedKey = crypto.privateDecrypt(privateKey, Buffer.from(jwe.recipients[0].encrypted_key, 'base64')) + const unwrappedSecretKey = crypto.createSecretKey(unwrappedKey) + + const enc = new jose.GeneralEncrypt( + Buffer.from(JSON.stringify(RESPONSE_BODY)), + ) + + enc.setProtectedHeader({ alg: 'A256GCMKW', enc: 'A256GCM', zip: 'DEF' }).addRecipient(unwrappedSecretKey) + + const jweResponse = await enc.encrypt() + const roundtripResponse = await encryption.decryptResponse(jweResponse, secretKey) + + expect(roundtripResponse).to.eql(RESPONSE_BODY) + }) +}) diff --git a/packages/server/test/unit/modes/record_spec.js b/packages/server/test/unit/modes/record_spec.js index 42eef2bbc60c..9f8b040bfb4e 100644 --- a/packages/server/test/unit/modes/record_spec.js +++ b/packages/server/test/unit/modes/record_spec.js @@ -16,6 +16,16 @@ const initialEnv = _.clone(process.env) // NOTE: the majority of the logic of record_spec is // tested as an e2e/record_spec describe('lib/modes/record', () => { + beforeEach(() => { + sinon.stub(api, 'preflight').callsFake(async () => { + api.setPreflightResult({ encrypt: false }) + }) + }) + + afterEach(() => { + api.resetPreflightResult({ encrypt: false }) + }) + // QUESTION: why are these tests here when // this is a module... ? context('.getCommitFromGitOrCi', () => { diff --git a/scripts/after-pack-hook.js b/scripts/after-pack-hook.js index dc24307ef0b2..cba2426c8f17 100644 --- a/scripts/after-pack-hook.js +++ b/scripts/after-pack-hook.js @@ -58,8 +58,23 @@ module.exports = async function (params) { if (!['1', 'true'].includes(process.env.DISABLE_SNAPSHOT_REQUIRE)) { const binaryEntryPointSource = await getBinaryEntryPointSource() + const encryptionFile = path.join(outputFolder, 'packages/server/lib/cloud/encryption.js') + const fileContents = await fs.readFile(encryptionFile, 'utf8') - await fs.writeFile(path.join(outputFolder, 'index.js'), binaryEntryPointSource) + if (!fileContents.includes(`test: CY_TEST,`)) { + throw new Error(`Expected to find test key in cloud encryption file`) + } + + await Promise.all([ + fs.writeFile(encryptionFile, fileContents.replace(`test: CY_TEST,`, '').replace(/const CY_TEST = `(.*?)`/, '')), + fs.writeFile(path.join(outputFolder, 'index.js'), binaryEntryPointSource), + ]) + + const afterReplace = await fs.readFile(encryptionFile, 'utf8') + + if (afterReplace.includes('CY_TEST')) { + throw new Error(`Expected test key to be stripped from cloud encryption file`) + } await flipFuses( exePathPerPlatform[os.platform()], @@ -70,6 +85,8 @@ module.exports = async function (params) { }, ) + process.env.V8_UPDATE_METAFILE = '1' + // Build out the entry point and clean up prior to setting up v8 snapshots so that the state of the binary is correct await buildEntryPointAndCleanup(outputFolder) await setupV8Snapshots({ diff --git a/system-tests/__snapshots__/record_spec.js b/system-tests/__snapshots__/record_spec.js index d16c60b96bb6..0571033efc59 100644 --- a/system-tests/__snapshots__/record_spec.js +++ b/system-tests/__snapshots__/record_spec.js @@ -2571,3 +2571,123 @@ Available browsers found on your system are: - browser2 - browser3 ` + +exports['e2e record /preflight preflight failure renders error messages properly 1'] = ` +Recording this run failed because the request was invalid. + +Recording this way is no longer supported + +Errors: + +[ + "attempted to send envUrl foo.bar.baz" +] + +Request Sent: + +{ + "ciBuildId": "ciBuildId123", + "projectId": "cy12345" +} + +` + +exports['e2e record /preflight preflight failure: unencrypted fails on an unencrypted preflight response 1'] = ` +We encountered an unexpected error talking to our servers. + +Because you passed the --parallel flag, this run cannot proceed because it requires a valid response from our servers. + +The --group flag you passed was: foo +The --ciBuildId flag you passed was: ciBuildId123 + +The server's response was: + +DecryptionError: JWE Recipients missing or incorrect type + +` + +exports['e2e record /preflight preflight failure: warning message renders preflight warning messages prior to run warnings 1'] = ` +Warning from Cypress Cloud: + +---------------------------------------------------------------------- +This feature will not be supported soon, please check with Cypress to learn more: https://on.cypress.io/ +---------------------------------------------------------------------- + +You've exceeded the limit of private test results under your free plan this month. The limit is 500 private test results. + +Your plan is now in a grace period, which means your tests will still be recorded until 2999-12-31. Please upgrade your plan to continue recording tests on Cypress Cloud in the future. + +https://on.cypress.io/dashboard/organizations/org-id-1234/billing + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (record_pass.cy.js) │ + │ Searched: cypress/e2e/record_pass* │ + │ Params: Tag: nightly, Group: foo, Parallel: true │ + │ Run URL: https://dashboard.cypress.io/projects/cjvoj7/runs/12 │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: record_pass.cy.js (1 of 1) + Estimated: X second(s) + + + record pass + ✓ passes + - is pending + + + 1 passing + 1 pending + + + (Results) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Tests: 2 │ + │ Passing: 1 │ + │ Failing: 0 │ + │ Pending: 1 │ + │ Skipped: 0 │ + │ Screenshots: 1 │ + │ Video: false │ + │ Duration: X seconds │ + │ Estimated: X second(s) │ + │ Spec Ran: record_pass.cy.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + (Screenshots) + + - /XXX/XXX/XXX/cypress/screenshots/record_pass.cy.js/yay it passes.png (400x1022) + + + (Uploading Results) + + - Done Uploading (1/1) /foo/bar/.projects/e2e/cypress/screenshots/record_pass.cy.js/yay it passes.png + +==================================================================================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ✔ record_pass.cy.js XX:XX 2 1 - 1 - │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + ✔ All specs passed! XX:XX 2 1 - 1 - + + +─────────────────────────────────────────────────────────────────────────────────────────────────────── + + Recorded Run: https://dashboard.cypress.io/projects/cjvoj7/runs/12 + + +` diff --git a/system-tests/lib/serverStub.ts b/system-tests/lib/serverStub.ts index ad1a3f855d47..bad85e367d3d 100644 --- a/system-tests/lib/serverStub.ts +++ b/system-tests/lib/serverStub.ts @@ -1,9 +1,16 @@ +import crypto from 'crypto' import _ from 'lodash' import Bluebird from 'bluebird' import bodyParser from 'body-parser' import { api as jsonSchemas } from '@cypress/json-schemas' +import * as jose from 'jose' +import base64Url from 'base64url' + import systemTests from './system-tests' +const SYSTEM_TESTS_PRIVATE = 'LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3VBa1docWZSTTB3dFUKZ0toNXE5Z2hTU1BsdG5kM1UxUWk1VHhuZm1pR3lvQVZlL25HRkFidkxXQjNMaTRoVTBkVGlSTjg4TUIwam5hMQpXbHIwK2F1YzBmeVYwMTNiaW5ONFRxWUhFNjdaUUlKYkJNWDNMOEE5K1BybDJ6WkVqZlFZNkYraklKbXNIQ29RCnl5NXU3WGxSS09VOU9rQ0ZsQmp0L3FXbnd6b3RvM0lnY3JmcmJUejkxbk9LVHdKSXBtWGFRRGd3TEhLVm84aFgKbFJWMmI0UjIvWnErWWF6K2dMbE5aUkR2MVFsdXZUMTdPYUY1cldyL2xYT2lQaWp6MGtrS0ROQnF0aWo5UDdsaQpUSnUyQ0YzZkRxdzRuUUVKeVJBVExTTlpSdFZIRkdRN3hOdVdzdGFYOURBT3ZCRDhNTStFVmtlRWVYNEgrdEExCmFWclZhMG10QWdNQkFBRUNnZ0VBWW9OWXhwakFmWW54M1NwbHQxU0pyUGFLZ3krVlhSSHBEVVI0dVNNQXJHY1MKc3BjWXBvS0tGbmk3SjE0V3NibERKVkR5bm9aeWZzcDAvR0VtSTVFQ0RtdDNzNThSZ1F4V0tTTmxyWllBSkhENApHKzJNNGsrL1o1YUEvUWJwSjFDeWhETnlpWmtZUnk4K3hYa3lWWXpPWlJ0aEJSUG9tWGRwMGJ1Y0wybEFrN3NJCnVTUWIreTJtTUFXY1Q2UmRpYnFqcnNNMkE5YW1PQWc1bHd4L3NQUHRTbEdmVkZ6eExQYklDK3o3UmR1eWcyVEwKOXhnZkV5c0Y2dkpxSzJieW1pNGprd3dVZnhFRHluTmtIbEwzR1NsQlE1TkxnVjRVaXhrWEhKZ21OY041OERGTwpwT1NHQzAxMkNOVjQ4b3Fuc3VObEVjeVZhbTVSZk1iWXlCRm5PQVF5WlFLQmdRRGUxNmdISUk3Yk1VaXVLZHBwClV0YU8vMTNjMzlqQXFIcnllVnQ5UUhaTjU5aXdGZlN1N3kzZlYzVlFZRWJYZVpIU1ZkbC9uakhYTmRaaHdtbmUKWlcvZ3UzbHo4TlVjSElhaWZuT2RVSEh0czY2bjFlYUNvZDN0T29VYkhVUEhqYUl2L0F6ZlZTNWtBNzB6RTh6RApRNW5qS2JEc1hucExKY0QrV25VYzVIUlNjd0tCZ1FESDVueGZBaGkxckppQk1TeUpJYkZjUTl0dVVMT3JiQk9mCkZSeVArQzZybi9kZndvb09vL2lvaHJvT3FPSnVZTG9oTTltN1NvaHNpU3R2bG1VVEl3YlVTd1NNR00yMFdlK1cKR0ZjT01rQlk5NFVXdHF2aDlDaGMycmV6NkNDZE1VQkNHaVlMQ1V1SGp4ZDZqZ3ZZbG5vS2xsZzVBakJ2aUJDbApNM0VNZ2tOTFh3S0JnUUNwUVNGRmNJd3duZWszSjJEVjJHNVFwRk0xZk91VHdTUEk0VFlGRng0RUpCRm9CUFVZCm5WKzVJQ05oamc2Z2dKeXFKanlSZXFVZWNheklDYk1Ca1FmOXFFY2lNWXliMG1yTUpzRkhmaDlhVEx4ZWk4K04KN3NXeDlsMjg3MmhZdkJHdzRuOGdiZ0ZUUTZmRGtNbFlraExpLy9wNlBYUWplYVJ4VEdGaE5YL0lVd0tCZ0dKeQpyTVhOcm9XcW51RGhhdUdPYWw3YVBITXo0NGlGRFpUSFBPM2FlSUdsb3ByU29GTmRoZFRacFVBYkJJai9zaXN2CjhnYy9TYmpLUlU0TGIzUGhTRGU5U2x3RXl5b0xNT2RtelZqOGZweFNLb1ZwS1hWNlhYWjljUU4xU3JxZnl0bkQKTHdFNGJxNHdWb3ZROFJ5VjN6emZsa3RkUEtWeENXR1MyQllsQVNkWkFvR0FGRjliM2QvRko4Rm0rS25qNlhTaAozT3FuZlJ6NGRLN042bkxIUGdxelBGdVdiVWVPRGY1dTkrN3NpUVlNVkZyRWlZUlNvRStqc0FWREhBb1dIZ1Q3CmZlM2lUNzZuZVlHWVd3M1VwTjdQby9udTNiT3FWUzZSUEs0L05wZ0ZuM1ZzTUdyRTVKVVY5N0Z1Q1NKNHM4Wk8KTzJnWnBRdVpHQm40Und0LzEwOXdEYTQ9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=' +const TEST_PRIVATE = crypto.createPrivateKey(Buffer.from(SYSTEM_TESTS_PRIVATE, 'base64').toString('utf8')) + export const postRunResponseWithWarnings = jsonSchemas.getExample('postRunResponse')('2.2.0') export const postRunInstanceResponse = jsonSchemas.getExample('postRunInstanceResponse')('2.1.0') @@ -50,7 +57,28 @@ const mockServerState = { specs: [], } -const routeHandlers = { +export const encryptBody = async (req, res, body) => { + const enc = new jose.GeneralEncrypt(Buffer.from(JSON.stringify(body))) + + enc + .setProtectedHeader({ alg: 'A256GCMKW', enc: 'A256GCM', zip: 'DEF' }) + .addRecipient(req.unwrappedSecretKey()) + + res.header('x-cypress-encrypted', 'true') + + return await enc.encrypt() +} + +export const routeHandlers = { + preflight: { + method: 'post', + url: '/preflight', + res: async (req, res) => { + const preflightResponse = { encrypt: true, apiUrl: req.body.apiUrl } + + return res.json(await encryptBody(req, res, preflightResponse)) + }, + }, postRun: { method: 'post', url: '/runs', @@ -135,11 +163,11 @@ beforeEach(() => { }) export const getRequestUrls = () => { - return _.map(mockServerState.requests, 'url') + return _.map(mockServerState.requests, 'url').filter((u) => u !== 'POST /preflight') } export const getRequests = () => { - return mockServerState.requests + return mockServerState.requests.filter((r) => r.url !== 'POST /preflight') } const getSchemaErr = (tag, err, schema) => { @@ -214,6 +242,7 @@ const ensureSchema = function (onRequestBody, expectedRequestSchema, responseBod body, }) } catch (err) { + // eslint-disable-next-line no-console console.log('Schema Error:', err.message) return res.status(412).json(getSchemaErr('request', err, expectedRequestSchema)) @@ -248,6 +277,7 @@ const assertResponseBodySchema = function (req, res, next) { try { jsonSchemas.assertSchema(resName, resVersion)(body) } catch (err) { + // eslint-disable-next-line no-console console.log('Schema Error:', err.message) return res.status(412).json(getSchemaErr('response', err, res.expectedResponseSchema)) @@ -267,6 +297,27 @@ const assertResponseBodySchema = function (req, res, next) { const onServer = (routes) => { return (function (app) { app.use(bodyParser.json()) + app.use((req, res, next) => { + if (req.headers['x-cypress-encrypted']) { + const jwe = req.body + + req.unwrappedSecretKey = () => { + return crypto.createSecretKey( + crypto.privateDecrypt( + TEST_PRIVATE, + Buffer.from(base64Url.toBase64(jwe.recipients[0].encrypted_key), 'base64'), + ), + ) + } + + return jose.generalDecrypt(jwe, TEST_PRIVATE).then(({ plaintext }) => Buffer.from(plaintext).toString('utf8')).then((body) => { + req.body = JSON.parse(body) + next() + }).catch(next) + } + + return next() + }) app.use(assertResponseBodySchema) diff --git a/system-tests/package.json b/system-tests/package.json index e6fb5c455367..f12ef75497eb 100644 --- a/system-tests/package.json +++ b/system-tests/package.json @@ -36,6 +36,7 @@ "@types/chai": "4.2.15", "@types/mocha": "9.1.0", "babel-loader": "8.1.0", + "base64url": "^3.0.1", "bluebird": "3.7.2", "body-parser": "1.19.0", "cachedir": "2.3.0", @@ -51,6 +52,7 @@ "cors": "2.8.5", "dayjs": "^1.9.3", "debug": "^4.3.4", + "dedent": "^0.7.0", "dockerode": "3.3.1", "execa": "4", "express": "4.17.3", @@ -90,4 +92,4 @@ "license": "ISC", "author": "", "keywords": [] -} +} \ No newline at end of file diff --git a/system-tests/test/record_spec.js b/system-tests/test/record_spec.js index 50cd46aa0f8c..197eae604057 100644 --- a/system-tests/test/record_spec.js +++ b/system-tests/test/record_spec.js @@ -2,17 +2,21 @@ const _ = require('lodash') const path = require('path') const Promise = require('bluebird') const jsonSchemas = require('@cypress/json-schemas').api +const dedent = require('dedent') + const systemTests = require('../lib/system-tests').default const { fs } = require('@packages/server/lib/util/fs') const Fixtures = require('../lib/fixtures') const { createRoutes, setupStubbedServer, - getRequestUrls, getRequests, + getRequestUrls, + getRequests, postRunResponse, postRunResponseWithWarnings, postRunInstanceResponse, postInstanceTestsResponse, + encryptBody, } = require('../lib/serverStub') const { expectRunsToHaveCorrectTimings } = require('../lib/resultsUtils') @@ -22,13 +26,7 @@ const outputPath = path.join(e2ePath, 'output.json') let { runId, groupId, machineId, runUrl, tags } = postRunResponse const { instanceId } = postRunInstanceResponse -let requests = null - describe('e2e record', () => { - beforeEach(() => { - requests = getRequests() - }) - context('passing', () => { setupStubbedServer(createRoutes()) @@ -53,6 +51,7 @@ describe('e2e record', () => { expect(stdout).to.include(runUrl) const urls = getRequestUrls() + const requests = getRequests() const instanceReqs = urls.slice(0, 22) @@ -403,6 +402,7 @@ describe('e2e record', () => { snapshot: true, }) .then(() => { + const requests = getRequests() const postResults = requests[3] expect(postResults.url).to.eq(`POST /instances/${instanceId}/results`) @@ -571,6 +571,8 @@ describe('e2e record', () => { }) + const requests = getRequests() + expect(requests[2].body.config.defaultCommandTimeout).eq(1111) expect(requests[2].body.config.resolved.defaultCommandTimeout).deep.eq({ value: 1111, @@ -642,6 +644,8 @@ describe('e2e record', () => { snapshot: false, }) + const requests = getRequests() + // specs were reordered expect(requests[2].body.tests[0].title[1]).eq('b test') expect(requests[6].body.tests[0].title[1]).eq('a test') @@ -684,6 +688,8 @@ describe('e2e record', () => { expectedExitCode: 1, }) + const requests = getRequests() + expect(getRequestUrls()).deep.eq([ 'POST /runs', 'POST /runs/00748421-e035-4a3d-8604-8468cc48bdb5/instances', @@ -745,6 +751,7 @@ describe('e2e record', () => { }, }, }) + const requests = getRequests() console.log(stdout) @@ -780,6 +787,7 @@ describe('e2e record', () => { }, }, }) + const requests = getRequests() console.log(stdout) @@ -1339,7 +1347,7 @@ describe('e2e record', () => { // return systemTests.exec(this, { // key: 'f858a2bc-b469-4e48-be67-0876339ee7e1', - 'cypress-with-project-id.config.js', + // configFile: 'cypress-with-project-id.config.js', // spec: '*_record.spec*', // record: true, // snapshot: true, @@ -1880,4 +1888,160 @@ describe('e2e record', () => { }) }) }) + + describe('/preflight', () => { + describe('preflight failure: unencrypted', () => { + setupStubbedServer(createRoutes({ + preflight: { + res (req, res) { + return res.json({ apiUrl: 'http://localhost:1234' }) + }, + }, + })) + + it('fails on an unencrypted preflight response', async function () { + return systemTests.exec(this, { + key: 'f858a2bc-b469-4e48-be67-0876339ee7e1', + configFile: 'cypress-with-project-id.config.js', + spec: 'record_pass*', + group: 'foo', + tag: 'nightly', + record: true, + parallel: true, + snapshot: true, + ciBuildId: 'ciBuildId123', + expectedExitCode: 1, + }) + }) + }) + + describe('preflight failure 500 server error', () => { + setupStubbedServer(createRoutes({ + preflight: { + res (req, res) { + return res.sendStatus(500) + }, + }, + })) + + it('retries on a preflight server error', async function () { + await new Promise((resolve, reject) => { + let sp + + systemTests.exec(this, { + key: 'f858a2bc-b469-4e48-be67-0876339ee7e1', + configFile: 'cypress-with-project-id.config.js', + spec: 'record_pass*', + group: 'foo', + tag: 'nightly', + record: true, + parallel: true, + ciBuildId: 'ciBuildId123', + onSpawn (spawnResult) { + sp = spawnResult + sp.stdout.on('data', (chunk) => { + const msg = String(chunk) + + if (msg.includes('We will retry')) { + resolve() + sp.kill() + } + }) + }, + }).catch(reject) + }) + }) + }) + + describe('preflight failure', () => { + setupStubbedServer(createRoutes({ + preflight: { + res: async (req, res) => { + return res.status(412).json(await encryptBody(req, res, { + message: 'Recording this way is no longer supported', + errors: [ + 'attempted to send envUrl foo.bar.baz', + ], + object: { + ciBuildId: 'ciBuildId123', + projectId: 'cy12345', + }, + })) + }, + }, + })) + + it('renders error messages properly', async function () { + return systemTests.exec(this, { + key: 'f858a2bc-b469-4e48-be67-0876339ee7e1', + configFile: 'cypress-with-project-id.config.js', + spec: 'record_pass*', + group: 'foo', + tag: 'nightly', + record: true, + parallel: true, + snapshot: true, + ciBuildId: 'ciBuildId123', + expectedExitCode: 1, + }) + }) + }) + + describe('preflight failure: warning message', () => { + const mockServer = setupStubbedServer(createRoutes({ + preflight: { + res: async (req, res) => { + return res.json(await encryptBody(req, res, { + encrypt: true, + apiUrl: req.body.apiUrl, + warnings: [ + { + message: dedent` + ---------------------------------------------------------------------- + This feature will not be supported soon, please check with Cypress to learn more: https://on.cypress.io/ + ---------------------------------------------------------------------- + `, + }, + ], + })) + }, + }, + postRun: { + res (req, res) { + mockServer.setSpecs(req) + + return res.status(200).json({ + runId, + groupId, + machineId, + runUrl, + tags, + warnings: [{ + name: 'foo', + message: 'foo', + code: 'FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_PRIVATE_TESTS', + limit: 500, + gracePeriodEnds: '2999-12-31', + orgId: 'org-id-1234', + }], + }) + }, + }, + })) + + it('renders preflight warning messages prior to run warnings', async function () { + return await systemTests.exec(this, { + key: 'f858a2bc-b469-4e48-be67-0876339ee7e1', + configFile: 'cypress-with-project-id.config.js', + spec: 'record_pass*', + group: 'foo', + tag: 'nightly', + record: true, + parallel: true, + snapshot: true, + ciBuildId: 'ciBuildId123', + }) + }) + }) + }) }) diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index 019ca282a2fd..4208d6c8f5e5 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -26,6 +26,7 @@ "./node_modules/fs-extra/node_modules/jsonfile/index.js", "./node_modules/get-package-info/node_modules/debug/src/node.js", "./node_modules/graceful-fs/polyfills.js", + "./node_modules/jose/dist/node/cjs/runtime/verify.js", "./node_modules/js-yaml/lib/js-yaml/type/js/function.js", "./node_modules/jsonfile/index.js", "./node_modules/make-dir/index.js", @@ -419,6 +420,14 @@ "./node_modules/isexe/index.js", "./node_modules/istextorbinary/edition-es2019/index.js", "./node_modules/jimp/dist/index.js", + "./node_modules/jose/dist/node/cjs/lib/buffer_utils.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1_sequence_encoder.js", + "./node_modules/jose/dist/node/cjs/runtime/base64url.js", + "./node_modules/jose/dist/node/cjs/runtime/is_key_like.js", + "./node_modules/jose/dist/node/cjs/runtime/is_key_object.js", + "./node_modules/jose/dist/node/cjs/runtime/node_key.js", + "./node_modules/jose/dist/node/cjs/runtime/sign.js", + "./node_modules/jose/dist/node/cjs/runtime/webcrypto.js", "./node_modules/jsbn/index.js", "./node_modules/json3/lib/json3.js", "./node_modules/keyv/src/index.js", @@ -1685,6 +1694,9 @@ "./node_modules/base/node_modules/define-property/index.js", "./node_modules/base64-js/index.js", "./node_modules/base64id/lib/base64id.js", + "./node_modules/base64url/dist/base64url.js", + "./node_modules/base64url/dist/pad-string.js", + "./node_modules/base64url/index.js", "./node_modules/basic-auth/index.js", "./node_modules/bcrypt-pbkdf/index.js", "./node_modules/binary-extensions/binary-extensions.json", @@ -2693,6 +2705,81 @@ "./node_modules/isstream/isstream.js", "./node_modules/iterall/index.js", "./node_modules/jetpack-id/index.js", + "./node_modules/jose/dist/node/cjs/index.js", + "./node_modules/jose/dist/node/cjs/jwe/compact/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/compact/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/flattened/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/flattened/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/general/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/general/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwk/embedded.js", + "./node_modules/jose/dist/node/cjs/jwk/thumbprint.js", + "./node_modules/jose/dist/node/cjs/jwks/local.js", + "./node_modules/jose/dist/node/cjs/jwks/remote.js", + "./node_modules/jose/dist/node/cjs/jws/compact/sign.js", + "./node_modules/jose/dist/node/cjs/jws/compact/verify.js", + "./node_modules/jose/dist/node/cjs/jws/flattened/sign.js", + "./node_modules/jose/dist/node/cjs/jws/flattened/verify.js", + "./node_modules/jose/dist/node/cjs/jws/general/sign.js", + "./node_modules/jose/dist/node/cjs/jws/general/verify.js", + "./node_modules/jose/dist/node/cjs/jwt/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwt/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwt/produce.js", + "./node_modules/jose/dist/node/cjs/jwt/sign.js", + "./node_modules/jose/dist/node/cjs/jwt/unsecured.js", + "./node_modules/jose/dist/node/cjs/jwt/verify.js", + "./node_modules/jose/dist/node/cjs/key/export.js", + "./node_modules/jose/dist/node/cjs/key/generate_key_pair.js", + "./node_modules/jose/dist/node/cjs/key/generate_secret.js", + "./node_modules/jose/dist/node/cjs/key/import.js", + "./node_modules/jose/dist/node/cjs/lib/aesgcmkw.js", + "./node_modules/jose/dist/node/cjs/lib/cek.js", + "./node_modules/jose/dist/node/cjs/lib/check_iv_length.js", + "./node_modules/jose/dist/node/cjs/lib/check_key_type.js", + "./node_modules/jose/dist/node/cjs/lib/check_p2s.js", + "./node_modules/jose/dist/node/cjs/lib/crypto_key.js", + "./node_modules/jose/dist/node/cjs/lib/decrypt_key_management.js", + "./node_modules/jose/dist/node/cjs/lib/encrypt_key_management.js", + "./node_modules/jose/dist/node/cjs/lib/epoch.js", + "./node_modules/jose/dist/node/cjs/lib/format_pem.js", + "./node_modules/jose/dist/node/cjs/lib/invalid_key_input.js", + "./node_modules/jose/dist/node/cjs/lib/is_disjoint.js", + "./node_modules/jose/dist/node/cjs/lib/is_object.js", + "./node_modules/jose/dist/node/cjs/lib/iv.js", + "./node_modules/jose/dist/node/cjs/lib/jwt_claims_set.js", + "./node_modules/jose/dist/node/cjs/lib/secs.js", + "./node_modules/jose/dist/node/cjs/lib/validate_algorithms.js", + "./node_modules/jose/dist/node/cjs/lib/validate_crit.js", + "./node_modules/jose/dist/node/cjs/runtime/aeskw.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1_sequence_decoder.js", + "./node_modules/jose/dist/node/cjs/runtime/cbc_tag.js", + "./node_modules/jose/dist/node/cjs/runtime/check_cek_length.js", + "./node_modules/jose/dist/node/cjs/runtime/check_modulus_length.js", + "./node_modules/jose/dist/node/cjs/runtime/ciphers.js", + "./node_modules/jose/dist/node/cjs/runtime/decrypt.js", + "./node_modules/jose/dist/node/cjs/runtime/digest.js", + "./node_modules/jose/dist/node/cjs/runtime/dsa_digest.js", + "./node_modules/jose/dist/node/cjs/runtime/ecdhes.js", + "./node_modules/jose/dist/node/cjs/runtime/encrypt.js", + "./node_modules/jose/dist/node/cjs/runtime/env.js", + "./node_modules/jose/dist/node/cjs/runtime/fetch_jwks.js", + "./node_modules/jose/dist/node/cjs/runtime/flags.js", + "./node_modules/jose/dist/node/cjs/runtime/generate.js", + "./node_modules/jose/dist/node/cjs/runtime/get_named_curve.js", + "./node_modules/jose/dist/node/cjs/runtime/get_sign_verify_key.js", + "./node_modules/jose/dist/node/cjs/runtime/hmac_digest.js", + "./node_modules/jose/dist/node/cjs/runtime/jwk_to_key.js", + "./node_modules/jose/dist/node/cjs/runtime/key_to_jwk.js", + "./node_modules/jose/dist/node/cjs/runtime/pbes2kw.js", + "./node_modules/jose/dist/node/cjs/runtime/random.js", + "./node_modules/jose/dist/node/cjs/runtime/rsaes.js", + "./node_modules/jose/dist/node/cjs/runtime/timing_safe_equal.js", + "./node_modules/jose/dist/node/cjs/runtime/zlib.js", + "./node_modules/jose/dist/node/cjs/util/base64url.js", + "./node_modules/jose/dist/node/cjs/util/decode_jwt.js", + "./node_modules/jose/dist/node/cjs/util/decode_protected_header.js", + "./node_modules/jose/dist/node/cjs/util/errors.js", "./node_modules/js-tokens/index.js", "./node_modules/jsesc/jsesc.js", "./node_modules/json-buffer/index.js", @@ -4952,6 +5039,7 @@ "./packages/server/lib/browsers/protocol.ts", "./packages/server/lib/browsers/webkit-automation.ts", "./packages/server/lib/browsers/webkit.ts", + "./packages/server/lib/cloud/encryption.ts", "./packages/server/lib/cloud/machine_id.js", "./packages/server/lib/cohorts.ts", "./packages/server/lib/config.ts", diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index d119c2cbd765..19adb10ec361 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -26,6 +26,7 @@ "./node_modules/fs-extra/node_modules/jsonfile/index.js", "./node_modules/get-package-info/node_modules/debug/src/node.js", "./node_modules/graceful-fs/polyfills.js", + "./node_modules/jose/dist/node/cjs/runtime/verify.js", "./node_modules/js-yaml/lib/js-yaml/type/js/function.js", "./node_modules/jsonfile/index.js", "./node_modules/make-dir/index.js", @@ -418,6 +419,14 @@ "./node_modules/isexe/index.js", "./node_modules/istextorbinary/edition-es2019/index.js", "./node_modules/jimp/dist/index.js", + "./node_modules/jose/dist/node/cjs/lib/buffer_utils.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1_sequence_encoder.js", + "./node_modules/jose/dist/node/cjs/runtime/base64url.js", + "./node_modules/jose/dist/node/cjs/runtime/is_key_like.js", + "./node_modules/jose/dist/node/cjs/runtime/is_key_object.js", + "./node_modules/jose/dist/node/cjs/runtime/node_key.js", + "./node_modules/jose/dist/node/cjs/runtime/sign.js", + "./node_modules/jose/dist/node/cjs/runtime/webcrypto.js", "./node_modules/jsbn/index.js", "./node_modules/json3/lib/json3.js", "./node_modules/keyv/src/index.js", @@ -989,7 +998,6 @@ "./packages/scaffold-config/index.js", "./packages/server/lib/browsers/chrome.ts", "./packages/server/lib/browsers/firefox.ts", - "./packages/server/lib/browsers/memory/index.ts", "./packages/server/lib/cache.js", "./packages/server/lib/cloud/api.ts", "./packages/server/lib/cloud/auth.ts", @@ -1796,6 +1804,9 @@ "./node_modules/base/node_modules/define-property/index.js", "./node_modules/base64-js/index.js", "./node_modules/base64id/lib/base64id.js", + "./node_modules/base64url/dist/base64url.js", + "./node_modules/base64url/dist/pad-string.js", + "./node_modules/base64url/index.js", "./node_modules/basic-auth/index.js", "./node_modules/bcrypt-pbkdf/index.js", "./node_modules/binary-extensions/binary-extensions.json", @@ -2803,6 +2814,81 @@ "./node_modules/isstream/isstream.js", "./node_modules/iterall/index.js", "./node_modules/jetpack-id/index.js", + "./node_modules/jose/dist/node/cjs/index.js", + "./node_modules/jose/dist/node/cjs/jwe/compact/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/compact/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/flattened/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/flattened/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/general/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/general/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwk/embedded.js", + "./node_modules/jose/dist/node/cjs/jwk/thumbprint.js", + "./node_modules/jose/dist/node/cjs/jwks/local.js", + "./node_modules/jose/dist/node/cjs/jwks/remote.js", + "./node_modules/jose/dist/node/cjs/jws/compact/sign.js", + "./node_modules/jose/dist/node/cjs/jws/compact/verify.js", + "./node_modules/jose/dist/node/cjs/jws/flattened/sign.js", + "./node_modules/jose/dist/node/cjs/jws/flattened/verify.js", + "./node_modules/jose/dist/node/cjs/jws/general/sign.js", + "./node_modules/jose/dist/node/cjs/jws/general/verify.js", + "./node_modules/jose/dist/node/cjs/jwt/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwt/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwt/produce.js", + "./node_modules/jose/dist/node/cjs/jwt/sign.js", + "./node_modules/jose/dist/node/cjs/jwt/unsecured.js", + "./node_modules/jose/dist/node/cjs/jwt/verify.js", + "./node_modules/jose/dist/node/cjs/key/export.js", + "./node_modules/jose/dist/node/cjs/key/generate_key_pair.js", + "./node_modules/jose/dist/node/cjs/key/generate_secret.js", + "./node_modules/jose/dist/node/cjs/key/import.js", + "./node_modules/jose/dist/node/cjs/lib/aesgcmkw.js", + "./node_modules/jose/dist/node/cjs/lib/cek.js", + "./node_modules/jose/dist/node/cjs/lib/check_iv_length.js", + "./node_modules/jose/dist/node/cjs/lib/check_key_type.js", + "./node_modules/jose/dist/node/cjs/lib/check_p2s.js", + "./node_modules/jose/dist/node/cjs/lib/crypto_key.js", + "./node_modules/jose/dist/node/cjs/lib/decrypt_key_management.js", + "./node_modules/jose/dist/node/cjs/lib/encrypt_key_management.js", + "./node_modules/jose/dist/node/cjs/lib/epoch.js", + "./node_modules/jose/dist/node/cjs/lib/format_pem.js", + "./node_modules/jose/dist/node/cjs/lib/invalid_key_input.js", + "./node_modules/jose/dist/node/cjs/lib/is_disjoint.js", + "./node_modules/jose/dist/node/cjs/lib/is_object.js", + "./node_modules/jose/dist/node/cjs/lib/iv.js", + "./node_modules/jose/dist/node/cjs/lib/jwt_claims_set.js", + "./node_modules/jose/dist/node/cjs/lib/secs.js", + "./node_modules/jose/dist/node/cjs/lib/validate_algorithms.js", + "./node_modules/jose/dist/node/cjs/lib/validate_crit.js", + "./node_modules/jose/dist/node/cjs/runtime/aeskw.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1_sequence_decoder.js", + "./node_modules/jose/dist/node/cjs/runtime/cbc_tag.js", + "./node_modules/jose/dist/node/cjs/runtime/check_cek_length.js", + "./node_modules/jose/dist/node/cjs/runtime/check_modulus_length.js", + "./node_modules/jose/dist/node/cjs/runtime/ciphers.js", + "./node_modules/jose/dist/node/cjs/runtime/decrypt.js", + "./node_modules/jose/dist/node/cjs/runtime/digest.js", + "./node_modules/jose/dist/node/cjs/runtime/dsa_digest.js", + "./node_modules/jose/dist/node/cjs/runtime/ecdhes.js", + "./node_modules/jose/dist/node/cjs/runtime/encrypt.js", + "./node_modules/jose/dist/node/cjs/runtime/env.js", + "./node_modules/jose/dist/node/cjs/runtime/fetch_jwks.js", + "./node_modules/jose/dist/node/cjs/runtime/flags.js", + "./node_modules/jose/dist/node/cjs/runtime/generate.js", + "./node_modules/jose/dist/node/cjs/runtime/get_named_curve.js", + "./node_modules/jose/dist/node/cjs/runtime/get_sign_verify_key.js", + "./node_modules/jose/dist/node/cjs/runtime/hmac_digest.js", + "./node_modules/jose/dist/node/cjs/runtime/jwk_to_key.js", + "./node_modules/jose/dist/node/cjs/runtime/key_to_jwk.js", + "./node_modules/jose/dist/node/cjs/runtime/pbes2kw.js", + "./node_modules/jose/dist/node/cjs/runtime/random.js", + "./node_modules/jose/dist/node/cjs/runtime/rsaes.js", + "./node_modules/jose/dist/node/cjs/runtime/timing_safe_equal.js", + "./node_modules/jose/dist/node/cjs/runtime/zlib.js", + "./node_modules/jose/dist/node/cjs/util/base64url.js", + "./node_modules/jose/dist/node/cjs/util/decode_jwt.js", + "./node_modules/jose/dist/node/cjs/util/decode_protected_header.js", + "./node_modules/jose/dist/node/cjs/util/errors.js", "./node_modules/js-tokens/index.js", "./node_modules/jsesc/jsesc.js", "./node_modules/json-buffer/index.js", @@ -5030,6 +5116,7 @@ "./packages/server/lib/browsers/protocol.ts", "./packages/server/lib/browsers/webkit-automation.ts", "./packages/server/lib/browsers/webkit.ts", + "./packages/server/lib/cloud/encryption.ts", "./packages/server/lib/cloud/machine_id.js", "./packages/server/lib/cohorts.ts", "./packages/server/lib/config.ts", diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 4222c2dcb018..f1fa7fbfa2f6 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -26,6 +26,7 @@ "./node_modules/fs-extra/node_modules/jsonfile/index.js", "./node_modules/get-package-info/node_modules/debug/src/node.js", "./node_modules/graceful-fs/polyfills.js", + "./node_modules/jose/dist/node/cjs/runtime/verify.js", "./node_modules/js-yaml/lib/js-yaml/type/js/function.js", "./node_modules/jsonfile/index.js", "./node_modules/make-dir/index.js", @@ -150,8 +151,6 @@ "./node_modules/@cypress/commit-info/node_modules/semver/semver.js", "./node_modules/@cypress/get-windows-proxy/node_modules/debug/src/browser.js", "./node_modules/@cypress/get-windows-proxy/node_modules/debug/src/index.js", - "./node_modules/@cypress/get-windows-proxy/node_modules/registry-js/dist/lib/index.js", - "./node_modules/@cypress/get-windows-proxy/node_modules/registry-js/dist/lib/registry.js", "./node_modules/@cypress/request-promise/lib/rp.js", "./node_modules/@cypress/request/index.js", "./node_modules/@cypress/request/lib/helpers.js", @@ -420,6 +419,14 @@ "./node_modules/isexe/index.js", "./node_modules/istextorbinary/edition-es2019/index.js", "./node_modules/jimp/dist/index.js", + "./node_modules/jose/dist/node/cjs/lib/buffer_utils.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1_sequence_encoder.js", + "./node_modules/jose/dist/node/cjs/runtime/base64url.js", + "./node_modules/jose/dist/node/cjs/runtime/is_key_like.js", + "./node_modules/jose/dist/node/cjs/runtime/is_key_object.js", + "./node_modules/jose/dist/node/cjs/runtime/node_key.js", + "./node_modules/jose/dist/node/cjs/runtime/sign.js", + "./node_modules/jose/dist/node/cjs/runtime/webcrypto.js", "./node_modules/jsbn/index.js", "./node_modules/json3/lib/json3.js", "./node_modules/keyv/src/index.js", @@ -994,7 +1001,6 @@ "./packages/scaffold-config/index.js", "./packages/server/lib/browsers/chrome.ts", "./packages/server/lib/browsers/firefox.ts", - "./packages/server/lib/browsers/memory/index.ts", "./packages/server/lib/cache.js", "./packages/server/lib/cloud/api.ts", "./packages/server/lib/cloud/auth.ts", @@ -1799,6 +1805,9 @@ "./node_modules/base/node_modules/define-property/index.js", "./node_modules/base64-js/index.js", "./node_modules/base64id/lib/base64id.js", + "./node_modules/base64url/dist/base64url.js", + "./node_modules/base64url/dist/pad-string.js", + "./node_modules/base64url/index.js", "./node_modules/basic-auth/index.js", "./node_modules/bcrypt-pbkdf/index.js", "./node_modules/binary-extensions/binary-extensions.json", @@ -2806,6 +2815,81 @@ "./node_modules/isstream/isstream.js", "./node_modules/iterall/index.js", "./node_modules/jetpack-id/index.js", + "./node_modules/jose/dist/node/cjs/index.js", + "./node_modules/jose/dist/node/cjs/jwe/compact/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/compact/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/flattened/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/flattened/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/general/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwe/general/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwk/embedded.js", + "./node_modules/jose/dist/node/cjs/jwk/thumbprint.js", + "./node_modules/jose/dist/node/cjs/jwks/local.js", + "./node_modules/jose/dist/node/cjs/jwks/remote.js", + "./node_modules/jose/dist/node/cjs/jws/compact/sign.js", + "./node_modules/jose/dist/node/cjs/jws/compact/verify.js", + "./node_modules/jose/dist/node/cjs/jws/flattened/sign.js", + "./node_modules/jose/dist/node/cjs/jws/flattened/verify.js", + "./node_modules/jose/dist/node/cjs/jws/general/sign.js", + "./node_modules/jose/dist/node/cjs/jws/general/verify.js", + "./node_modules/jose/dist/node/cjs/jwt/decrypt.js", + "./node_modules/jose/dist/node/cjs/jwt/encrypt.js", + "./node_modules/jose/dist/node/cjs/jwt/produce.js", + "./node_modules/jose/dist/node/cjs/jwt/sign.js", + "./node_modules/jose/dist/node/cjs/jwt/unsecured.js", + "./node_modules/jose/dist/node/cjs/jwt/verify.js", + "./node_modules/jose/dist/node/cjs/key/export.js", + "./node_modules/jose/dist/node/cjs/key/generate_key_pair.js", + "./node_modules/jose/dist/node/cjs/key/generate_secret.js", + "./node_modules/jose/dist/node/cjs/key/import.js", + "./node_modules/jose/dist/node/cjs/lib/aesgcmkw.js", + "./node_modules/jose/dist/node/cjs/lib/cek.js", + "./node_modules/jose/dist/node/cjs/lib/check_iv_length.js", + "./node_modules/jose/dist/node/cjs/lib/check_key_type.js", + "./node_modules/jose/dist/node/cjs/lib/check_p2s.js", + "./node_modules/jose/dist/node/cjs/lib/crypto_key.js", + "./node_modules/jose/dist/node/cjs/lib/decrypt_key_management.js", + "./node_modules/jose/dist/node/cjs/lib/encrypt_key_management.js", + "./node_modules/jose/dist/node/cjs/lib/epoch.js", + "./node_modules/jose/dist/node/cjs/lib/format_pem.js", + "./node_modules/jose/dist/node/cjs/lib/invalid_key_input.js", + "./node_modules/jose/dist/node/cjs/lib/is_disjoint.js", + "./node_modules/jose/dist/node/cjs/lib/is_object.js", + "./node_modules/jose/dist/node/cjs/lib/iv.js", + "./node_modules/jose/dist/node/cjs/lib/jwt_claims_set.js", + "./node_modules/jose/dist/node/cjs/lib/secs.js", + "./node_modules/jose/dist/node/cjs/lib/validate_algorithms.js", + "./node_modules/jose/dist/node/cjs/lib/validate_crit.js", + "./node_modules/jose/dist/node/cjs/runtime/aeskw.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1.js", + "./node_modules/jose/dist/node/cjs/runtime/asn1_sequence_decoder.js", + "./node_modules/jose/dist/node/cjs/runtime/cbc_tag.js", + "./node_modules/jose/dist/node/cjs/runtime/check_cek_length.js", + "./node_modules/jose/dist/node/cjs/runtime/check_modulus_length.js", + "./node_modules/jose/dist/node/cjs/runtime/ciphers.js", + "./node_modules/jose/dist/node/cjs/runtime/decrypt.js", + "./node_modules/jose/dist/node/cjs/runtime/digest.js", + "./node_modules/jose/dist/node/cjs/runtime/dsa_digest.js", + "./node_modules/jose/dist/node/cjs/runtime/ecdhes.js", + "./node_modules/jose/dist/node/cjs/runtime/encrypt.js", + "./node_modules/jose/dist/node/cjs/runtime/env.js", + "./node_modules/jose/dist/node/cjs/runtime/fetch_jwks.js", + "./node_modules/jose/dist/node/cjs/runtime/flags.js", + "./node_modules/jose/dist/node/cjs/runtime/generate.js", + "./node_modules/jose/dist/node/cjs/runtime/get_named_curve.js", + "./node_modules/jose/dist/node/cjs/runtime/get_sign_verify_key.js", + "./node_modules/jose/dist/node/cjs/runtime/hmac_digest.js", + "./node_modules/jose/dist/node/cjs/runtime/jwk_to_key.js", + "./node_modules/jose/dist/node/cjs/runtime/key_to_jwk.js", + "./node_modules/jose/dist/node/cjs/runtime/pbes2kw.js", + "./node_modules/jose/dist/node/cjs/runtime/random.js", + "./node_modules/jose/dist/node/cjs/runtime/rsaes.js", + "./node_modules/jose/dist/node/cjs/runtime/timing_safe_equal.js", + "./node_modules/jose/dist/node/cjs/runtime/zlib.js", + "./node_modules/jose/dist/node/cjs/util/base64url.js", + "./node_modules/jose/dist/node/cjs/util/decode_jwt.js", + "./node_modules/jose/dist/node/cjs/util/decode_protected_header.js", + "./node_modules/jose/dist/node/cjs/util/errors.js", "./node_modules/js-tokens/index.js", "./node_modules/jsesc/jsesc.js", "./node_modules/json-buffer/index.js", @@ -5030,6 +5114,7 @@ "./packages/server/lib/browsers/protocol.ts", "./packages/server/lib/browsers/webkit-automation.ts", "./packages/server/lib/browsers/webkit.ts", + "./packages/server/lib/cloud/encryption.ts", "./packages/server/lib/cloud/machine_id.js", "./packages/server/lib/cohorts.ts", "./packages/server/lib/config.ts", diff --git a/tooling/v8-snapshot/package.json b/tooling/v8-snapshot/package.json index 065dd2441b87..7dca8f630ded 100644 --- a/tooling/v8-snapshot/package.json +++ b/tooling/v8-snapshot/package.json @@ -57,4 +57,4 @@ "@cypress/snapbuild-windows-32": "1.0.2", "@cypress/snapbuild-windows-64": "1.0.2" } -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9e9c554e439e..95e617319b56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9271,6 +9271,11 @@ base64id@2.0.0, base64id@~2.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -18656,6 +18661,11 @@ jmespath@0.15.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= +jose@^4.11.2: + version "4.11.2" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.2.tgz#d9699307c02e18ff56825843ba90e2fae9f09e23" + integrity sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A== + jpeg-js@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.1.2.tgz#135b992c0575c985cfa0f494a3227ed238583ece"