From d26d877db86048c4e1392415574967863a052a15 Mon Sep 17 00:00:00 2001 From: Tim Griesser Date: Wed, 15 Feb 2023 08:20:57 -0500 Subject: [PATCH 1/7] 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" From 9e27540f0547163a8ec745da2d66ce455683187c Mon Sep 17 00:00:00 2001 From: Emily Rohrbough Date: Wed, 15 Feb 2023 08:25:30 -0600 Subject: [PATCH 2/7] chore: bump for 12.6.0 release (#25812) --- cli/CHANGELOG.md | 28 ++++++++++++---------------- package.json | 2 +- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 4c2141b646f7..4fb680375b25 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,35 +1,31 @@ ## 12.6.0 -_Released 02/14/2023 (PENDING)_ +_Released 02/15/2023_ **Features:** -- It is now possible to overwrite query commands using [`Cypress.Commands.overwriteQuery`](https://on.cypress.io/api/custom-queries). Addressed in [#25674](https://github.com/cypress-io/cypress/pull/25674). -- Added the "Open in IDE" feature for failed tests reported from the Debug page. Addressed in [#25691](https://github.com/cypress-io/cypress/pull/25691). -- Added a new CLI flag, called [`--auto-cancel-after-failures`](https://docs.cypress.io/guides/guides/command-line#Options), that overrides the project-level CI ["Auto Cancellation"](https://docs.cypress.io/guides/cloud/smart-orchestration#Auto-Cancellation) value when recording to the Cloud. This gives Cloud users on Business and Enterprise plans the flexibility to alter the auto-cancellation value per run. Addressed in [#25237](https://github.com/cypress-io/cypress/pull/25237). -- Added `Cypress.require()` for including dependencies within the `cy.origin()` callback. Removed support for `require()` and `import()` within the callback. Addresses [#24976](https://github.com/cypress-io/cypress/issues/24976). +- Added a new CLI flag, called [`--auto-cancel-after-failures`](https://docs.cypress.io/guides/guides/command-line#Options), that overrides the project-level ["Auto Cancellation"](https://docs.cypress.io/guides/cloud/smart-orchestration#Auto-Cancellation) value when recording to the Cloud. This gives Cloud users on Business and Enterprise plans the flexibility to alter the auto-cancellation value per run. Addressed in [#25237](https://github.com/cypress-io/cypress/pull/25237). +- It is now possible to overwrite query commands using [`Cypress.Commands.overwriteQuery`](https://on.cypress.io/api/custom-queries). Addressed in [#25078](https://github.com/cypress-io/cypress/issues/25078). +- Added [`Cypress.require()`](https://docs.cypress.io/api/cypress-api/require) for including dependencies within the [`cy.origin()`](https://docs.cypress.io/api/commands/origin) callback. This change removed support for using `require()` and `import()` directly within the callback because we found that it impacted performance not only for spec files using them within the [`cy.origin()`](https://docs.cypress.io/api/commands/origin) callback, but even for spec files that did not use them. Addresses [#24976](https://github.com/cypress-io/cypress/issues/24976). +- Added the ability to open the failing test in the IDE from the Debug page before needing to re-run the test. Addressed in [#24850](https://github.com/cypress-io/cypress/issues/24850). **Bugfixes:** -- Fixed an issue with the Cloud project selection modal not showing the correct prompts. Fixes [#25520](https://github.com/cypress-io/cypress/issues/25520). -- Fixed an issue in middleware where error-handling code could itself generate an error and fail to report the original issue. Fixes [#22825](https://github.com/cypress-io/cypress/issues/22825). -- Fixed an issue that could cause the Debug page to display a different number of specs for in-progress runs than shown in Cypress Cloud. Fixes [#25647](https://github.com/cypress-io/cypress/issues/25647). -- Fixed an issue introduced in Cypress 12.3.0 where custom browsers that relied on process environment variables were not found on macOS arm64 architectures. Fixed in [#25753](https://github.com/cypress-io/cypress/pull/25753). +- When a Cloud user is apart of multiple Cloud organizations, the [Connect to Cloud setup](https://docs.cypress.io/guides/cloud/projects#Set-up-a-project-to-record) now shows the correct organizational prompts when connecting a new project. Fixes [#25520](https://github.com/cypress-io/cypress/issues/25520). - Fixed an issue where Cypress would fail to load any specs if the project `specPattern` included a resource that could not be accessed due to filesystem permissions. Fixes [#24109](https://github.com/cypress-io/cypress/issues/24109). +- Fixed an issue where the Debug page would display a different number of specs for in-progress runs than the in-progress specs reported in Cypress Cloud. Fixes [#25647](https://github.com/cypress-io/cypress/issues/25647). +- Fixed an issue in middleware where error-handling code could itself generate an error and fail to report the original issue. Fixes [#22825](https://github.com/cypress-io/cypress/issues/22825). +- Fixed an regression introduced in Cypress [12.3.0](#12-3-0) where custom browsers that relied on process environment variables were not found on macOS arm64 architectures. Fixed in [#25753](https://github.com/cypress-io/cypress/pull/25753). **Misc:** -- Improved the layout of the Debug Page on smaller viewports when there is a pending run. Addresses [#25664](https://github.com/cypress-io/cypress/issues/25664). -- Improved the layout of the Debug Page when displaying informational messages. Addresses [#25669](https://github.com/cypress-io/cypress/issues/25669). -- Icons in Debug page will no longer shrink at small viewports. Addresses [#25665](https://github.com/cypress-io/cypress/issues/25665). -- Increased maximum number of failing tests to reflect in sidebar badge to 99. Addresses [#25662](https://github.com/cypress-io/cypress/issues/25662). -- Improved the layout of the Debug Page empty states on smaller viewports. Addressed in [#25703](https://github.com/cypress-io/cypress/pull/25703). -- Increased the spacing between elements and their associated tooltip and added borders around artifact links on the Debug Page. Addresses [#25666](https://github.com/cypress-io/cypress/issues/25666). +- Improved the UI of the Debug page. Addresses [#25664](https://github.com/cypress-io/cypress/issues/25664), [#25669](https://github.com/cypress-io/cypress/issues/25669), [#25665](https://github.com/cypress-io/cypress/issues/25665), [#25666](https://github.com/cypress-io/cypress/issues/25666), and [#25667](https://github.com/cypress-io/cypress/issues/25667). +- Updated the Debug page sidebar badge to to show 0 to 99+ failing tests, increased from showing 0 to 9+ failing tests, to provide better test failure insights. Addresses [#25662](https://github.com/cypress-io/cypress/issues/25662). **Dependency Updates:** -- Upgrade [`debug`][(https://www.npmjs.com/package/debug) to `4.3.4`. Addressed in [#25699](https://github.com/cypress-io/cypress/pull/25699). +- Upgrade [`debug`](https://www.npmjs.com/package/debug) to `4.3.4`. Addressed in [#25699](https://github.com/cypress-io/cypress/pull/25699). ## 12.5.1 diff --git a/package.json b/package.json index 1c9ab667368b..605285f3023e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cypress", - "version": "12.5.1", + "version": "12.6.0", "description": "Cypress is a next generation front end testing tool built for the modern web", "private": true, "scripts": { From b7a21d6238067ce4f2815594acf579323d74e202 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Feb 2023 11:15:56 -0500 Subject: [PATCH 3/7] chore: release @cypress/webpack-batteries-included-preprocessor-v2.4.0 [skip ci] --- npm/webpack-batteries-included-preprocessor/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/npm/webpack-batteries-included-preprocessor/CHANGELOG.md b/npm/webpack-batteries-included-preprocessor/CHANGELOG.md index b5df0a53bb14..b9e2d153cf63 100644 --- a/npm/webpack-batteries-included-preprocessor/CHANGELOG.md +++ b/npm/webpack-batteries-included-preprocessor/CHANGELOG.md @@ -1,3 +1,10 @@ +# [@cypress/webpack-batteries-included-preprocessor-v2.4.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-batteries-included-preprocessor-v2.3.0...@cypress/webpack-batteries-included-preprocessor-v2.4.0) (2023-02-15) + + +### Features + +* Bundle cy.origin() dependencies at runtime ([#25626](https://github.com/cypress-io/cypress/issues/25626)) ([41512c4](https://github.com/cypress-io/cypress/commit/41512c416a80e5158752fef9ffbe722402a5ada4)) + # [@cypress/webpack-batteries-included-preprocessor-v2.3.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-batteries-included-preprocessor-v2.2.4...@cypress/webpack-batteries-included-preprocessor-v2.3.0) (2022-12-02) From 641888ef91802cfabf1163cc7d7bdc2252c733ac Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Feb 2023 11:16:12 -0500 Subject: [PATCH 4/7] chore: release @cypress/webpack-preprocessor-v5.17.0 [skip ci] --- npm/webpack-preprocessor/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/npm/webpack-preprocessor/CHANGELOG.md b/npm/webpack-preprocessor/CHANGELOG.md index ea3f01d1b8eb..02be04fbf2ae 100644 --- a/npm/webpack-preprocessor/CHANGELOG.md +++ b/npm/webpack-preprocessor/CHANGELOG.md @@ -1,3 +1,10 @@ +# [@cypress/webpack-preprocessor-v5.17.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-preprocessor-v5.16.3...@cypress/webpack-preprocessor-v5.17.0) (2023-02-15) + + +### Features + +* Bundle cy.origin() dependencies at runtime ([#25626](https://github.com/cypress-io/cypress/issues/25626)) ([41512c4](https://github.com/cypress-io/cypress/commit/41512c416a80e5158752fef9ffbe722402a5ada4)) + # [@cypress/webpack-preprocessor-v5.16.3](https://github.com/cypress-io/cypress/compare/@cypress/webpack-preprocessor-v5.16.2...@cypress/webpack-preprocessor-v5.16.3) (2023-02-06) # [@cypress/webpack-preprocessor-v5.16.2](https://github.com/cypress-io/cypress/compare/@cypress/webpack-preprocessor-v5.16.1...@cypress/webpack-preprocessor-v5.16.2) (2023-02-02) From 470ee8428464a40539ced7099ea162e7526ce4e0 Mon Sep 17 00:00:00 2001 From: Zachary Williams Date: Wed, 15 Feb 2023 12:48:03 -0600 Subject: [PATCH 5/7] test: skip flaky GitDataSource test (#25825) --- packages/data-context/test/unit/sources/GitDataSource.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/data-context/test/unit/sources/GitDataSource.spec.ts b/packages/data-context/test/unit/sources/GitDataSource.spec.ts index 1a9b5ca34460..0f093c8586b4 100644 --- a/packages/data-context/test/unit/sources/GitDataSource.spec.ts +++ b/packages/data-context/test/unit/sources/GitDataSource.spec.ts @@ -46,7 +46,9 @@ describe('GitDataSource', () => { sinon.restore() }) - it(`gets correct status for files on ${os.platform()}`, async function () { + // TODO: Very flaky on CI, even with a bunch of retries it still fails more often than nought + // see: https://github.com/cypress-io/cypress/issues/25824 + it.skip(`gets correct status for files on ${os.platform()}`, async function () { // TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23317 this.retries(15) From 8e070e849b6a0a3cffe29eb6faebca5f6e866c89 Mon Sep 17 00:00:00 2001 From: Ben M Date: Wed, 15 Feb 2023 14:01:05 -0500 Subject: [PATCH 6/7] chore: making our add-to-triage-board workflow reusable within the Cypress-io org (#25820) * chore: Making our add to triage workflow callable from other projects inside the Cypress-io org in Github --- .github/workflows/triage_add_to_project.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/triage_add_to_project.yml b/.github/workflows/triage_add_to_project.yml index 2a5f775d378c..11e0b99f68a7 100644 --- a/.github/workflows/triage_add_to_project.yml +++ b/.github/workflows/triage_add_to_project.yml @@ -1,6 +1,11 @@ name: 'Triage: add issue/PR to project' on: + # makes this workflow reusable + workflow_call: + secrets: + ADD_TO_PROJECT_TOKEN: + required: true issues: types: - opened From 2c0a6eef38c348ee0e045fcde0166c19d35cbc72 Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Wed, 15 Feb 2023 15:14:45 -0700 Subject: [PATCH 7/7] chore: updated cypress-example-kitchensink version (#25828) --- packages/example/README.md | 2 +- packages/example/package.json | 5 +++-- yarn.lock | 37 ++++++++++++++++++++++++++--------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/example/README.md b/packages/example/README.md index cffce50884e8..8d57d43339f8 100644 --- a/packages/example/README.md +++ b/packages/example/README.md @@ -23,7 +23,7 @@ When a commit is merged into `master`, a new version of the [`cypress-example-ki 1. Bump the `cypress-example-kitchensink` `devDependency` within this package's [`package.json`](https://github.com/cypress-io/cypress/blob/develop/packages/example/package.json). -2. Run `yarn` and `yarn workspace @packages/example` to build the app and spec files. +2. Run `yarn` and `yarn workspace @packages/example build` to build the app and spec files. 3. Create a new pull-request following this repo's [pull request instructions](CONTRIBUTING.md#pull-requests). diff --git a/packages/example/package.json b/packages/example/package.json index 849071906a8f..3db74c65acf9 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -12,11 +12,12 @@ "build": "node ./bin/build.js && gulp build", "build-prod": "yarn build", "predeploy": "yarn build", - "deploy": "echo 'gh-pages -d build -b gh-pages'" + "deploy": "gh-pages -d build -b gh-pages" }, "devDependencies": { "cross-env": "6.0.3", - "cypress-example-kitchensink": "https://github.com/cypress-io/cypress-example-kitchensink.git#d7150632ac15268f2c7a50110cadf039a248ac4e", + "cypress-example-kitchensink": "1.16.0", + "gh-pages": "5.0.0", "gulp": "4.0.2", "gulp-clean": "0.4.0", "gulp-rev-all": "2.0.2", diff --git a/yarn.lock b/yarn.lock index 95e617319b56..a2b43ecd3287 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8873,10 +8873,10 @@ async@0.9.x, async@^0.9.0: resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= -async@>=0.2.9, async@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" - integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== +async@>=0.2.9, async@^3.2.0, async@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== async@^2.1.4, async@^2.4.1, async@^2.6.2: version "2.6.4" @@ -11130,7 +11130,7 @@ commander@2.9.0: dependencies: graceful-readlink ">= 1.0.0" -commander@2.x.x, commander@^2.11.0, commander@^2.12.1, commander@^2.19.0, commander@^2.20.0: +commander@2.x.x, commander@^2.11.0, commander@^2.12.1, commander@^2.18.0, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -12135,9 +12135,10 @@ cypress-each@^1.11.0: resolved "https://registry.yarnpkg.com/cypress-each/-/cypress-each-1.11.0.tgz#013c9b43a950f157bcf082d4bd0bb424fb370441" integrity sha512-zeqeQkppPL6BKLIJdfR5IUoZRrxRudApJapnFzWCkkrmefQSqdlBma2fzhmniSJ3TRhxe5xpK3W3/l8aCrHvwQ== -"cypress-example-kitchensink@https://github.com/cypress-io/cypress-example-kitchensink.git#d7150632ac15268f2c7a50110cadf039a248ac4e": - version "0.0.0-development" - resolved "https://github.com/cypress-io/cypress-example-kitchensink.git#d7150632ac15268f2c7a50110cadf039a248ac4e" +cypress-example-kitchensink@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/cypress-example-kitchensink/-/cypress-example-kitchensink-1.16.0.tgz#b2f7759c2414eb032c82a32ca498b1b6a22b7257" + integrity sha512-JXbNyOMTi1IXqmiEpZRzFEHL03LG09V+b5abxrB39pil+QmAyLKhfdmWspFRu+GWsALdEsdFrcTmexIW6Y15tA== dependencies: npm-run-all "^4.1.2" serve "11.3.0" @@ -13346,6 +13347,11 @@ elliptic@^6.5.3: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +email-addresses@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-5.0.0.tgz#7ae9e7f58eef7d5e3e2c2c2d3ea49b78dc854fa6" + integrity sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw== + email-validator@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" @@ -14976,7 +14982,7 @@ filename-reserved-regex@^2.0.0: resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= -filenamify@^4.1.0: +filenamify@^4.1.0, filenamify@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-4.3.0.tgz#62391cb58f02b09971c9d4f9d63b3cf9aba03106" integrity sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg== @@ -15838,6 +15844,19 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +gh-pages@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-5.0.0.tgz#e0893272a0e33f0453e53a3c017c33b91ddd6394" + integrity sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ== + dependencies: + async "^3.2.4" + commander "^2.18.0" + email-addresses "^5.0.0" + filenamify "^4.3.0" + find-cache-dir "^3.3.1" + fs-extra "^8.1.0" + globby "^6.1.0" + gift@0.10.2: version "0.10.2" resolved "https://registry.yarnpkg.com/gift/-/gift-0.10.2.tgz#4600efe8f284b51fcb01c3527b321e22b494e156"