azure-synapse-execute-as-via-pipeline
Publish a pipeline with attacker code (CanModifyCode on workspace) + trigger it (CanTrigger) + workspace ExecutesAs MI => execute as workspace MI. Cites cicd-chains.yaml cicd-modify-buildspec-executes-as and can-execute-as.yaml.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanModifyCode {'workspace': None}
{'principal': None} CanTrigger {'workspace': None}
{'workspace': None} ExecutesAs {'mi': None}
where
node_type(?workspace) ==
AnalyticsService
node_type(?mi) == ManagedIdentity
?mi.privilege_level >= ?principal.privilege_level
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | <principal> |
| target | <workspace managed identity> |
| permissions | Synapse RBAC: Synapse Artifact Publisher Synapse RBAC: Synapse Compute Operator |
| state logic | ACTIVE: principal can both publish (CanModifyCode) and trigger (CanTrigger) the pipeline, so the full modify+run chain is closed. Inherit weakest from CanModifyCode and CanTrigger contributors. If principal only has CanModifyCode but NOT CanTrigger, CONDITIONAL(trigger_exists) — see cicd-chains rule 1. If principal only has CanTrigger but NOT CanModifyCode, CONDITIONAL(attacker_influenced_definition) — see cicd-chains rule 2. |
Narrative
{principal.name} can publish attacker-controlled pipeline code to Synapse workspace {workspace.name} (Artifact Publisher) and trigger it (Compute Operator), executing as workspace MI {mi.name}.
Raw rule rules/derived/azure/synapse.yaml
id: azure-synapse-execute-as-via-pipeline
emits: CanExecuteAs
description: Publish a pipeline with attacker code (CanModifyCode on workspace) + trigger it (CanTrigger)
+ workspace ExecutesAs MI => execute as workspace MI. Cites cicd-chains.yaml cicd-modify-buildspec-executes-as
and can-execute-as.yaml.
match:
- - principal: null
- CanModifyCode
- workspace: null
- - principal: null
- CanTrigger
- workspace: null
- - workspace: null
- ExecutesAs
- mi: null
where:
- node_type(?workspace) == AnalyticsService
- node_type(?mi) == ManagedIdentity
- ?mi.privilege_level >= ?principal.privilege_level
emit:
source_type: Identity
target_type: Identity
source: <principal>
target: <workspace managed identity>
permissions:
- 'Synapse RBAC: Synapse Artifact Publisher'
- 'Synapse RBAC: Synapse Compute Operator'
conditions: []
state_logic: "ACTIVE: principal can both publish (CanModifyCode) and trigger (CanTrigger) the pipeline,\
\ so the full modify+run chain is closed. Inherit weakest from CanModifyCode and CanTrigger contributors.\
\ If principal only has CanModifyCode but NOT CanTrigger, CONDITIONAL(trigger_exists) \u2014 see cicd-chains\
\ rule 1. If principal only has CanTrigger but NOT CanModifyCode, CONDITIONAL(attacker_influenced_definition)\
\ \u2014 see cicd-chains rule 2."
derived_from:
- <CanModifyCode edge_id>
- <CanTrigger edge_id>
- <ExecutesAs edge_id>
note: "CITES cicd-chains.yaml cicd-modify-buildspec-executes-as (rule 1). The generic linchpin does\
\ NOT apply here because it checks node_class(?compute) == Compute; AnalyticsService is Data class.\
\ This rule is the sole authoritative emitter for the pipeline-modification path; there is no deduplication\
\ with the generic linchpin. Dedupes on (CanExecuteAs, ?principal, ?mi). The escalation roll-up is\
\ can-control.yaml escalate-via-execute-as \u2014 CITE, DO NOT re-derive."
false_positive_note: Requires BOTH publish AND trigger roles; if only one is held, downgrade state as
above. The Synapse RBAC roles are independent of Azure RBAC. Verify that Managed VNet exfiltration
protection does not block the pipeline's outbound calls (does not affect execution-as-MI). Deny assignments
on Synapse workspaces -> BLOCKED.
narrative: '{principal.name} can publish attacker-controlled pipeline code to Synapse workspace {workspace.name}
(Artifact Publisher) and trigger it (Compute Operator), executing as workspace MI {mi.name}.'