Repository: ChristopheBougere/asl-validator
Pull request #103 (2022-08-18 13:02:25)

fix: fix bug in JSONPath traversal to nested parallel states giving false positive (#100)
Bringing [this v2 fix](https://github.com/ChristopheBougere/asl-validator/pull/100) to v3


Patch:
diff --git a/src/checks/state-transitions-errors.ts b/src/checks/state-transitions-errors.ts
index 5ef47c4..37ecb28 100644
--- a/src/checks/state-transitions-errors.ts
+++ b/src/checks/state-transitions-errors.ts
@@ -12,12 +12,26 @@ export const stateTransitionsErrors : AslChecker = (definition) => {
   // targets for states outside the containers.
   const nextAndDefaultTargets = (nestedStateMachine: StateMachine) => {
     const states: string[] = [];
-    Object.keys(nestedStateMachine.States).forEach((stateName) => {
-      const nestedState = nestedStateMachine.States[stateName];
-      const isContainer = ['Map', 'Parallel'].indexOf(nestedState.Type as string) >= 0;
-      const path = isContainer ? '$.[Next,Default]' : '$..[Next,Default]';
-      states.push(...JSONPath<string[]>({ json: nestedState, path }));
-    });
+    Object.keys(nestedStateMachine.States)
+      .forEach((stateName) => {
+        const nestedState = nestedStateMachine.States[stateName];
+        const isContainer = ['Map', 'Parallel'].indexOf(nestedState.Type as string) >= 0;
+        if (isContainer) {
+          states.push(...JSONPath<string[]>({
+            json: nestedState,
+            path: '$.Next',
+          }));
+          states.push(...JSONPath<string[]>({
+            json: nestedState,
+            path: '$.Default',
+          }));
+        } else {
+          states.push(...JSONPath<string[]>({
+            json: nestedState,
+            path: '$..[Next,Default]',
+          }));
+        }
+      });
     return states;
   };
 
@@ -28,7 +42,10 @@ export const stateTransitionsErrors : AslChecker = (definition) => {
     // don't traverse into any nested states. We only want to record the States
     // that are immediately under the Branch.
     // These are the only valid states to link to from within the branch
-    JSONPath<States[]>({ json: nestedStateMachine, path: '$.States' })
+    JSONPath<States[]>({
+      json: nestedStateMachine,
+      path: '$.States',
+    })
       .forEach((branchStates) => {
         availStateNames = availStateNames.concat(Object.keys(branchStates));
       });
@@ -43,7 +60,10 @@ export const stateTransitionsErrors : AslChecker = (definition) => {
   // we know that every `Parallel` state has its expected `Branches` field
   // we need to visit each Branch within a Parallel to ensure that it doesn't
   // link outside its branch.
-  JSONPath<StateMachine[][]>({ json: definition, path: '$..Branches' })
+  JSONPath<StateMachine[][]>({
+    json: definition,
+    path: '$..Branches',
+  })
     .forEach((parallelBranches) => {
       parallelBranches.forEach((nestedStateMachine) => {
         const errs = validateNestedStateMachine(nestedStateMachine).map((state) => ({
@@ -59,15 +79,19 @@ export const stateTransitionsErrors : AslChecker = (definition) => {
   // we know that every `Map` state has its expected `Iterator` field
   // we need to visit the Iterator within a Map to ensure that it doesn't
   // link outside its container.
-  JSONPath<StateMachine[]>({ json: definition, path: '$..Iterator' })
+  JSONPath<StateMachine[]>({
+    json: definition,
+    path: '$..Iterator',
+  })
     .forEach((nestedStateMachine) => {
-      const errs = validateNestedStateMachine(nestedStateMachine).map((state) => ({
-        'Error code': StateMachineErrorCode.MapOutboundTransitionTarget,
-        Message: `Map branch state cannot transition to target: ${state}`,
-      }));
+      const errs = validateNestedStateMachine(nestedStateMachine)
+        .map((state) => ({
+          'Error code': StateMachineErrorCode.MapOutboundTransitionTarget,
+          Message: `Map branch state cannot transition to target: ${state}`,
+        }));
 
       errorMessages.push(...errs);
     });
 
   return errorMessages;
-}
\ No newline at end of file
+}


Test patch:
diff --git a/src/__tests__/definitions/valid-parallel-nested-2.json b/src/__tests__/definitions/valid-parallel-nested-2.json
new file mode 100644
index 0000000..b712953
--- /dev/null
+++ b/src/__tests__/definitions/valid-parallel-nested-2.json
@@ -0,0 +1,35 @@
+{
+  "StartAt": "Parallel 1",
+  "States": {
+    "Parallel 1": {
+      "Branches": [
+        {
+          "StartAt": "Parallel 2",
+          "States": {
+            "Parallel 2": {
+              "Branches": [
+                {
+                  "StartAt": "Task 1",
+                  "States": {
+                    "Task 1": {
+                      "Type": "Pass",
+                      "Next": "Task 2"
+                    },
+                    "Task 2": {
+                      "Type": "Pass",
+                      "End": true
+                    }
+                  }
+                }
+              ],
+              "Type": "Parallel",
+              "End": true
+            }
+          }
+        }
+      ],
+      "Type": "Parallel",
+      "End": true
+    }
+  }
+}