diff --git a/packages/amplify-app/package.json b/packages/amplify-app/package.json index 8639f98ef0e..d3125eacb78 100644 --- a/packages/amplify-app/package.json +++ b/packages/amplify-app/package.json @@ -41,7 +41,7 @@ "node-emoji": "^1.10.0", "ora": "^4.0.3", "rimraf": "^3.0.0", - "semver": "^7.1.1", + "semver": "^7.3.5", "strip-ansi": "^6.0.0", "xcode": "^2.1.0", "yargs": "^15.1.0" diff --git a/packages/amplify-category-auth/src/index.js b/packages/amplify-category-auth/src/index.js index 65217a4179b..e80c7564929 100644 --- a/packages/amplify-category-auth/src/index.js +++ b/packages/amplify-category-auth/src/index.js @@ -24,6 +24,12 @@ const { projectHasAuth } = require('./provider-utils/awscloudformation/utils/pro const { attachPrevParamsToContext } = require('./provider-utils/awscloudformation/utils/attach-prev-params-to-context'); const { stateManager } = require('amplify-cli-core'); +const { + doesConfigurationIncludeSMS, + loadResourceParameters, + loadImportedAuthParameters, +} = require('./provider-utils/awscloudformation/utils/auth-sms-workflow-helper'); + // this function is being kept for temporary compatability. async function add(context) { const { amplify } = context; @@ -424,6 +430,18 @@ async function importAuth(context) { return providerController.importResource(context, serviceSelection, undefined, undefined, false); } +async function isSMSWorkflowEnabled(context, resourceName) { + const { imported, userPoolId } = context.amplify.getImportedAuthProperties(context); + let userNameAndMfaConfig; + if (imported) { + userNameAndMfaConfig = await loadImportedAuthParameters(context, userPoolId); + } else { + userNameAndMfaConfig = loadResourceParameters(context, resourceName); + } + const result = doesConfigurationIncludeSMS(userNameAndMfaConfig); + return result; +} + module.exports = { externalAuthEnable, checkRequirements, @@ -439,4 +457,5 @@ module.exports = { uploadFiles, category, importAuth, + isSMSWorkflowEnabled, }; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts index d2adc441f23..a05f3ab1baa 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts @@ -139,6 +139,7 @@ export interface ProviderUtils { privateParams: $TSObject, envSpecificParams: string[], ): void; + loadResourceParameters(context: $TSContext, category: string, resourceName: string): Record; } export type ImportAuthHeadlessParameters = { diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-sms-workflow-helper.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-sms-workflow-helper.ts new file mode 100644 index 00000000000..fe316c565f1 --- /dev/null +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-sms-workflow-helper.ts @@ -0,0 +1,46 @@ +import { $TSContext } from 'amplify-cli-core'; +import { ProviderUtils } from '../import/types'; + +import { ServiceQuestionsResult } from '../service-walkthrough-types'; +import { supportedServices } from '../../supported-services'; + +export type UserPoolMessageConfiguration = { + mfaConfiguration?: string; + mfaTypes?: string[]; + usernameAttributes?: string[]; +}; + +export const doesConfigurationIncludeSMS = (request: UserPoolMessageConfiguration): boolean => { + if ((request.mfaConfiguration === 'OPTIONAL' || request.mfaConfiguration === 'ON') && request.mfaTypes?.includes('SMS Text Message')) { + return true; + } + + if (request.usernameAttributes?.includes('phone_number')) { + return true; + } + + return false; +}; + +const getProviderPlugin = (context: $TSContext): ProviderUtils => { + const serviceMetaData = supportedServices.Cognito; + const { provider } = serviceMetaData; + + return context.amplify.getPluginInstance(context, provider); +}; +export const loadResourceParameters = (context: $TSContext, resourceName: string): UserPoolMessageConfiguration => { + const providerPlugin = getProviderPlugin(context); + return providerPlugin.loadResourceParameters(context, 'auth', resourceName) as ServiceQuestionsResult; +}; + +export const loadImportedAuthParameters = async (context: $TSContext, userPoolName: string): Promise => { + const providerPlugin = getProviderPlugin(context); + const cognitoUserPoolService = await providerPlugin.createCognitoUserPoolService(context); + const userPoolDetails = await cognitoUserPoolService.getUserPoolDetails(userPoolName); + const mfaConfig = await cognitoUserPoolService.getUserPoolMfaConfig(userPoolName); + return { + mfaConfiguration: mfaConfig.MfaConfiguration, + usernameAttributes: userPoolDetails.UsernameAttributes, + mfaTypes: mfaConfig.SmsMfaConfiguration ? ['SMS Text Message'] : [], + }; +}; diff --git a/packages/amplify-cli-core/package.json b/packages/amplify-cli-core/package.json index 0190c2dda4b..88f59e90fd3 100644 --- a/packages/amplify-cli-core/package.json +++ b/packages/amplify-cli-core/package.json @@ -33,7 +33,10 @@ "hjson": "^3.2.1", "js-yaml": "^4.0.0", "lodash": "^4.17.19", - "open": "^7.3.1" + "node-fetch": "^2.6.1", + "open": "^7.3.1", + "proxy-agent": "^4.0.1", + "semver": "^7.3.5" }, "devDependencies": { "@types/fs-extra": "^8.0.1", @@ -44,6 +47,7 @@ "@types/rimraf": "^3.0.0", "@types/uuid": "^8.0.0", "amplify-function-plugin-interface": "1.7.2", + "nock": "^13.0.11", "rimraf": "^3.0.0" }, "jest": { diff --git a/packages/amplify-cli-core/src/__tests__/banner-message/index.test.ts b/packages/amplify-cli-core/src/__tests__/banner-message/index.test.ts new file mode 100644 index 00000000000..78e0ac3bca7 --- /dev/null +++ b/packages/amplify-cli-core/src/__tests__/banner-message/index.test.ts @@ -0,0 +1,117 @@ +import nock from 'nock'; +import url from 'url'; +import { BannerMessage, AWS_AMPLIFY_DEFAULT_BANNER_URL, Message } from '../../banner-message'; + +const ONE_DAY = 1000 * 60 * 60 * 24; +let mockServer: nock.Interceptor; +let serverResponse: { version: string; messages: Message[] }; +describe('BannerMessage', () => { + beforeEach(async () => { + serverResponse = { + version: '1.0.0', + messages: [ + { + message: 'first message', + id: 'first', + conditions: { + enabled: true, + cliVersions: '4.41.0', + startTime: new Date(Date.now() - ONE_DAY).toISOString(), + endTime: new Date(Date.now() + ONE_DAY).toISOString(), + }, + }, + ], + }; + + const urlInfo = url.parse(AWS_AMPLIFY_DEFAULT_BANNER_URL); + mockServer = nock(`${urlInfo.protocol}//${urlInfo.host}`).get(urlInfo.pathname!); + + await BannerMessage.initialize('4.41.0'); + }); + afterEach(() => { + BannerMessage.releaseInstance(); + }); + + it('should return message by fetching it from remote url', async () => { + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toEqual('first message'); + }); + + it('should return message when there are no conditions', async () => { + delete serverResponse.messages[0].conditions; + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toEqual('first message'); + }); + + it('should not throw error when server sends 404', async () => { + mockServer.reply(404, 'page not found'); + const result = await BannerMessage.getMessage('first'); + expect(result).toBeUndefined(); + }); + + it('Should not process the Banner response if the response version is not supported', async () => { + serverResponse.version = '20.2'; + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toBeUndefined(); + }); + + it('should not return message when the message version does not match', async () => { + serverResponse.messages[0].conditions!.cliVersions! = '110022.0.0'; + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toBeUndefined(); + }); + + it('should not show message when message is not enabled', async () => { + serverResponse.messages[0].conditions!.enabled = false; + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toBeUndefined(); + }); + + it('should show message when conditions.enabled is undefined', async () => { + (serverResponse.messages[0].conditions!.enabled as any) = undefined; + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toEqual('first message'); + }); + + it('should not show message when startDate is after current Date', async () => { + serverResponse.messages[0].conditions!.endTime = undefined; + serverResponse.messages[0].conditions!.startTime = new Date(Date.now() + ONE_DAY).toISOString(); + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toBeUndefined(); + }); + + it('should not show message when endDate is before current Date', async () => { + serverResponse.messages[0].conditions!.startTime = undefined; + serverResponse.messages[0].conditions!.endTime = new Date(Date.now() - ONE_DAY).toISOString(); + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toBeUndefined(); + }); + + it('should show message when start and endDate are not defined', async () => { + delete serverResponse.messages[0].conditions!.startTime; + delete serverResponse.messages[0]!.conditions!.endTime; + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toEqual('first message'); + }); + + it('should show message when cliVersions is undefined', async () => { + delete serverResponse.messages[0].conditions!.cliVersions; + mockServer.reply(200, serverResponse, { 'Content-Type': 'application/json' }); + const result = await BannerMessage.getMessage('first'); + expect(result).toEqual('first message'); + }); + + it('should throw error when BannerMessage is not initialized', async () => { + BannerMessage.releaseInstance(); + await expect(() => BannerMessage.getMessage('first')).rejects.toThrowError('BannerMessage is not initialized'); + }); +}); diff --git a/packages/amplify-cli-core/src/banner-message/index.ts b/packages/amplify-cli-core/src/banner-message/index.ts new file mode 100644 index 00000000000..eb53db9ab7c --- /dev/null +++ b/packages/amplify-cli-core/src/banner-message/index.ts @@ -0,0 +1,96 @@ +import fetch from 'node-fetch'; +import semver from 'semver'; +import ProxyAgent from 'proxy-agent'; +import { getLogger } from '../logger'; + +export type Message = { + message: string; + id: string; + conditions?: { + enabled: boolean; + cliVersions?: string; + startTime?: string; + endTime?: string; + }; +}; + +export const AWS_AMPLIFY_DEFAULT_BANNER_URL: string = 'https://aws-amplify.github.io/amplify-cli/banner-message.json'; +const MAX_SUPPORTED_MESSAGE_CONFIG_VERSION = '1.0.0'; + +const logger = getLogger('amplify-cli-core', 'banner-message/index.ts'); + +export class BannerMessage { + private static instance?: BannerMessage; + private messages: Message[] = []; + + public static initialize = (cliVersion: string): BannerMessage => { + if (!BannerMessage.instance) { + BannerMessage.instance = new BannerMessage(cliVersion); + } + + return BannerMessage.instance; + }; + + private static ensureInitialized = () => { + if (!BannerMessage.instance) { + throw new Error('BannerMessage is not initialized'); + } + }; + + private constructor(private cliVersion: string) {} + + private fetchMessages = async (url: string): Promise => { + try { + logger.info(`fetch banner messages from ${url}`); + const proxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY; + const fetchOptions = proxy ? { agent: new ProxyAgent(proxy) } : {}; + const result = await fetch(url, fetchOptions); + const body = await result.json(); + if (!semver.satisfies(body.version, MAX_SUPPORTED_MESSAGE_CONFIG_VERSION)) { + return; + } + this.messages = body.messages ?? []; + } catch (e) { + // network error should not cause CLI execution failure + logger.error('fetch banner message failed', e); + } + }; + + public static getMessage = async (messageId: string): Promise => { + BannerMessage.ensureInitialized(); + return BannerMessage.instance!.getMessages(messageId); + }; + + getMessages = async (messageId: string): Promise => { + if (!this.messages.length) { + await this.fetchMessages(process.env.AMPLIFY_CLI_BANNER_MESSAGE_URL ?? AWS_AMPLIFY_DEFAULT_BANNER_URL); + } + + const matchingMessageItems = this.messages.filter( + m => + m.id === messageId && + m.conditions?.enabled !== false && + (m.conditions?.cliVersions ? semver.satisfies(this.cliVersion, m.conditions.cliVersions) : true), + ); + + const messageItem = matchingMessageItems.find(m => { + if (m.conditions) { + const currentTime = Date.now(); + const startTime = m.conditions?.startTime ? Date.parse(m.conditions?.startTime) : currentTime; + const endTime = m.conditions?.endTime ? Date.parse(m.conditions?.endTime) : currentTime; + return currentTime >= startTime && currentTime <= endTime; + } + return true; + }); + + return messageItem?.message; + }; + + /** + * @internal + * package private method used in unit tests to release the instance + */ + public static releaseInstance = (): void => { + BannerMessage.instance = undefined; + }; +} diff --git a/packages/amplify-cli-core/src/index.ts b/packages/amplify-cli-core/src/index.ts index baec0561c1a..fc73f0ce98b 100644 --- a/packages/amplify-cli-core/src/index.ts +++ b/packages/amplify-cli-core/src/index.ts @@ -17,6 +17,7 @@ export * from './cliConstants'; export * from './deploymentSecretsHelper'; export * from './deploymentState'; export * from './utils'; +export * from './banner-message'; // Temporary types until we can finish full type definition across the whole CLI @@ -174,7 +175,7 @@ interface AmplifyToolkit { getEnvDetails: () => $TSAny; getEnvInfo: () => $TSAny; getProviderPlugins: (context: $TSContext) => $TSAny; - getPluginInstance: () => $TSAny; + getPluginInstance: (context: $TSContext, pluginName: string) => $TSAny; getProjectConfig: () => $TSAny; getProjectDetails: () => $TSAny; getProjectMeta: () => $TSMeta; diff --git a/packages/amplify-cli-core/src/logger/index.ts b/packages/amplify-cli-core/src/logger/index.ts new file mode 100644 index 00000000000..468c3f38cca --- /dev/null +++ b/packages/amplify-cli-core/src/logger/index.ts @@ -0,0 +1,12 @@ +import { Redactor, logger } from 'amplify-cli-logger'; + +export const getLogger = (moduleName: string, fileName: string) => { + return { + info: (message: string, args: any = {}) => { + logger.logInfo({ message: `${moduleName}.${fileName}.${message}(${Redactor(JSON.stringify(args))}` }); + }, + error: (message: string, error: Error) => { + logger.logError({ message: `${moduleName}.${fileName}.${message}`, error }); + }, + }; +}; diff --git a/packages/amplify-cli/package.json b/packages/amplify-cli/package.json index 2d25c21d443..217126f399b 100644 --- a/packages/amplify-cli/package.json +++ b/packages/amplify-cli/package.json @@ -93,7 +93,7 @@ "parse-json": "^5.0.0", "progress": "^2.0.3", "promise-sequential": "^1.1.1", - "semver": "^7.1.1", + "semver": "^7.3.5", "tar-fs": "^2.1.1", "update-notifier": "^4.1.0", "which": "^2.0.2", diff --git a/packages/amplify-cli/src/__tests__/test-aborting.test.ts b/packages/amplify-cli/src/__tests__/test-aborting.test.ts index 1a92f461f50..739058866ca 100644 --- a/packages/amplify-cli/src/__tests__/test-aborting.test.ts +++ b/packages/amplify-cli/src/__tests__/test-aborting.test.ts @@ -32,6 +32,9 @@ describe('test SIGINT with execute', () => { FeatureFlags: { initialize: jest.fn(), }, + BannerMessage: { + initialize: jest.fn(), + }, PathConstants: { TeamProviderFileName: 'team-provider-info.json', DeploymentSecretsFileName: 'deployment-secrets.json', diff --git a/packages/amplify-cli/src/index.ts b/packages/amplify-cli/src/index.ts index 2ac8b8cbc0a..83806e2f115 100644 --- a/packages/amplify-cli/src/index.ts +++ b/packages/amplify-cli/src/index.ts @@ -8,6 +8,8 @@ import { pathManager, stateManager, TeamProviderInfoMigrateError, + BannerMessage, + JSONUtilities, } from 'amplify-cli-core'; import { isCI } from 'ci-info'; import { EventEmitter } from 'events'; @@ -38,7 +40,7 @@ Error.stackTraceLimit = Number.MAX_SAFE_INTEGER; let errorHandler = (e: Error) => {}; -process.on('uncaughtException', function (error) { +process.on('uncaughtException', function(error) { // Invoke the configured error handler if it is already configured if (errorHandler) { errorHandler(error); @@ -57,7 +59,7 @@ process.on('uncaughtException', function (error) { }); // In this handler we have to rethrow the error otherwise the process stucks there. -process.on('unhandledRejection', function (error) { +process.on('unhandledRejection', function(error) { throw error; }); @@ -65,6 +67,7 @@ process.on('unhandledRejection', function (error) { export async function run() { try { deleteOldVersion(); + let pluginPlatform = await getPluginPlatform(); let input = getCommandLineInput(pluginPlatform); // with non-help command supplied, give notification before execution @@ -73,6 +76,10 @@ export async function run() { notify({ defer: false, isGlobal: true }); } + // Initialize Banner messages. These messages are set on the server side + const pkg = JSONUtilities.readJson<$TSAny>(path.join(__dirname, '..', 'package.json')); + BannerMessage.initialize(pkg.version); + ensureFilePermissions(pathManager.getAWSCredentialsFilePath()); ensureFilePermissions(pathManager.getAWSConfigFilePath()); diff --git a/packages/amplify-go-function-runtime-provider/package.json b/packages/amplify-go-function-runtime-provider/package.json index 570a17ed031..36dad22c79a 100644 --- a/packages/amplify-go-function-runtime-provider/package.json +++ b/packages/amplify-go-function-runtime-provider/package.json @@ -28,7 +28,7 @@ "fs-extra": "^8.1.0", "glob": "^7.1.6", "portfinder": "^1.0.25", - "semver": "^7.1.3", + "semver": "^7.3.5", "which": "^2.0.2" }, "devDependencies": { diff --git a/packages/amplify-java-function-runtime-provider/package.json b/packages/amplify-java-function-runtime-provider/package.json index 023f3aca0e2..ec4ecc19349 100644 --- a/packages/amplify-java-function-runtime-provider/package.json +++ b/packages/amplify-java-function-runtime-provider/package.json @@ -26,7 +26,7 @@ "execa": "^4.1.0", "fs-extra": "^8.1.0", "glob": "^7.1.6", - "semver": "^7.1.3", + "semver": "^7.3.5", "which": "^2.0.2" }, "devDependencies": { diff --git a/packages/amplify-provider-awscloudformation/src/display-helpful-urls.js b/packages/amplify-provider-awscloudformation/src/display-helpful-urls.js index 10b00d209af..475048d26ec 100644 --- a/packages/amplify-provider-awscloudformation/src/display-helpful-urls.js +++ b/packages/amplify-provider-awscloudformation/src/display-helpful-urls.js @@ -1,4 +1,9 @@ +// @ts-check const chalk = require('chalk'); +const { BannerMessage } = require('amplify-cli-core'); +const { fileLogger } = require('./utils/aws-logger'); + +const logger = fileLogger('display-helpful-urls'); async function displayHelpfulURLs(context, resourcesToBeCreated) { context.print.info(''); @@ -9,6 +14,7 @@ async function displayHelpfulURLs(context, resourcesToBeCreated) { showContainerHostingInfo(context, resourcesToBeCreated); showHostedUIURLs(context, resourcesToBeCreated); await showRekognitionURLS(context, resourcesToBeCreated); + await showCognitoSandBoxMessage(context, resourcesToBeCreated); context.print.info(''); } @@ -91,15 +97,17 @@ function showRestAPIURL(context, resourcesToBeCreated) { } function showContainerHostingInfo(context, resourcesToBeCreated) { - const resource = resourcesToBeCreated.find(resource => resource.category === 'hosting' && resource.service === 'ElasticContainer' && !resource.hostedZoneId); + const resource = resourcesToBeCreated.find( + resource => resource.category === 'hosting' && resource.service === 'ElasticContainer' && !resource.hostedZoneId, + ); if (resource && resource.output) { const { output: { LoadBalancerCnameDomainName, LoadBalancerAliasDomainName, CloudfrontDistributionAliasDomainName, - CloudfrontDistributionCnameDomainName - } + CloudfrontDistributionCnameDomainName, + }, } = resource; context.print.info(`Make sure to add the following CNAMEs to your domain’s DNS records:\n`); @@ -162,6 +170,34 @@ function showHostedUIURLs(context, resourcesToBeCreated) { } } +async function showCognitoSandBoxMessage(context, resources) { + const smsSandBoxMessage = await BannerMessage.getMessage('COGNITO_SMS_SANDBOX_UPDATE_WARNING'); + if (!smsSandBoxMessage) { + return; + } + + const cognitoResource = resources.filter(resource => resource.service === 'Cognito'); + + if (cognitoResource.length > 0) { + const log = logger('showCognitoSandBoxMessage', [cognitoResource[0].resourceName]); + try { + log(); + const smsWorkflowEnabled = await await context.amplify.invokePluginMethod(context, 'auth', 'cognito', 'isSMSWorkflowEnabled', [ + context, + cognitoResource[0].resourceName, + ]); + if (smsWorkflowEnabled) { + context.print.warning(smsSandBoxMessage); + return; + } + } catch (e) { + if (e.name !== 'MethodNotFound') { + log(e); + } + } + } +} + async function showRekognitionURLS(context, resourcesToBeCreated) { const resource = resourcesToBeCreated.find(resource => { if (resource.identifyType && resource.identifyType === 'identifyEntities') { diff --git a/packages/amplify-python-function-runtime-provider/package.json b/packages/amplify-python-function-runtime-provider/package.json index d9ff66f6150..7c7b3e17a0f 100644 --- a/packages/amplify-python-function-runtime-provider/package.json +++ b/packages/amplify-python-function-runtime-provider/package.json @@ -27,7 +27,7 @@ "execa": "^4.1.0", "glob": "^7.1.6", "ini": "^1.3.5", - "semver": "^7.1.3", + "semver": "^7.3.5", "which": "^2.0.2" }, "devDependencies": { diff --git a/packages/amplify-util-mock/package.json b/packages/amplify-util-mock/package.json index 3b67018e6fb..afaf1de7c31 100644 --- a/packages/amplify-util-mock/package.json +++ b/packages/amplify-util-mock/package.json @@ -38,7 +38,7 @@ "execa": "^4.1.0", "fs-extra": "^8.1.0", "lodash": "^4.17.19", - "semver": "^7.1.3", + "semver": "^7.3.5", "which": "^2.0.2" }, "devDependencies": { diff --git a/tsconfig.base.json b/tsconfig.base.json index 5a740d5a0c8..52922e9d939 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -57,7 +57,8 @@ "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, "resolveJsonModule": true, "preserveConstEnums": true, - "pretty": true + "pretty": true, + "stripInternal": true }, "exclude": ["**/__mocks__/**/*", "**/__tests__/**/*", "**/lib/**/*"] } diff --git a/yarn.lock b/yarn.lock index a597146a3b3..2f07b9bc2d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18124,6 +18124,16 @@ nock@^12.0.3: lodash "^4.17.13" propagate "^2.0.0" +nock@^13.0.11: + version "13.0.11" + resolved "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz#ba733252e720897ca50033205c39db0c7470f331" + integrity sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash.set "^4.3.2" + propagate "^2.0.0" + node-abi@^2.7.0: version "2.19.1" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.1.tgz#6aa32561d0a5e2fdb6810d8c25641b657a8cea85" @@ -18154,7 +18164,7 @@ node-fetch@2.1.2: node-fetch@2.6.1, node-fetch@^2.2.0, node-fetch@^2.5.0, node-fetch@^2.6.1: version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== node-fetch@^1.0.1: @@ -20320,7 +20330,7 @@ proxy-addr@~2.0.5: proxy-agent@^4.0.0, proxy-agent@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-4.0.1.tgz#326c3250776c7044cd19655ccbfadf2e065a045c" + resolved "https://registry.npmjs.org/proxy-agent/-/proxy-agent-4.0.1.tgz#326c3250776c7044cd19655ccbfadf2e065a045c" integrity sha512-ODnQnW2jc/FUVwHHuaZEfN5otg/fMbvMxz9nMSUQfJ9JU7q2SZvSULSsjLloVgJOiv9yhc8GlNMKc4GkFmcVEA== dependencies: agent-base "^6.0.0" @@ -21645,7 +21655,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.3.2, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2: +semver@7.3.2, semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== @@ -21657,6 +21667,13 @@ semver@7.x, semver@^7.2.1: dependencies: lru-cache "^6.0.0" +semver@^7.3.5: + version "7.3.5" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"