diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.assets.json index 73491dc95a7c1..c22fb38008e94 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.assets.json @@ -1,7 +1,7 @@ { - "version": "36.0.0", + "version": "38.0.1", "files": { - "8f6d546b5966575d2c8a9ded4222a8ccfd78518d5f171cc1bb28e98248d31c24": { + "f8765f468615ed28f8cec4da577433a7dc83ebe20a869215d33ec1118aea4da1": { "source": { "path": "aws-cdk-s3-legacy-name-integ.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "8f6d546b5966575d2c8a9ded4222a8ccfd78518d5f171cc1bb28e98248d31c24.json", + "objectKey": "f8765f468615ed28f8cec4da577433a7dc83ebe20a869215d33ec1118aea4da1.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.template.json index 4ec4d77afcb46..72e8d92338534 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/aws-cdk-s3-legacy-name-integ.template.json @@ -26,7 +26,7 @@ "Action": "s3:*", "Effect": "Allow", "Resource": [ - "arn:aws:s3:::my_legacy_bucket2/*", + "arn:aws:s3:::My_legacy_bucket2/*", { "Fn::Join": [ "", @@ -35,7 +35,7 @@ { "Ref": "AWS::Partition" }, - ":s3:::my_legacy_bucket1/*" + ":s3:::My_legacy_bucket1/*" ] ] }, @@ -47,7 +47,7 @@ { "Ref": "AWS::Partition" }, - ":s3:::my_legacy_bucket3/*" + ":s3:::My_legacy_bucket3/*" ] ] } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/awscdks3integtestDefaultTestDeployAssert9DECDFBF.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/awscdks3integtestDefaultTestDeployAssert9DECDFBF.assets.json index 6be83bd7547da..9846a67205894 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/awscdks3integtestDefaultTestDeployAssert9DECDFBF.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/awscdks3integtestDefaultTestDeployAssert9DECDFBF.assets.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "38.0.1", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/cdk.out index 1f0068d32659a..c6e612584e352 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"36.0.0"} \ No newline at end of file +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/integ.json index efae270bc704c..4e629f35673f9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "38.0.1", "testCases": { "aws-cdk-s3-integ-test/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/manifest.json index 61de53d375dd7..f7f57f466bf14 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "38.0.1", "artifacts": { "aws-cdk-s3-legacy-name-integ.assets": { "type": "cdk:asset-manifest", @@ -16,9 +16,10 @@ "templateFile": "aws-cdk-s3-legacy-name-integ.template.json", "terminationProtection": false, "validateOnSynth": false, + "notificationArns": [], "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/8f6d546b5966575d2c8a9ded4222a8ccfd78518d5f171cc1bb28e98248d31c24.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/f8765f468615ed28f8cec4da577433a7dc83ebe20a869215d33ec1118aea4da1.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -76,6 +77,7 @@ "templateFile": "awscdks3integtestDefaultTestDeployAssert9DECDFBF.template.json", "terminationProtection": false, "validateOnSynth": false, + "notificationArns": [], "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/tree.json index 43458f80f9d51..8ff8931449606 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.js.snapshot/tree.json @@ -85,7 +85,7 @@ "Action": "s3:*", "Effect": "Allow", "Resource": [ - "arn:aws:s3:::my_legacy_bucket2/*", + "arn:aws:s3:::My_legacy_bucket2/*", { "Fn::Join": [ "", @@ -94,7 +94,7 @@ { "Ref": "AWS::Partition" }, - ":s3:::my_legacy_bucket1/*" + ":s3:::My_legacy_bucket1/*" ] ] }, @@ -106,7 +106,7 @@ { "Ref": "AWS::Partition" }, - ":s3:::my_legacy_bucket3/*" + ":s3:::My_legacy_bucket3/*" ] ] } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.ts index 1bc73452b610d..edf7e7cc6c2c2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-legacy-naming.ts @@ -7,12 +7,12 @@ import * as iam from 'aws-cdk-lib/aws-iam'; const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-s3-legacy-name-integ'); -const legacyBucketFromName = s3.Bucket.fromBucketName(stack, 'LegacyBucketFromName', 'my_legacy_bucket1'); +const legacyBucketFromName = s3.Bucket.fromBucketName(stack, 'LegacyBucketFromName', 'My_legacy_bucket1'); -const legacyBucketFromArn = s3.Bucket.fromBucketArn(stack, 'LegacyBucketFromArn', 'arn:aws:s3:::my_legacy_bucket2'); +const legacyBucketFromArn = s3.Bucket.fromBucketArn(stack, 'LegacyBucketFromArn', 'arn:aws:s3:::My_legacy_bucket2'); const legacyBucketFromAttributes = s3.Bucket.fromBucketAttributes(stack, 'LegacyBucketFromAttributes', { - bucketName: 'my_legacy_bucket3', + bucketName: 'My_legacy_bucket3', }); const role = new iam.Role(stack, 'LegacyBucketRole', { @@ -37,4 +37,3 @@ new IntegTest(app, 'aws-cdk-s3-integ-test', { // In the synthesized template, verify: // 1. The bucket names imported using different methods (Bucket.fromBucketName, Bucket.fromBucketArn, Bucket.fromBucketAttributes) are not modified and contain underscores. // 2. The policy attached to the role includes the correct bucket ARNs with underscores for all three imported buckets. - diff --git a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts index 6ca50b3fbc4fe..482ed2b84d767 100644 --- a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts +++ b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts @@ -1897,24 +1897,37 @@ export class Bucket extends BucketBase { if (bucketName.length < 3 || bucketName.length > 63) { errors.push('Bucket name must be at least 3 and no more than 63 characters'); } - const charsetRegex = allowLegacyBucketNaming ? /[^a-z0-9._-]/ : /[^a-z0-9.-]/; - const charsetMatch = bucketName.match(charsetRegex); - if (charsetMatch) { - errors.push(`Bucket name must only contain lowercase characters and the symbols, period (.)${allowLegacyBucketNaming ? ', underscore (_), ' : ' '}and dash (-) ` - + `(offset: ${charsetMatch.index})`); + + const illegalCharsetRegEx = allowLegacyBucketNaming ? /[^A-Za-z0-9._-]/ : /[^a-z0-9.-]/; + const allowedEdgeCharsetRegEx = allowLegacyBucketNaming ? /[A-Za-z0-9]/ : /[a-z0-9]/; + + const illegalCharMatch = bucketName.match(illegalCharsetRegEx); + if (illegalCharMatch) { + errors.push(allowLegacyBucketNaming + ? 'Bucket name must only contain uppercase or lowercase characters and the symbols, period (.), underscore (_), and dash (-)' + : 'Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-)' + + ` (offset: ${illegalCharMatch.index})`, + ); } - if (!/[a-z0-9]/.test(bucketName.charAt(0))) { - errors.push('Bucket name must start and end with a lowercase character or number ' - + '(offset: 0)'); + if (!allowedEdgeCharsetRegEx.test(bucketName.charAt(0))) { + errors.push(allowLegacyBucketNaming + ? 'Bucket name must start with an uppercase, lowercase character or number' + : 'Bucket name must start with a lowercase character or number' + + ' (offset: 0)', + ); } - if (!/[a-z0-9]/.test(bucketName.charAt(bucketName.length - 1))) { - errors.push('Bucket name must start and end with a lowercase character or number ' - + `(offset: ${bucketName.length - 1})`); + if (!allowedEdgeCharsetRegEx.test(bucketName.charAt(bucketName.length - 1))) { + errors.push(allowLegacyBucketNaming + ? 'Bucket name must end with an uppercase, lowercase character or number' + : 'Bucket name must end with a lowercase character or number' + + ` (offset: ${bucketName.length - 1})`, + ); } + const consecSymbolMatch = bucketName.match(/\.-|-\.|\.\./); if (consecSymbolMatch) { - errors.push('Bucket name must not have dash next to period, or period next to dash, or consecutive periods ' - + `(offset: ${consecSymbolMatch.index})`); + errors.push('Bucket name must not have dash next to period, or period next to dash, or consecutive periods' + + ` (offset: ${consecSymbolMatch.index})`); } if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(bucketName)) { errors.push('Bucket name must not resemble an IP address'); diff --git a/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts b/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts index 0ce54d5a999e4..21289402d51b1 100644 --- a/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts +++ b/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts @@ -193,8 +193,8 @@ describe('bucket', () => { `Invalid S3 bucket name (value: ${bucket})`, 'Bucket name must be at least 3 and no more than 63 characters', 'Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 5)', - 'Bucket name must start and end with a lowercase character or number (offset: 0)', - `Bucket name must start and end with a lowercase character or number (offset: ${bucket.length - 1})`, + 'Bucket name must start with a lowercase character or number (offset: 0)', + `Bucket name must end with a lowercase character or number (offset: ${bucket.length - 1})`, 'Bucket name must not have dash next to period, or period next to dash, or consecutive periods (offset: 7)', ].join(EOL); @@ -215,12 +215,30 @@ describe('bucket', () => { }).toThrow(/Bucket name must only contain lowercase characters and the symbols, period \(\.\) and dash \(-\)/); }); + test('validateBucketName allows uppercase characters when allowLegacyBucketNaming=true', () => { + expect(() => { + s3.Bucket.validateBucketName('Test_Bucket_Name', true); + }).not.toThrow(); + }); + + test('validateBucketName does not allow uppercase characters when allowLegacyBucketNaming=false', () => { + expect(() => { + s3.Bucket.validateBucketName('Test_Bucket_Name', false); + }).toThrow(/Bucket name must only contain lowercase characters and the symbols, period \(\.\) and dash \(-\)/); + }); + test('validateBucketName does not allow underscore by default', () => { expect(() => { s3.Bucket.validateBucketName('test_bucket_name'); }).toThrow(/Bucket name must only contain lowercase characters and the symbols, period \(\.\) and dash \(-\)/); }); + test('validateBucketName does not allow uppercase characters by default', () => { + expect(() => { + s3.Bucket.validateBucketName('TestBucketName'); + }).toThrow(/Bucket name must only contain lowercase characters and the symbols, period \(\.\) and dash \(-\)/); + }); + test('fails if bucket name has less than 3 or more than 63 characters', () => { const stack = new cdk.Stack(); @@ -3877,4 +3895,3 @@ describe('bucket', () => { }); }); }); -