fix: reject PermissionsBoundary on CreateRole and CreateUser (#944)

This commit is contained in:
Julian Sommer
2026-08-31 09:51:29 +10:00
committed by GitHub
parent e90dfa4ea3
commit 82ce31626b
4 changed files with 101 additions and 0 deletions
+4
View File
@@ -44,6 +44,10 @@ func (s *IAMServiceImpl) CreateRole(accountID string, input *iam.CreateRoleInput
return nil, errors.New(awserrors.ErrorIAMInvalidInput)
}
if err := validatePermissionsBoundary(input.PermissionsBoundary); err != nil {
return nil, err
}
path := "/"
if input.Path != nil {
path = *input.Path
+42
View File
@@ -108,6 +108,48 @@ func TestCreateRole_InvalidPath(t *testing.T) {
assert.Contains(t, err.Error(), awserrors.ErrorIAMInvalidInput)
}
func TestCreateRole_PermissionsBoundarySet(t *testing.T) {
svc := setupTestIAMService(t)
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
RoleName: aws.String("bounded-role"),
AssumeRolePolicyDocument: aws.String(validTrustPolicy()),
PermissionsBoundary: aws.String("arn:aws:iam::" + testAccountID + ":policy/deny-all"),
})
require.Error(t, err)
assert.Contains(t, err.Error(), awserrors.ErrorValidationError)
assert.Contains(t, err.Error(), "PermissionsBoundary")
_, err = svc.GetRole(testAccountID, &iam.GetRoleInput{RoleName: aws.String("bounded-role")})
require.Error(t, err)
assert.Contains(t, err.Error(), awserrors.ErrorIAMNoSuchEntity)
}
func TestCreateRole_PermissionsBoundaryAbsent(t *testing.T) {
svc := setupTestIAMService(t)
tests := []struct {
name string
roleName string
boundary *string
}{
{name: "nil", roleName: "nil-boundary", boundary: nil},
{name: "empty string", roleName: "empty-boundary", boundary: aws.String("")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
RoleName: aws.String(tt.roleName),
AssumeRolePolicyDocument: aws.String(validTrustPolicy()),
PermissionsBoundary: tt.boundary,
})
require.NoError(t, err)
assert.Equal(t, tt.roleName, *out.Role.RoleName)
})
}
}
func TestCreateRole_MalformedTrustPolicy_InvalidJSON(t *testing.T) {
svc := setupTestIAMService(t)
+15
View File
@@ -323,6 +323,10 @@ func (s *IAMServiceImpl) CreateUser(accountID string, input *iam.CreateUserInput
return nil, errors.New(awserrors.ErrorIAMInvalidInput)
}
if err := validatePermissionsBoundary(input.PermissionsBoundary); err != nil {
return nil, err
}
path := "/"
if input.Path != nil {
path = *input.Path
@@ -1984,6 +1988,17 @@ func validatePolicyName(name string) error {
return nil
}
// validatePermissionsBoundary rejects a boundary the evaluator cannot enforce.
// Storing an identity with an unenforced boundary would widen access silently,
// so the create fails visibly instead.
func validatePermissionsBoundary(boundary *string) error {
if aws.StringValue(boundary) == "" {
return nil
}
return awserrors.Errorf(awserrors.ErrorValidationError,
"PermissionsBoundary is not supported in this release; omit it and scope the identity with its attached policies instead")
}
func validatePath(path string) error {
if !strings.HasPrefix(path, "/") || !strings.HasSuffix(path, "/") {
return fmt.Errorf("path must begin and end with /")
@@ -91,6 +91,46 @@ func TestCreateUser_Duplicate(t *testing.T) {
assert.Contains(t, err.Error(), awserrors.ErrorIAMEntityAlreadyExists)
}
func TestCreateUser_PermissionsBoundarySet(t *testing.T) {
svc := setupTestIAMService(t)
_, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
UserName: aws.String("boundeduser"),
PermissionsBoundary: aws.String("arn:aws:iam::" + testAccountID + ":policy/deny-all"),
})
require.Error(t, err)
assert.Contains(t, err.Error(), awserrors.ErrorValidationError)
assert.Contains(t, err.Error(), "PermissionsBoundary")
_, err = svc.GetUser(testAccountID, &iam.GetUserInput{UserName: aws.String("boundeduser")})
require.Error(t, err)
assert.Contains(t, err.Error(), awserrors.ErrorIAMNoSuchEntity)
}
func TestCreateUser_PermissionsBoundaryAbsent(t *testing.T) {
svc := setupTestIAMService(t)
tests := []struct {
name string
userName string
boundary *string
}{
{name: "nil", userName: "nilboundary", boundary: nil},
{name: "empty string", userName: "emptyboundary", boundary: aws.String("")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
UserName: aws.String(tt.userName),
PermissionsBoundary: tt.boundary,
})
require.NoError(t, err)
assert.Equal(t, tt.userName, *out.User.UserName)
})
}
}
func TestGetUser(t *testing.T) {
svc := setupTestIAMService(t)
createTestUser(t, svc, "getuser")