package aws import ( "strings" "testing" ) func TestValidateEcrRepositoryName(t *testing.T) { validNames := []string{ "nginx-web-app", "project-a/nginx-web-app", "domain.ltd/nginx-web-app", "3chosome-thing.com/01different-pattern", "0123456789/999999999", "double/forward/slash", "000000000000000", } for _, v := range validNames { _, errors := validateEcrRepositoryName(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid ECR repository name: %q", v, errors) } } invalidNames := []string{ // length > 256 "3cho_some-thing.com/01different.-_pattern01different.-_pattern01diff" + "erent.-_pattern01different.-_pattern01different.-_pattern01different" + ".-_pattern01different.-_pattern01different.-_pattern01different.-_pa" + "ttern01different.-_pattern01different.-_pattern234567", // length < 2 "i", "special@character", "different+special=character", "double//slash", "double..dot", "/slash-at-the-beginning", "slash-at-the-end/", } for _, v := range invalidNames { _, errors := validateEcrRepositoryName(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid ECR repository name", v) } } } func TestValidateCloudWatchEventRuleName(t *testing.T) { validNames := []string{ "HelloWorl_d", "hello-world", "hello.World0125", } for _, v := range validNames { _, errors := validateCloudWatchEventRuleName(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid CW event rule name: %q", v, errors) } } invalidNames := []string{ "special@character", "slash/in-the-middle", // Length > 64 "TooLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName", } for _, v := range invalidNames { _, errors := validateCloudWatchEventRuleName(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid CW event rule name", v) } } } func TestValidateLambdaFunctionName(t *testing.T) { validNames := []string{ "arn:aws:lambda:us-west-2:123456789012:function:ThumbNail", "FunctionName", "function-name", } for _, v := range validNames { _, errors := validateLambdaFunctionName(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid Lambda function name: %q", v, errors) } } invalidNames := []string{ "/FunctionNameWithSlash", "function.name.with.dots", // length > 140 "arn:aws:lambda:us-west-2:123456789012:function:TooLoooooo" + "ooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + "ooooooooooooooooongFunctionName", } for _, v := range invalidNames { _, errors := validateLambdaFunctionName(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid Lambda function name", v) } } } func TestValidateLambdaQualifier(t *testing.T) { validNames := []string{ "123", "prod", "PROD", "MyTestEnv", "$LATEST", } for _, v := range validNames { _, errors := validateLambdaQualifier(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid Lambda function qualifier: %q", v, errors) } } invalidNames := []string{ // No ARNs allowed "arn:aws:lambda:us-west-2:123456789012:function:prod", // length > 128 "TooLooooooooooooooooooooooooooooooooooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooooooooooooo" + "oooooooooooongQualifier", } for _, v := range invalidNames { _, errors := validateLambdaQualifier(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid Lambda function qualifier", v) } } } func TestValidateLambdaPermissionAction(t *testing.T) { validNames := []string{ "lambda:*", "lambda:InvokeFunction", "*", } for _, v := range validNames { _, errors := validateLambdaPermissionAction(v, "action") if len(errors) != 0 { t.Fatalf("%q should be a valid Lambda permission action: %q", v, errors) } } invalidNames := []string{ "yada", "lambda:123", "*:*", "lambda:Invoke*", } for _, v := range invalidNames { _, errors := validateLambdaPermissionAction(v, "action") if len(errors) == 0 { t.Fatalf("%q should be an invalid Lambda permission action", v) } } } func TestValidateAwsAccountId(t *testing.T) { validNames := []string{ "123456789012", "999999999999", } for _, v := range validNames { _, errors := validateAwsAccountId(v, "account_id") if len(errors) != 0 { t.Fatalf("%q should be a valid AWS Account ID: %q", v, errors) } } invalidNames := []string{ "12345678901", // too short "1234567890123", // too long "invalid", "x123456789012", } for _, v := range invalidNames { _, errors := validateAwsAccountId(v, "account_id") if len(errors) == 0 { t.Fatalf("%q should be an invalid AWS Account ID", v) } } } func TestValidateArn(t *testing.T) { validNames := []string{ "arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/My App/MyEnvironment", // Beanstalk "arn:aws:iam::123456789012:user/David", // IAM User "arn:aws:rds:eu-west-1:123456789012:db:mysql-db", // RDS "arn:aws:s3:::my_corporate_bucket/exampleobject.png", // S3 object "arn:aws:events:us-east-1:319201112229:rule/rule_name", // CloudWatch Rule "arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction", // Lambda function "arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction:Qualifier", // Lambda func qualifier } for _, v := range validNames { _, errors := validateArn(v, "arn") if len(errors) != 0 { t.Fatalf("%q should be a valid ARN: %q", v, errors) } } invalidNames := []string{ "arn", "123456789012", "arn:aws", "arn:aws:logs", "arn:aws:logs:region:*:*", } for _, v := range invalidNames { _, errors := validateArn(v, "arn") if len(errors) == 0 { t.Fatalf("%q should be an invalid ARN", v) } } } func TestValidatePolicyStatementId(t *testing.T) { validNames := []string{ "YadaHereAndThere", "Valid-5tatement_Id", "1234", } for _, v := range validNames { _, errors := validatePolicyStatementId(v, "statement_id") if len(errors) != 0 { t.Fatalf("%q should be a valid Statement ID: %q", v, errors) } } invalidNames := []string{ "Invalid/StatementId/with/slashes", "InvalidStatementId.with.dots", // length > 100 "TooooLoooooooooooooooooooooooooooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooStatementId", } for _, v := range invalidNames { _, errors := validatePolicyStatementId(v, "statement_id") if len(errors) == 0 { t.Fatalf("%q should be an invalid Statement ID", v) } } } func TestValidateCIDRNetworkAddress(t *testing.T) { cases := []struct { CIDR string ExpectedErrSubstr string }{ {"notacidr", `must contain a valid CIDR`}, {"10.0.1.0/16", `must contain a valid network CIDR`}, {"10.0.1.0/24", ``}, } for i, tc := range cases { _, errs := validateCIDRNetworkAddress(tc.CIDR, "foo") if tc.ExpectedErrSubstr == "" { if len(errs) != 0 { t.Fatalf("%d/%d: Expected no error, got errs: %#v", i+1, len(cases), errs) } } else { if len(errs) != 1 { t.Fatalf("%d/%d: Expected 1 err containing %q, got %d errs", i+1, len(cases), tc.ExpectedErrSubstr, len(errs)) } if !strings.Contains(errs[0].Error(), tc.ExpectedErrSubstr) { t.Fatalf("%d/%d: Expected err: %q, to include %q", i+1, len(cases), errs[0], tc.ExpectedErrSubstr) } } } } func TestValidateHTTPMethod(t *testing.T) { validCases := []string{"GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH"} for i, method := range validCases { _, errs := validateHTTPMethod(method, "foo") if len(errs) != 0 { t.Fatalf("%d/%d: Expected no error, got errs: %#v", i+1, len(validCases), errs) } } } func TestValidateLogMetricFilterName(t *testing.T) { validNames := []string{ "YadaHereAndThere", "Valid-5Metric_Name", "This . is also %% valid@!)+(", "1234", strings.Repeat("W", 512), } for _, v := range validNames { _, errors := validateLogMetricFilterName(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid Log Metric Filter Name: %q", v, errors) } } invalidNames := []string{ "Here is a name with: colon", "and here is another * invalid name", "*", // length > 512 strings.Repeat("W", 513), } for _, v := range invalidNames { _, errors := validateLogMetricFilterName(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid Log Metric Filter Name", v) } } } func TestValidateLogMetricTransformationName(t *testing.T) { validNames := []string{ "YadaHereAndThere", "Valid-5Metric_Name", "This . is also %% valid@!)+(", "1234", "", strings.Repeat("W", 255), } for _, v := range validNames { _, errors := validateLogMetricFilterTransformationName(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid Log Metric Filter Transformation Name: %q", v, errors) } } invalidNames := []string{ "Here is a name with: colon", "and here is another * invalid name", "also $ invalid", "*", // length > 255 strings.Repeat("W", 256), } for _, v := range invalidNames { _, errors := validateLogMetricFilterTransformationName(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid Log Metric Filter Transformation Name", v) } } } func TestValidateLogGroupName(t *testing.T) { validNames := []string{ "ValidLogGroupName", "ValidLogGroup.Name", "valid/Log-group", "1234", "YadaValid#0123", "Also_valid-name", strings.Repeat("W", 512), } for _, v := range validNames { _, errors := validateLogGroupName(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid Log Metric Filter Transformation Name: %q", v, errors) } } invalidNames := []string{ "Here is a name with: colon", "and here is another * invalid name", "also $ invalid", "This . is also %% invalid@!)+(", "*", "", // length > 512 strings.Repeat("W", 513), } for _, v := range invalidNames { _, errors := validateLogGroupName(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid Log Metric Filter Transformation Name", v) } } } func TestValidateS3BucketLifecycleTimestamp(t *testing.T) { validDates := []string{ "2016-01-01", "2006-01-02", } for _, v := range validDates { _, errors := validateS3BucketLifecycleTimestamp(v, "date") if len(errors) != 0 { t.Fatalf("%q should be valid date: %q", v, errors) } } invalidDates := []string{ "Jan 01 2016", "20160101", } for _, v := range invalidDates { _, errors := validateS3BucketLifecycleTimestamp(v, "date") if len(errors) == 0 { t.Fatalf("%q should be invalid date", v) } } } func TestValidateS3BucketLifecycleStorageClass(t *testing.T) { validStorageClass := []string{ "STANDARD_IA", "GLACIER", } for _, v := range validStorageClass { _, errors := validateS3BucketLifecycleStorageClass(v, "storage_class") if len(errors) != 0 { t.Fatalf("%q should be valid storage class: %q", v, errors) } } invalidStorageClass := []string{ "STANDARD", "1234", } for _, v := range invalidStorageClass { _, errors := validateS3BucketLifecycleStorageClass(v, "storage_class") if len(errors) == 0 { t.Fatalf("%q should be invalid storage class", v) } } } func TestValidateS3BucketLifecycleRuleId(t *testing.T) { validId := []string{ "YadaHereAndThere", "Valid-5Rule_ID", "This . is also %% valid@!)+*(:ID", "1234", strings.Repeat("W", 255), } for _, v := range validId { _, errors := validateS3BucketLifecycleRuleId(v, "id") if len(errors) != 0 { t.Fatalf("%q should be a valid lifecycle rule id: %q", v, errors) } } invalidId := []string{ // length > 255 strings.Repeat("W", 256), } for _, v := range invalidId { _, errors := validateS3BucketLifecycleRuleId(v, "id") if len(errors) == 0 { t.Fatalf("%q should be an invalid lifecycle rule id", v) } } } func TestValidateIntegerInRange(t *testing.T) { validIntegers := []int{-259, 0, 1, 5, 999} min := -259 max := 999 for _, v := range validIntegers { _, errors := validateIntegerInRange(min, max)(v, "name") if len(errors) != 0 { t.Fatalf("%q should be an integer in range (%d, %d): %q", v, min, max, errors) } } invalidIntegers := []int{-260, -99999, 1000, 25678} for _, v := range invalidIntegers { _, errors := validateIntegerInRange(min, max)(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an integer outside range (%d, %d)", v, min, max) } } } func TestResourceAWSElastiCacheClusterIdValidation(t *testing.T) { cases := []struct { Value string ErrCount int }{ { Value: "tEsting", ErrCount: 1, }, { Value: "t.sting", ErrCount: 1, }, { Value: "t--sting", ErrCount: 1, }, { Value: "1testing", ErrCount: 1, }, { Value: "testing-", ErrCount: 1, }, { Value: randomString(65), ErrCount: 1, }, } for _, tc := range cases { _, errors := validateElastiCacheClusterId(tc.Value, "aws_elasticache_cluster_cluster_id") if len(errors) != tc.ErrCount { t.Fatalf("Expected the ElastiCache Cluster cluster_id to trigger a validation error") } } } func TestValidateDbEventSubscriptionName(t *testing.T) { validNames := []string{ "valid-name", "valid02-name", "Valid-Name1", } for _, v := range validNames { _, errors := validateDbEventSubscriptionName(v, "name") if len(errors) != 0 { t.Fatalf("%q should be a valid RDS Event Subscription Name: %q", v, errors) } } invalidNames := []string{ "Here is a name with: colon", "and here is another * invalid name", "also $ invalid", "This . is also %% invalid@!)+(", "*", "", " ", "_", // length > 255 strings.Repeat("W", 256), } for _, v := range invalidNames { _, errors := validateDbEventSubscriptionName(v, "name") if len(errors) == 0 { t.Fatalf("%q should be an invalid RDS Event Subscription Name", v) } } } func TestValidateJsonString(t *testing.T) { type testCases struct { Value string ErrCount int } invalidCases := []testCases{ { Value: `{0:"1"}`, ErrCount: 1, }, { Value: `{'abc':1}`, ErrCount: 1, }, { Value: `{"def":}`, ErrCount: 1, }, { Value: `{"xyz":[}}`, ErrCount: 1, }, } for _, tc := range invalidCases { _, errors := validateJsonString(tc.Value, "json") if len(errors) != tc.ErrCount { t.Fatalf("Expected %q to trigger a validation error.", tc.Value) } } validCases := []testCases{ { Value: ``, ErrCount: 0, }, { Value: `{}`, ErrCount: 0, }, { Value: `{"abc":["1","2"]}`, ErrCount: 0, }, } for _, tc := range validCases { _, errors := validateJsonString(tc.Value, "json") if len(errors) != tc.ErrCount { t.Fatalf("Expected %q not to trigger a validation error.", tc.Value) } } }