diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 3730c7c97a301..960d14bb8b426 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -34,7 +34,7 @@ export interface SignInAliases { readonly phone?: boolean; /** - * Whether a user is allowed to ign in with a secondary username, that can be set and modified after sign up. + * Whether a user is allowed to sign in with a secondary username, that can be set and modified after sign up. * Can only be used in conjunction with `USERNAME`. * @default false */ diff --git a/packages/@aws-cdk/aws-ecr/README.md b/packages/@aws-cdk/aws-ecr/README.md index 97115adc5f647..a2e4ab8ee3d35 100644 --- a/packages/@aws-cdk/aws-ecr/README.md +++ b/packages/@aws-cdk/aws-ecr/README.md @@ -37,6 +37,22 @@ repository.onImageScanCompleted('ImageScanComplete') .addTarget(...) ``` +### Authorization Token + +Besides the Amazon ECR APIs, ECR also allows the Docker CLI or a language-specific Docker library to push and pull +images from an ECR repository. However, the Docker CLI does not support native IAM authentication methods and +additional steps must be taken so that Amazon ECR can authenticate and authorize Docker push and pull requests. +More information can be found at at [Registry Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth). + +A Docker authorization token can be obtained using the `GetAuthorizationToken` ECR API. The following code snippets +grants an IAM user access to call this API. + +```ts +import * as iam from '@aws-cdk/aws-iam'; + +const user = new iam.User(this, 'User', { ... }); +AuthorizationToken.grantRead(user); +``` ### Automatically clean up repositories diff --git a/packages/@aws-cdk/aws-ecr/lib/auth-token.ts b/packages/@aws-cdk/aws-ecr/lib/auth-token.ts new file mode 100644 index 0000000000000..52c10cc513d0a --- /dev/null +++ b/packages/@aws-cdk/aws-ecr/lib/auth-token.ts @@ -0,0 +1,20 @@ +import * as iam from '@aws-cdk/aws-iam'; + +/** + * Authorization token to access ECR repositories via Docker CLI. + */ +export class AuthorizationToken { + /** + * Grant access to retrieve an authorization token. + */ + public static grantRead(grantee: iam.IGrantable) { + grantee.grantPrincipal.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['ecr:GetAuthorizationToken'], + // GetAuthorizationToken only allows '*'. See https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelasticcontainerregistry.html#amazonelasticcontainerregistry-actions-as-permissions + resources: ['*'], + })); + } + + private constructor() { + } +} diff --git a/packages/@aws-cdk/aws-ecr/lib/index.ts b/packages/@aws-cdk/aws-ecr/lib/index.ts index bfeae449ded7c..63fff9b49a1c4 100644 --- a/packages/@aws-cdk/aws-ecr/lib/index.ts +++ b/packages/@aws-cdk/aws-ecr/lib/index.ts @@ -3,3 +3,4 @@ export * from './ecr.generated'; export * from './repository'; export * from './lifecycle'; +export * from './auth-token'; diff --git a/packages/@aws-cdk/aws-ecr/test/test.auth-token.ts b/packages/@aws-cdk/aws-ecr/test/test.auth-token.ts new file mode 100644 index 0000000000000..4e9e12e4fb078 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr/test/test.auth-token.ts @@ -0,0 +1,31 @@ +import { expect, haveResourceLike } from '@aws-cdk/assert'; +import * as iam from '@aws-cdk/aws-iam'; +import { Stack } from '@aws-cdk/core'; +import { Test } from 'nodeunit'; +import { AuthorizationToken } from '../lib'; + +export = { + 'grant()'(test: Test) { + // GIVEN + const stack = new Stack(); + const user = new iam.User(stack, 'User'); + + // WHEN + AuthorizationToken.grantRead(user); + + // THEN + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'ecr:GetAuthorizationToken', + Effect: 'Allow', + Resource: '*', + }, + ], + }, + })); + + test.done(); + }, +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index 2391a9015d690..afb611e22c6bb 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -68,7 +68,7 @@ "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "delay": "4.4.0", - "esbuild": "^0.8.9", + "esbuild": "^0.8.17", "pkglint": "0.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json index 89a8de563b17f..75ecb420e7ef5 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3Bucket70E87BF4" + "Ref": "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3BucketF29906B2" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3VersionKey888B2605" + "Ref": "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3VersionKey320A7F12" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3VersionKey888B2605" + "Ref": "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3VersionKey320A7F12" } ] } @@ -92,17 +92,17 @@ } }, "Parameters": { - "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3Bucket70E87BF4": { + "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3BucketF29906B2": { "Type": "String", - "Description": "S3 bucket for asset \"a366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1\"" + "Description": "S3 bucket for asset \"2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835\"" }, - "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3VersionKey888B2605": { + "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3VersionKey320A7F12": { "Type": "String", - "Description": "S3 key for asset version \"a366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1\"" + "Description": "S3 key for asset version \"2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835\"" }, - "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1ArtifactHash01A9D743": { + "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835ArtifactHash9E439F77": { "Type": "String", - "Description": "Artifact hash for asset \"a366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1\"" + "Description": "Artifact hash for asset \"2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts index 28825543a31d3..a4418867356e7 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts @@ -1,3 +1,4 @@ +/// !cdk-integ pragma:ignore-assets import * as path from 'path'; import { Runtime } from '@aws-cdk/aws-lambda'; import { App, Stack, StackProps } from '@aws-cdk/core'; diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json index 97a6b1425b48a..e41f6a5cc565e 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3BucketDF2E98AE" + "Ref": "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3Bucket499E594B" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3VersionKey240B23FB" + "Ref": "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3VersionKey0D51A516" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3VersionKey240B23FB" + "Ref": "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3VersionKey0D51A516" } ] } @@ -835,17 +835,17 @@ } }, "Parameters": { - "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3BucketDF2E98AE": { + "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3Bucket499E594B": { "Type": "String", - "Description": "S3 bucket for asset \"87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756e\"" + "Description": "S3 bucket for asset \"1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dc\"" }, - "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3VersionKey240B23FB": { + "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3VersionKey0D51A516": { "Type": "String", - "Description": "S3 key for asset version \"87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756e\"" + "Description": "S3 key for asset version \"1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dc\"" }, - "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eArtifactHashC957048B": { + "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcArtifactHashECB545C0": { "Type": "String", - "Description": "Artifact hash for asset \"87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756e\"" + "Description": "Artifact hash for asset \"1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dc\"" }, "AssetParameters565126ff75ab727f0b3a67e78cf0fa9996426d1458123e8d6ee81c7ea93a6cfaS3BucketDFAA619E": { "Type": "String", diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts index 1c700d9b4d55c..f08a041178e5e 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts @@ -1,3 +1,4 @@ +/// !cdk-integ pragma:ignore-assets import * as path from 'path'; import { Vpc } from '@aws-cdk/aws-ec2'; import { Runtime } from '@aws-cdk/aws-lambda'; diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts index 33bc8a709efcf..2b5361572e804 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts @@ -1,6 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; -import { IResource, RemovalPolicy, Resource, SecretValue, Stack, Token } from '@aws-cdk/core'; +import { FeatureFlags, Fn, IResource, RemovalPolicy, Resource, SecretValue, Stack, Token } from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; import { IConstruct, Construct } from 'constructs'; import { ResourcePolicy } from './policy'; import { RotationSchedule, RotationScheduleOptions } from './rotation-schedule'; @@ -30,7 +31,10 @@ export interface ISecret extends IResource { readonly secretFullArn?: string; /** - * The name of the secret + * The name of the secret. + * + * For "owned" secrets, this will be the full resource name (secret name + suffix), unless the + * '@aws-cdk/aws-secretsmanager:parseOwnedSecretName' feature flag is set. */ readonly secretName: string; @@ -437,7 +441,10 @@ export class Secret extends SecretBase { }); this.encryptionKey = props.encryptionKey; - this.secretName = parseSecretName(this, this.secretArn); + const parseOwnedSecretName = FeatureFlags.of(this).isEnabled(cxapi.SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME); + this.secretName = parseOwnedSecretName + ? parseSecretNameForOwnedSecret(this, this.secretArn, props.secretName) + : parseSecretName(this, this.secretArn); // @see https://docs.aws.amazon.com/kms/latest/developerguide/services-secrets-manager.html#asm-authz const principal = @@ -707,6 +714,39 @@ function parseSecretName(construct: IConstruct, secretArn: string) { throw new Error('invalid ARN format; no secret name provided'); } +/** + * Parses the secret name from the ARN of an owned secret. With owned secrets we know a few things we don't with imported secrets: + * - The ARN is guaranteed to be a full ARN, with suffix. + * - The name -- if provided -- will tell us how many hyphens to expect in the final secret name. + * - If the name is not provided, we know the format used by CloudFormation for auto-generated names. + * + * Note: This is done rather than just returning the secret name passed in by the user to keep the relationship + * explicit between the Secret and wherever the secretName might be used (i.e., using Tokens). + */ +function parseSecretNameForOwnedSecret(construct: Construct, secretArn: string, secretName?: string) { + const resourceName = Stack.of(construct).parseArn(secretArn, ':').resourceName; + if (!resourceName) { + throw new Error('invalid ARN format; no secret name provided'); + } + + // Secret name was explicitly provided, but is unresolved; best option is to use it directly. + // If it came from another Secret, it should (hopefully) already be properly formatted. + if (secretName && Token.isUnresolved(secretName)) { + return secretName; + } + + // If no secretName was provided, the name will be automatically generated by CloudFormation. + // The autogenerated names have the form of `${logicalID}-${random}`. + // Otherwise, we can use the existing secretName to determine how to parse the resulting resourceName. + const secretNameHyphenatedSegments = secretName ? secretName.split('-').length : 2; + // 2 => [0, 1] + const segmentIndexes = [...new Array(secretNameHyphenatedSegments)].map((_, i) => i); + + // Create the secret name from the resource name by joining all the known segments together. + // This should have the effect of stripping the final hyphen and SecretManager suffix. + return Fn.join('-', segmentIndexes.map(i => Fn.select(i, Fn.split('-', resourceName)))); +} + /** Performs a best guess if an ARN is complete, based on if it ends with a 6-character suffix. */ function arnIsComplete(secretArn: string): boolean { return Token.isUnresolved(secretArn) || /-[a-z0-9]{6}$/i.test(secretArn); diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index 9cf9e71ee87e7..9c6c5a435b3a8 100644 --- a/packages/@aws-cdk/aws-secretsmanager/package.json +++ b/packages/@aws-cdk/aws-secretsmanager/package.json @@ -86,6 +86,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^3.2.0" }, "peerDependencies": { @@ -95,6 +96,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^3.2.0" }, "engines": { diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.expected.json b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.expected.json new file mode 100644 index 0000000000000..b1b91a239d649 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.expected.json @@ -0,0 +1,392 @@ +{ + "Resources": { + "DefaultSecret67034726": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {} + } + }, + "NamedSecret7CD5422D": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {}, + "Name": "namedSecret" + } + }, + "NamedSecretWithHyphen6DC9716A": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {}, + "Name": "named-secret-1" + } + }, + "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {} + } + }, + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderRole4A6F8B2A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + { + "Ref": "DefaultSecret67034726" + }, + { + "Ref": "NamedSecret7CD5422D" + }, + { + "Ref": "NamedSecretWithHyphen6DC9716A" + }, + { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + } + ], + "Action": [ + "secretsmanager:DescribeSecret" + ] + } + ] + } + } + ] + } + }, + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderHandler2A57F6AE": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3BucketED542E1C" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3VersionKey10487FD6" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3VersionKey10487FD6" + } + ] + } + ] + } + ] + ] + } + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderRole4A6F8B2A", + "Arn" + ] + }, + "Runtime": "nodejs12.x" + }, + "DependsOn": [ + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderRole4A6F8B2A" + ] + }, + "SecretNameVerification": { + "Type": "Custom::IntegVerificationSecretNameMatches", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderHandler2A57F6AE", + "Arn" + ] + }, + "Secrets": [ + { + "secretArn": { + "Ref": "DefaultSecret67034726" + }, + "secretName": { + "Fn::Join": [ + "-", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "DefaultSecret67034726" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "DefaultSecret67034726" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } + }, + { + "secretArn": { + "Ref": "NamedSecret7CD5422D" + }, + "secretName": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecret7CD5422D" + } + ] + } + ] + } + ] + } + ] + } + }, + { + "secretArn": { + "Ref": "NamedSecretWithHyphen6DC9716A" + }, + "secretName": { + "Fn::Join": [ + "-", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecretWithHyphen6DC9716A" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecretWithHyphen6DC9716A" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecretWithHyphen6DC9716A" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } + }, + { + "secretArn": { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + }, + "secretName": { + "Fn::Join": [ + "-", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3BucketED542E1C": { + "Type": "String", + "Description": "S3 bucket for asset \"2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9\"" + }, + "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3VersionKey10487FD6": { + "Type": "String", + "Description": "S3 key for asset version \"2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9\"" + }, + "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9ArtifactHashB26239A1": { + "Type": "String", + "Description": "Artifact hash for asset \"2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/.gitignore b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/.gitignore new file mode 100644 index 0000000000000..033e6722bb6e0 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/.gitignore @@ -0,0 +1 @@ +!index.js diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/index.js b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/index.js new file mode 100644 index 0000000000000..758813d070cc5 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/index.js @@ -0,0 +1,21 @@ +/* eslint-disable no-console */ +var AWS = require('aws-sdk'); + +exports.handler = async function (event) { + const props = event.ResourceProperties; + + if (event.RequestType === 'Create' || event.RequestType === 'Update') { + const secretsmanager = new AWS.SecretsManager(); + console.log(`Secrets to validate: ${props.Secrets}`); + for (const secret of props.Secrets) { + await validateSecretNameMatchesExpected(secretsmanager, secret); + } + } +} + +async function validateSecretNameMatchesExpected(secretsmanager, secretInfo) { + const secret = await secretsmanager.describeSecret({ SecretId: secretInfo.secretArn }).promise(); + if (secretInfo.secretName !== secret.Name) { + throw new Error(`Actual secret name doesn't match expected. Actual: (${secret.Name}), Expected: (${secretInfo.secretName})`); + } +} diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts new file mode 100644 index 0000000000000..49dde40e8b9f4 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts @@ -0,0 +1,47 @@ +import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; +import * as secretsmanager from '../lib'; + +/** + * Creates several secrets, with varying names and IDs, with the parseOwnedSecretName feature flag set, + * to verify the secretName returned by `Secret.secretName` matches the `Name` returned by `DescribeSecrets`. + */ + +class SecretsManagerStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const defaultSecret = new secretsmanager.Secret(this, 'DefaultSecret'); + const namedSecret = new secretsmanager.Secret(this, 'NamedSecret', { secretName: 'namedSecret' }); + const namedSecretWithHyphen = new secretsmanager.Secret(this, 'NamedSecretWithHyphen', { secretName: 'named-secret-1' }); + const longSecret = new secretsmanager.Secret(this, 'AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName'); + + const secrets = [defaultSecret, namedSecret, namedSecretWithHyphen, longSecret]; + + const resourceType = 'Custom::IntegVerificationSecretNameMatches'; + const serviceToken = cdk.CustomResourceProvider.getOrCreate(this, resourceType, { + codeDirectory: path.join(__dirname, 'integ.secret-name-parsed.handler'), + runtime: cdk.CustomResourceProviderRuntime.NODEJS_12, + policyStatements: [{ + Effect: 'Allow', + Resource: secrets.map(s => s.secretArn), + Action: ['secretsmanager:DescribeSecret'], + }], + }); + new cdk.CustomResource(this, 'SecretNameVerification', { + resourceType: resourceType, + serviceToken, + properties: { + Secrets: secrets.map(s => ({ secretArn: s.secretArn, secretName: s.secretName })), + }, + }); + } +} + +const app = new cdk.App({ + context: { + '@aws-cdk/aws-secretsmanager:parseOwnedSecretName': 'true', + }, +}); +new SecretsManagerStack(app, 'Integ-SecretsManager-ParsedSecretName'); +app.synth(); diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts index 0e56a72bc3c54..22dae639500dd 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts @@ -1,14 +1,16 @@ import '@aws-cdk/assert/jest'; -import { ResourcePart } from '@aws-cdk/assert'; +import { expect as assertExpect, ResourcePart } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as secretsmanager from '../lib'; +let app: cdk.App; let stack: cdk.Stack; beforeEach(() => { - stack = new cdk.Stack(); + app = new cdk.App(); + stack = new cdk.Stack(app); }); test('default secret', () => { @@ -426,18 +428,106 @@ test('secretValue', () => { }); describe('secretName', () => { - test.each([undefined, 'mySecret'])('when secretName is %s', (secretName) => { - const secret = new secretsmanager.Secret(stack, 'Secret', { - secretName, + describe('without @aws-cdk/aws-secretsmanager:parseOwnedSecretName set', () => { + test.each([undefined, 'mySecret'])('when secretName is %s', (secretName) => { + const secret = new secretsmanager.Secret(stack, 'Secret', { + secretName, + }); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + // Creates secret name by parsing ARN. + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }, + }); }); - new cdk.CfnOutput(stack, 'MySecretName', { - value: secret.secretName, + }); + + describe('with @aws-cdk/aws-secretsmanager:parseOwnedSecretName set', () => { + beforeEach(() => { + app = new cdk.App({ + context: { + '@aws-cdk/aws-secretsmanager:parseOwnedSecretName': 'true', + }, + }); + stack = new cdk.Stack(app); + }); + + test('selects the first two parts of the resource name when the name is auto-generated', () => { + const secret = new secretsmanager.Secret(stack, 'Secret'); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + const resourceName = { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }; + + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { + 'Fn::Join': ['-', [ + { 'Fn::Select': [0, { 'Fn::Split': ['-', resourceName] }] }, + { 'Fn::Select': [1, { 'Fn::Split': ['-', resourceName] }] }, + ]], + }, + }); + }); + + test('is simply the first segment when the provided secret name has no hyphens', () => { + const secret = new secretsmanager.Secret(stack, 'Secret', { secretName: 'mySecret' }); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + const resourceName = { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }; + + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { + 'Fn::Select': [0, { 'Fn::Split': ['-', resourceName] }], + }, + }); + }); + + test.each([ + [2, 'my-secret'], + [3, 'my-secret-hyphenated'], + [4, 'my-secret-with-hyphens'], + ])('selects the %n parts of the resource name when the secret name is provided', (segments, secretName) => { + const secret = new secretsmanager.Secret(stack, 'Secret', { secretName }); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + const resourceName = { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }; + const secretNameSegments = []; + for (let i = 0; i < segments; i++) { + secretNameSegments.push({ 'Fn::Select': [i, { 'Fn::Split': ['-', resourceName] }] }); + } + + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { + 'Fn::Join': ['-', secretNameSegments], + }, + }); }); - // Creates secret name by parsing ARN. - expect(stack).toHaveOutput({ - outputName: 'MySecretName', - outputValue: { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }, + test('uses existing Tokens as secret names as-is', () => { + const secret1 = new secretsmanager.Secret(stack, 'Secret1'); + const secret2 = new secretsmanager.Secret(stack, 'Secret2', { + secretName: secret1.secretName, + }); + new cdk.CfnOutput(stack, 'MySecretName1', { + value: secret1.secretName, + }); + new cdk.CfnOutput(stack, 'MySecretName2', { + value: secret2.secretName, + }); + + const outputs = assertExpect(stack).value.Outputs; + expect(outputs.MySecretName1).toEqual(outputs.MySecretName2); }); }); }); @@ -492,7 +582,6 @@ test('import by secretArn does not strip suffixes unless the suffix length is si test('import by secretArn supports tokens for ARNs', () => { // GIVEN - const app = new cdk.App(); const stackA = new cdk.Stack(app, 'StackA'); const stackB = new cdk.Stack(app, 'StackB'); const secretA = new secretsmanager.Secret(stackA, 'SecretA'); @@ -862,7 +951,6 @@ test('can add to the resource policy of a secret', () => { test('fails if secret policy has no actions', () => { // GIVEN - const app = new cdk.App(); stack = new cdk.Stack(app, 'my-stack'); const secret = new secretsmanager.Secret(stack, 'Secret'); @@ -878,7 +966,6 @@ test('fails if secret policy has no actions', () => { test('fails if secret policy has no IAM principals', () => { // GIVEN - const app = new cdk.App(); stack = new cdk.Stack(app, 'my-stack'); const secret = new secretsmanager.Secret(stack, 'Secret'); diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index 2d95f60f4748e..63b466de84421 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -197,8 +197,9 @@ export class Queue extends QueueBase { */ public static fromQueueAttributes(scope: Construct, id: string, attrs: QueueAttributes): IQueue { const stack = Stack.of(scope); - const queueName = attrs.queueName || stack.parseArn(attrs.queueArn).resource; - const queueUrl = attrs.queueUrl || `https://sqs.${stack.region}.${stack.urlSuffix}/${stack.account}/${queueName}`; + const parsedArn = stack.parseArn(attrs.queueArn); + const queueName = attrs.queueName || parsedArn.resource; + const queueUrl = attrs.queueUrl || `https://sqs.${parsedArn.region}.${stack.urlSuffix}/${parsedArn.account}/${queueName}`; class Import extends QueueBase { public readonly queueArn = attrs.queueArn; // arn:aws:sqs:us-east-1:123456789012:queue1 diff --git a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts index afcc44beadc5b..87e38c2dcf2ca 100644 --- a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts +++ b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts @@ -162,7 +162,7 @@ export = { test.deepEqual(stack.resolve(imports.queueArn), 'arn:aws:sqs:us-east-1:123456789012:queue1'); test.deepEqual(stack.resolve(imports.queueUrl), { 'Fn::Join': - ['', ['https://sqs.', { Ref: 'AWS::Region' }, '.', { Ref: 'AWS::URLSuffix' }, '/', { Ref: 'AWS::AccountId' }, '/queue1']], + ['', ['https://sqs.us-east-1.', { Ref: 'AWS::URLSuffix' }, '/123456789012/queue1']], }); test.deepEqual(stack.resolve(imports.queueName), 'queue1'); test.done(); @@ -176,6 +176,25 @@ export = { test.deepEqual(fifoQueue.fifo, true); test.done(); }, + + 'importing works correctly for cross region queue'(test: Test) { + // GIVEN + const stack = new Stack(undefined, 'Stack', { env: { region: 'us-east-1' } }); + + // WHEN + const imports = sqs.Queue.fromQueueArn(stack, 'Imported', 'arn:aws:sqs:us-west-2:123456789012:queue1'); + + // THEN + + // "import" returns an IQueue bound to `Fn::ImportValue`s. + test.deepEqual(stack.resolve(imports.queueArn), 'arn:aws:sqs:us-west-2:123456789012:queue1'); + test.deepEqual(stack.resolve(imports.queueUrl), { + 'Fn::Join': + ['', ['https://sqs.us-west-2.', { Ref: 'AWS::URLSuffix' }, '/123456789012/queue1']], + }); + test.deepEqual(stack.resolve(imports.queueName), 'queue1'); + test.done(); + }, }, 'grants': { diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index d28a88071f10d..84485ddf3c416 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -56,6 +56,15 @@ export const STACK_RELATIVE_EXPORTS_CONTEXT = '@aws-cdk/core:stackRelativeExport */ export const DOCKER_IGNORE_SUPPORT = '@aws-cdk/aws-ecr-assets:dockerIgnoreSupport'; +/** + * Secret.secretName for an "owned" secret will attempt to parse the secretName from the ARN, + * rather than the default full resource name, which includes the SecretsManager suffix. + * + * If this flag is not set, Secret.secretName will include the SecretsManager suffix, which cannot be directly + * used by SecretsManager.DescribeSecret, and must be parsed by the user first (e.g., Fn:Join, Fn:Select, Fn:Split). + */ +export const SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME = '@aws-cdk/aws-secretsmanager:parseOwnedSecretName'; + /** * This map includes context keys and values for feature flags that enable * capabilities "from the future", which we could not introduce as the default @@ -74,6 +83,7 @@ export const FUTURE_FLAGS = { [ENABLE_DIFF_NO_FAIL_CONTEXT]: 'true', [STACK_RELATIVE_EXPORTS_CONTEXT]: 'true', [DOCKER_IGNORE_SUPPORT]: true, + [SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME]: true, // We will advertise this flag when the feature is complete // [NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: 'true', @@ -89,8 +99,9 @@ const FUTURE_FLAGS_DEFAULTS: { [key: string]: boolean } = { [STACK_RELATIVE_EXPORTS_CONTEXT]: false, [NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: false, [DOCKER_IGNORE_SUPPORT]: false, + [SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME]: false, }; export function futureFlagDefault(flag: string): boolean { return FUTURE_FLAGS_DEFAULTS[flag]; -} \ No newline at end of file +} diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index e66a321fc2f06..cad79354de315 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -39,7 +39,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/core": "0.0.0", - "@types/archiver": "^3.1.1", + "@types/archiver": "^5.1.0", "@types/fs-extra": "^8.1.1", "@types/glob": "^7.1.3", "@types/jest": "^26.0.15", diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json index 24bc67075c70f..d2b4361fb9133 100644 --- a/packages/cdk-assets/package.json +++ b/packages/cdk-assets/package.json @@ -30,7 +30,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/archiver": "^3.1.1", + "@types/archiver": "^5.1.0", "@types/glob": "^7.1.3", "@types/jest": "^26.0.15", "@types/jszip": "^3.4.1", diff --git a/yarn.lock b/yarn.lock index dfe5ea7785597..a47c414f52354 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1493,10 +1493,10 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@types/archiver@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-3.1.1.tgz#10cc1be44af8911e57484342c7b3b32a5f178a1a" - integrity sha512-TzVZ9204sH1TuFylfr1cw/AA/3/VldAAXswEwKLXUOzA9mDg+m6gHF9EaqKNlozcjc6knX5m1KAqJzksPLSEfw== +"@types/archiver@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-5.1.0.tgz#869f4ce4028e49cf9a0243cf914415f4cc3d1f3d" + integrity sha512-baFOhanb/hxmcOd1Uey2TfFg43kTSmM6py1Eo7Rjbv/ivcl7PXLhY0QgXGf50Hx/eskGCFqPfhs/7IZLb15C5g== dependencies: "@types/glob" "*" @@ -2057,6 +2057,11 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +app-root-path@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" + integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== + append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -2294,7 +2299,7 @@ aws-sdk-mock@^5.1.0: sinon "^9.0.1" traverse "^0.6.6" -aws-sdk@^2.637.0, aws-sdk@^2.799.0: +aws-sdk@^2.596.0, aws-sdk@^2.637.0, aws-sdk@^2.799.0: version "2.799.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.799.0.tgz#8b1a64c1a9f8ccf5794eb07bdd8051e4cb6adcfd" integrity sha512-NYAoiNU+bJXhlJsC0rFqrmD5t5ho7/VxldmziP6HLPYHfOCI9Uvk6UVjfPmhLWPm0mHnIxhsHqmsNGyjhHNYmw== @@ -3840,6 +3845,16 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" +dotenv-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dotenv-json/-/dotenv-json-1.0.0.tgz#fc7f672aafea04bed33818733b9f94662332815c" + integrity sha512-jAssr+6r4nKhKRudQ0HOzMskOFFi9+ubXWwmrSGJFgTvpjyPXCXsCsYbjif6mXp7uxA7xY3/LGaiTQukZzSbOQ== + +dotenv@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + dotgitignore@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b" @@ -4025,10 +4040,10 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -esbuild@^0.8.9: - version "0.8.9" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.9.tgz#e2b4254ee7aabb23e88c9d4abb38a16668856569" - integrity sha512-HAV4mKJqos0L8g6pL7evrw/ZPm478yFNtkuYhqJAeTrIW40XtBxhHrt4Pm2faYeRB8K6nA7dTDgmF+O0e9JCXQ== +esbuild@^0.8.17: + version "0.8.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.17.tgz#1c16c6d5988dcfdcf27a7e1612b7fd05e1477c54" + integrity sha512-ReHap+Iyn5BQF0B8F3xrLwu+j57ri5uDUw2ej9XTPAuFDebYiWwRzBY4jhF610bklveXLbCGim/8/2wQKQlu1w== escalade@^3.1.1: version "3.1.1" @@ -4062,6 +4077,11 @@ escodegen@^1.14.1, escodegen@^1.8.1: optionalDependencies: source-map "~0.6.1" +eslint-config-standard@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea" + integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg== + eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -4089,6 +4109,14 @@ eslint-module-utils@^2.6.0: debug "^2.6.9" pkg-dir "^2.0.0" +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + eslint-plugin-import@^2.22.1: version "2.22.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" @@ -4108,11 +4136,33 @@ eslint-plugin-import@^2.22.1: resolve "^1.17.0" tsconfig-paths "^3.9.0" +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + eslint-plugin-rulesdir@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-rulesdir/-/eslint-plugin-rulesdir-0.1.0.tgz#ad144d7e98464fda82963eff3fab331aecb2bf08" integrity sha1-rRRNfphGT9qClj7/P6szGuyyvwg= +eslint-plugin-standard@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz#0c3bf3a67e853f8bbbc580fb4945fbf16f41b7c5" + integrity sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ== + eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -5212,7 +5262,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4, ignore@^5.1.8: +ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -5405,6 +5455,13 @@ is-core-module@^2.0.0: dependencies: has "^1.0.3" +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -6488,6 +6545,24 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +lambda-leak@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lambda-leak/-/lambda-leak-2.0.0.tgz#771985d3628487f6e885afae2b54510dcfb2cd7e" + integrity sha1-dxmF02KEh/boha+uK1RRDc+yzX4= + +lambda-tester@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/lambda-tester/-/lambda-tester-3.6.0.tgz#ceb7d4f4f0da768487a05cff37dcd088508b5247" + integrity sha512-F2ZTGWCLyIR95o/jWK46V/WnOCFAEUG/m/V7/CLhPJ7PCM+pror1rZ6ujP3TkItSGxUfpJi0kqwidw+M/nEqWw== + dependencies: + app-root-path "^2.2.1" + dotenv "^8.0.0" + dotenv-json "^1.0.0" + lambda-leak "^2.0.0" + semver "^6.1.1" + uuid "^3.3.2" + vandium-utils "^1.1.1" + lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -8613,6 +8688,14 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.13.1, resolve@^1.17 dependencies: path-parse "^1.0.6" +resolve@^1.10.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + resolve@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" @@ -8770,7 +8853,7 @@ semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -10136,6 +10219,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +vandium-utils@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vandium-utils/-/vandium-utils-1.2.0.tgz#44735de4b7641a05de59ebe945f174e582db4f59" + integrity sha1-RHNd5LdkGgXeWevpRfF05YLbT1k= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"