Skip to content

Commit

Permalink
feat(iam): validate roleName (#28509)
Browse files Browse the repository at this point in the history
> Validates roleName


Closes #28502 

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
frattallone committed Jan 2, 2024
1 parent 1f9788f commit 999c01a
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/aws-cdk-lib/aws-iam/lib/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@ export class Role extends Resource implements IRole {
physicalName: props.roleName,
});

if (props.roleName && !Token.isUnresolved(props.roleName) && !/^[\w+=,.@-]{1,64}$/.test(props.roleName)) {
throw new Error('Invalid roleName. The name must be a string of characters consisting of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: _+=,.@-. Length must be between 1 and 64 characters.');
}

const externalIds = props.externalIds || [];
if (props.externalId) {
externalIds.push(props.externalId);
Expand Down
83 changes: 82 additions & 1 deletion packages/aws-cdk-lib/aws-iam/test/role.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { testDeprecated } from '@aws-cdk/cdk-build-tools';
import { Construct } from 'constructs';
import { Template, Match, Annotations } from '../../assertions';
import { Duration, Stack, App, CfnResource, RemovalPolicy, Lazy, Stage, DefaultStackSynthesizer, CliCredentialsStackSynthesizer, PERMISSIONS_BOUNDARY_CONTEXT_KEY, PermissionsBoundary } from '../../core';
import { Duration, Stack, App, CfnResource, RemovalPolicy, Lazy, Stage, DefaultStackSynthesizer, CliCredentialsStackSynthesizer, PERMISSIONS_BOUNDARY_CONTEXT_KEY, PermissionsBoundary, Token } from '../../core';
import { AnyPrincipal, ArnPrincipal, CompositePrincipal, FederatedPrincipal, ManagedPolicy, PolicyStatement, Role, ServicePrincipal, User, Policy, PolicyDocument, Effect } from '../lib';

describe('isRole() returns', () => {
Expand Down Expand Up @@ -1325,3 +1325,84 @@ test('cross-env role ARNs include path', () => {
},
});
});

test('doesn\'t throw with roleName of 64 chars', () => {
const app = new App();
const stack = new Stack(app, 'MyStack');
const valdName = 'a'.repeat(64);

expect(() => {
new Role(stack, 'Test', {
assumedBy: new ServicePrincipal('sns.amazonaws.com'),
roleName: valdName,
});
}).not.toThrow('Invalid roleName');
});

test('throws with roleName over 64 chars', () => {
const app = new App();
const stack = new Stack(app, 'MyStack');
const longName = 'a'.repeat(65);

expect(() => {
new Role(stack, 'Test', {
assumedBy: new ServicePrincipal('sns.amazonaws.com'),
roleName: longName,
});
}).toThrow('Invalid roleName');
});

describe('roleName validation', () => {
const app = new App();
const stack = new Stack(app, 'MyStack');
const invalidChars = '!#$%^&*()';

it('rejects names with spaces', () => {
expect(() => {
new Role(stack, 'test spaces', {
assumedBy: new ServicePrincipal('sns.amazonaws.com'),
roleName: 'invalid name',
});
}).toThrow('Invalid roleName');
});

invalidChars.split('').forEach(char => {
it(`rejects name with ${char}`, () => {
expect(() => {
new Role(stack, `test ${char}`, {
assumedBy: new ServicePrincipal('sns.amazonaws.com'),
roleName: `invalid${char}`,
});
}).toThrow('Invalid roleName');
});
});

});

test('roleName validation with Tokens', () =>{
const app = new App();
const stack = new Stack(app, 'MyStack');
const token = Lazy.string({ produce: () => 'token' });

// Mock isUnresolved to return false
jest.spyOn(Token, 'isUnresolved').mockReturnValue(false);

expect(() => {
new Role(stack, 'Valid', {
assumedBy: new ServicePrincipal('sns.amazonaws.com'),
roleName: token,
});
}).toThrow('Invalid roleName');

// Mock isUnresolved to return true
jest.spyOn(Token, 'isUnresolved').mockReturnValue(true);

expect(() => {
new Role(stack, 'Invalid', {
assumedBy: new ServicePrincipal('sns.amazonaws.com'),
roleName: token,
});
}).not.toThrow('Invalid roleName');

jest.clearAllMocks();
});

0 comments on commit 999c01a

Please sign in to comment.