Skip to content

Commit 536de26

Browse files
tyukeikddejong
andauthored
Fix Step Functions StartAt path resolution in Parallel branches (aws-cloudformation#4313)
* fix: Validate `StartAt` for both `ItemProcessor` and `Iterator` * fix: `StartAt` validation in nested `Parallel` and test case. * Fix Step Functions Map Iterator validation crash in StateMachineDefinition (aws-cloudformation#4308) --------- Co-authored-by: Kevin DeJong <kddejong@amazon.com> --------- Co-authored-by: Kevin DeJong <kddejong@amazon.com>
1 parent f18378c commit 536de26

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/cfnlint/rules/resources/stepfunctions/StateMachineDefinition.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,19 @@ def _validate_start_at(
138138
yield ValidationError(message, path=error_path, rule=self)
139139

140140
# Validate nested StartAt in Parallel and Map states
141+
base_path = deque() if path is None else deque(path)
141142
for state_name, state in states.items():
143+
if not isinstance(state, dict):
144+
continue
142145
state_type = state.get("Type")
143146

144147
if state_type == "Parallel":
145148
branches = state.get("Branches", [])
146149
if not isinstance(branches, list):
147150
continue
148151
for idx, branch in enumerate(branches):
149-
branch_path = deque(["States", state_name, "Branches", idx])
152+
branch_path = deque(base_path)
153+
branch_path.extend(["States", state_name, "Branches", idx])
150154
yield from self._validate_start_at(
151155
branch, k, add_path_to_message, branch_path
152156
)
@@ -155,14 +159,16 @@ def _validate_start_at(
155159
# ItemProcessor (distributed/inline mode)
156160
processor = state.get("ItemProcessor")
157161
if isinstance(processor, dict):
158-
processor_path = deque(["States", state_name, "ItemProcessor"])
162+
processor_path = deque(base_path)
163+
processor_path.extend(["States", state_name, "ItemProcessor"])
159164
yield from self._validate_start_at(
160165
processor, k, add_path_to_message, processor_path
161166
)
162167
# Iterator (classic map)
163168
iterator = state.get("Iterator")
164169
if isinstance(iterator, dict):
165-
iterator_path = deque(["States", state_name, "Iterator"])
170+
iterator_path = deque(base_path)
171+
iterator_path.extend(["States", state_name, "Iterator"])
166172
yield from self._validate_start_at(
167173
iterator, k, add_path_to_message, iterator_path
168174
)

test/unit/rules/resources/stepfunctions/test_state_machine_definition.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,65 @@ def rule():
14191419
),
14201420
],
14211421
),
1422+
(
1423+
"Parallel state with Map/ItemProcessor missing StartAt target",
1424+
{
1425+
"Definition": {
1426+
"StartAt": "ParallelExecution",
1427+
"States": {
1428+
"ParallelExecution": {
1429+
"Type": "Parallel",
1430+
"Branches": [
1431+
{
1432+
"StartAt": "Pass1",
1433+
"States": {"Pass1": {"Type": "Pass", "End": True}},
1434+
},
1435+
{
1436+
"StartAt": "TestMap",
1437+
"States": {
1438+
"TestMap": {
1439+
"Type": "Map",
1440+
"ItemsPath": "$",
1441+
"ItemProcessor": {
1442+
"ProcessorConfig": {"Mode": "INLINE"},
1443+
"StartAt": "FAIL",
1444+
"States": {
1445+
"Pass2": {
1446+
"Type": "Pass",
1447+
"End": True,
1448+
}
1449+
},
1450+
},
1451+
"End": True,
1452+
}
1453+
},
1454+
},
1455+
],
1456+
"End": True,
1457+
}
1458+
},
1459+
}
1460+
},
1461+
[
1462+
ValidationError(
1463+
"Missing 'Next' target 'FAIL' at /States/ParallelExecution/Branches/1/States/TestMap/ItemProcessor/StartAt",
1464+
rule=StateMachineDefinition(),
1465+
path=deque(
1466+
[
1467+
"Definition",
1468+
"States",
1469+
"ParallelExecution",
1470+
"Branches",
1471+
1,
1472+
"States",
1473+
"TestMap",
1474+
"ItemProcessor",
1475+
"StartAt",
1476+
]
1477+
),
1478+
),
1479+
],
1480+
),
14221481
(
14231482
"Map state with missing StartAt target in ItemProcessor",
14241483
{

0 commit comments

Comments
 (0)