Repository: aws-cloudformation/cfn-lint

Issue:
Feature Request: `AWS::StepFunctions::StateMachine` - Recognize if `wait` state for `Seconds` is above the limit
### Is this feature request related to a new rule or cfn-lint capabilities?

New capability

### Describe the feature you'd like to request

Currently cfn-lint is unable to recognize if the "wait" state has a higher value for `Seconds` than allowed. The following CloudFormation Template will cause a CloudFormation error:

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Test

Resources:
  StepFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: states.amazonaws.com
            Action: sts:AssumeRole

  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt StepFunctionRole.Arn
      Definition:
        StartAt: Wait
        States:
          Wait:
            Type: Wait
            Seconds: 10000000000000000
            Next: Success
          Success:
            Type: Succeed
```

CloudFormation error:

```
Resource handler returned message:
"Invalid State Machine Definition: 'SCHEMA_VALIDATION_FAILED: Maximum value is 99999999 at /States/Wait/Seconds'
(Service: Sfn, Status Code: 400, Request ID: f...1)
(SDK Attempt Count: 1)"
(RequestToken: 9...9, HandlerErrorCode: InvalidRequest)
```

It would be great if cfn-lint could give an error on this.

### Describe the solution you'd like

A new/updated rule to notify the user of this error.

### Additional context

_No response_

### Is this something that you'd be interested in working on?

- [ ] 👋 I may be able to implement this feature request

### Would this feature include a breaking change?

- [ ] ⚠️ This feature might incur a breaking change
Feature Request: `AWS::StepFunctions::StateMachine` - Recognize if `Next` is missing in `Choices`
### Is this feature request related to a new rule or cfn-lint capabilities?

New capability

### Describe the feature you'd like to request

Currently cfn-lint is unable to recognize if `Next` is missing in `Choices`. The following CloudFormation Template will cause a CloudFormation error:

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Test

Resources:
  StepFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: states.amazonaws.com
            Action: sts:AssumeRole

  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt StepFunctionRole.Arn
      Definition:
        StartAt: Pass
        States:
          Pass:
            Type: Pass
            Next: Choice
          Choice:
            Type: Choice
            Choices:
              - Variable: "$"
                StringMatches: "Test"
                # Next: Pass # <------------------ This is needed
            Default: Success
          Success:
            Type: Succeed
```

CloudFormation error:

```
Resource handler returned message:
"Invalid State Machine Definition: 'SCHEMA_VALIDATION_FAILED: These fields are required: [Next] at /States/Choice/Choices[0],
MISSING_TRANSITION_TARGET: Missing transition pointer at /States/Choice/Choices[0]/Next'
(Service: Sfn, Status Code: 400, Request ID: f...d)
(SDK Attempt Count: 1)"
(RequestToken: 7...9, HandlerErrorCode: InvalidRequest)
```

It would be great if cfn-lint could give an error on these.

### Describe the solution you'd like

A new/updated rule to notify the user of this error.

### Additional context

_No response_

### Is this something that you'd be interested in working on?

- [ ] 👋 I may be able to implement this feature request

### Would this feature include a breaking change?

- [ ] ⚠️ This feature might incur a breaking change
Feature Request: `AWS::StepFunctions::StateMachine` - Correctly recognize the max character limit of a state name
### Is this feature request related to a new rule or cfn-lint capabilities?

New capability

### Describe the feature you'd like to request

Currently cfn-lint is unable to recognize the correct character limit of a state name. The following CloudFormation Template will cause a CloudFormation error:

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Test

Resources:
  StepFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: states.amazonaws.com
            Action: sts:AssumeRole

  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt StepFunctionRole.Arn
      Definition:
        StartAt: Passsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
        States:
          Passsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss:
            Type: Pass
            Next: Success
          Success:
            Type: Succeed
```

CloudFormation error:

```
Resource handler returned message:
"Invalid State Machine Definition: 'INVALID_STATE_NAME: Invalid State name:
State 'Passsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss' exceeds the 80-character limit allowed by the service at
/States/Passsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss'
(Service: Sfn, Status Code: 400, Request ID: 6...8)
(SDK Attempt Count: 1)"
(RequestToken: 9...4, HandlerErrorCode: InvalidRequest)
```

### Describe the solution you'd like

It would be great if cfn-lint could give an error on this.

### Additional context

cfn-lint seems to have a different check though:

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Test

Resources:
  StepFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: states.amazonaws.com
            Action: sts:AssumeRole

  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt StepFunctionRole.Arn
      Definition:
        StartAt: Passsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
        States:
          Passsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss:
            Type: Pass
            Next: Success
          Success:
            Type: Succeed
```

causes

```
[cfn-lint] E3601: 'Passsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss' does not match any of the regexes: '^.{1,128}$'
```

### Is this something that you'd be interested in working on?

- [ ] 👋 I may be able to implement this feature request

### Would this feature include a breaking change?

- [ ] ⚠️ This feature might incur a breaking change


Discussion:

Patch:
diff --git a/src/cfnlint/data/schemas/other/step_functions/statemachine.json b/src/cfnlint/data/schemas/other/step_functions/statemachine.json
index 74fbf2ca37..f115938a55 100644
--- a/src/cfnlint/data/schemas/other/step_functions/statemachine.json
+++ b/src/cfnlint/data/schemas/other/step_functions/statemachine.json
@@ -53,7 +53,10 @@
       "properties": {
        "Choices": {
         "items": {
-         "$ref": "#/definitions/choice/definitions/Operator"
+         "$ref": "#/definitions/choice/definitions/Operator",
+         "required": [
+          "Next"
+         ]
         },
         "type": "array"
        }
@@ -1199,6 +1202,7 @@
      "$ref": "#/definitions/queryLanguage"
     },
     "Seconds": {
+     "maximum": 99999999,
      "minimum": 0,
      "type": "number"
     },
@@ -1247,7 +1251,7 @@
   "States": {
    "additionalProperties": false,
    "patternProperties": {
-    "^.{1,128}$": {
+    "^.{1,80}$": {
      "$ref": "#/definitions/state"
     }
    },


Test patch:
diff --git a/test/unit/rules/resources/stepfunctions/test_state_machine_definition.py b/test/unit/rules/resources/stepfunctions/test_state_machine_definition.py
index de7b49f134..eabe3bb41c 100644
--- a/test/unit/rules/resources/stepfunctions/test_state_machine_definition.py
+++ b/test/unit/rules/resources/stepfunctions/test_state_machine_definition.py
@@ -431,7 +431,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "required",
                         ]
                     ),
@@ -446,7 +446,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             0,
                             "then",
@@ -464,7 +464,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             3,
                             "then",
@@ -496,7 +496,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "required",
                         ]
                     ),
@@ -578,7 +578,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -598,7 +598,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -674,7 +674,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -774,7 +774,12 @@ def rule():
                                 {
                                     "Condition": "BatchJobNotification",
                                     "Next": "Notify Success",
-                                }
+                                },
+                                {
+                                    "Variable": "$.value",
+                                    "NumericLessThan": 0,
+                                    # missing next
+                                },
                             ],
                             "Next": "Notify Success",
                         },
@@ -822,7 +827,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -848,7 +853,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -874,7 +879,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -911,7 +916,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             0,
                             "then",
@@ -926,6 +931,30 @@ def rule():
                     ),
                     path=deque(["Definition", "States", "Choices2", "Choices", 0]),
                 ),
+                ValidationError(
+                    ("'Next' is a required property"),
+                    rule=StateMachineDefinition(),
+                    validator="required",
+                    schema_path=deque(
+                        [
+                            "properties",
+                            "States",
+                            "patternProperties",
+                            "^.{1,80}$",
+                            "allOf",
+                            0,
+                            "then",
+                            "allOf",
+                            1,
+                            "then",
+                            "properties",
+                            "Choices",
+                            "items",
+                            "required",
+                        ]
+                    ),
+                    path=deque(["Definition", "States", "Choices2", "Choices", 1]),
+                ),
                 ValidationError(
                     ("'Condition' is a required property"),
                     rule=StateMachineDefinition(),
@@ -935,7 +964,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             0,
                             "then",
@@ -1067,7 +1096,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -1093,7 +1122,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -1118,7 +1147,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -1141,7 +1170,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             0,
                             "then",
@@ -1164,7 +1193,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             0,
                             "then",
@@ -1191,7 +1220,7 @@ def rule():
                             "properties",
                             "States",
                             "patternProperties",
-                            "^.{1,128}$",
+                            "^.{1,80}$",
                             "allOf",
                             5,
                             "then",
@@ -1217,10 +1246,4 @@ def test_validate(
     validator,
 ):
     errs = list(rule.validate(validator, {}, instance, {}))
-    for i, err in enumerate(errs):
-        print(i)
-        print(err.message, err.message == expected[i].message)
-        print(err.path, err.path == expected[i].path)
-        print(err.schema_path, err.schema_path == expected[i].schema_path)
-        print(err.validator, err.validator == expected[i].validator)
     assert errs == expected, f"{name!r} test failed with {errs!r}"