aws-sf-execute-as-via-definition
Modify a state machine's ASL definition (states:UpdateStateMachine), then trigger an execution to run arbitrary AWS SDK Task states as the execution role.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanModifyCode {'state_machine': None}
{'state_machine': None} ExecutesAs {'role': None}
where
node_type(?state_machine) ==
Workflow
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'principal': None}
CanInvoke {'state_machine': None}
emit
| source type | Identity |
|---|---|
| target type | MachineIdentity |
| source | ?principal |
| target | ?role |
| permissions | states:UpdateStateMachine states:StartExecution? |
| conditions | trigger_exists |
| state logic | ACTIVE when the principal can both modify the definition AND invoke/trigger the state machine (matched(optional[0]) — states:StartExecution — OR an existing EventBridge rule or Scheduler is already bound to the state machine and is ENABLED). CONDITIONAL(trigger_exists) when the definition can be modified but no run path is established (no StartExecution permission and no existing active trigger). Never stronger than the contributing CanModifyCode and ExecutesAs edges; BLOCKED if either is BLOCKED (SCP/ permission boundary denial of states:UpdateStateMachine or the execution role is deleted/disabled). |
Narrative
{principal.name} can replace the definition of state machine {state_machine.name} (states:UpdateStateMachine) to include Task states that call arbitrary AWS services as {role.name}; triggering an execution (direct StartExecution or an existing EventBridge trigger) then runs those Task states as {role.name}.
Raw rule rules/derived/aws/stepfunctions.yaml
id: aws-sf-execute-as-via-definition
emits: CanExecuteAs
description: Modify a state machine's ASL definition (states:UpdateStateMachine), then trigger an execution
to run arbitrary AWS SDK Task states as the execution role.
match:
- - principal: null
- CanModifyCode
- state_machine: null
- - state_machine: null
- ExecutesAs
- role: null
where:
- node_type(?state_machine) == Workflow
optional:
- - principal: null
- CanInvoke
- state_machine: null
emit:
source_type: Identity
target_type: MachineIdentity
source: ?principal
target: ?role
permissions:
- states:UpdateStateMachine
- states:StartExecution?
conditions:
- trigger_exists
state_logic: "ACTIVE when the principal can both modify the definition AND invoke/trigger the state\
\ machine (matched(optional[0]) \u2014 states:StartExecution \u2014 OR an existing EventBridge rule\
\ or Scheduler is already bound to the state machine and is ENABLED). CONDITIONAL(trigger_exists)\
\ when the definition can be modified but no run path is established (no StartExecution permission\
\ and no existing active trigger). Never stronger than the contributing CanModifyCode and ExecutesAs\
\ edges; BLOCKED if either is BLOCKED (SCP/ permission boundary denial of states:UpdateStateMachine\
\ or the execution role is deleted/disabled)."
confidence: min(contributing_confidences) * 0.95
derived_from:
- <CanModifyCode edge_id on ?state_machine>
- <ExecutesAs edge_id ?state_machine -> ?role>
- <CanInvoke edge_id?>
false_positive_note: "Only ACTIVE when a run path exists: the attacker can StartExecution (CanInvoke\
\ matched) OR a live EventBridge rule / Scheduler schedule is already bound to the state machine (trigger_exists\
\ confirmed). A modified definition with no trigger is CONDITIONAL(trigger_exists). Do NOT emit ACTIVE\
\ for a state machine that has no active triggers and whose principal lacks states:StartExecution.\
\ Dedupes with can-execute-as execute-as-via-code-modify on (CanExecuteAs, ?principal, ?role): this\
\ rule provides the Workflow-typed framing with Step Functions permissions; do not double-count weight\
\ where both fire. The escalation consequence (role more privileged than ?principal) is produced by\
\ can-control escalate-via-execute-as \u2014 cite, do not re-derive."
narrative: '{principal.name} can replace the definition of state machine {state_machine.name} (states:UpdateStateMachine)
to include Task states that call arbitrary AWS services as {role.name}; triggering an execution (direct
StartExecution or an existing EventBridge trigger) then runs those Task states as {role.name}.'