azure-devops-sc-identity-enters-subscription
A principal that can CanExecuteAs the pipeline's service-connection identity, where that identity holds ARM Contributor/Owner at subscription scope, gains a foothold in that Azure subscription.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanExecuteAs {'sc_identity': None}
{'sc_identity': None} HasRole {'arm_role': None}
where
node_type(?principal) in [Identity,
HumanIdentity, MachineIdentity, ServiceAccount, ManagedIdentity, ApplicationIdentity]
node_type(?sc_identity) in [MachineIdentity, ApplicationIdentity, FederatedIdentity]
?arm_role.scope_type == subscription
?arm_role.name in ['Contributor', 'Owner', 'User Access Administrator'] OR ?arm_role.actions covers Microsoft.Resources/*
?sc_identity is a DevOps pipeline service-connection identity (provider_type == Microsoft.DevOps/serviceconnections)
emit
| source type | Identity |
|---|---|
| target type | Subscription |
| source | ?principal |
| target | Azure subscription where ?arm_role is assigned to ?sc_identity |
| permissions | ARM Contributor/Owner at subscription scope (on the service-connection identity) |
| state logic | Inherit from the CanExecuteAs contributor: ACTIVE when the principal can actively execute code as the SC identity (CanExecuteAs is ACTIVE) AND the SC identity holds the ARM role at subscription scope. CONDITIONAL when CanExecuteAs is CONDITIONAL (e.g. trigger-only, no direct code modification). BLOCKED if the SC identity is disabled or the ARM role assignment is revoked. The foothold is exactly as strong as the code-execution capability that grants it. |
Narrative
{principal.name} can execute code as service-connection identity {sc_identity.name} (via pipeline code injection - cicd-chains), which holds ARM {arm_role.name} at subscription scope; this gives {principal.name} a foothold in that Azure subscription.
Raw rule rules/derived/azure/devops.yaml
id: azure-devops-sc-identity-enters-subscription
emits: CanEnterSubscription
description: A principal that can CanExecuteAs the pipeline's service-connection identity, where that
identity holds ARM Contributor/Owner at subscription scope, gains a foothold in that Azure subscription.
applies_to:
- azure
match:
- - principal: null
- CanExecuteAs
- sc_identity: null
- - sc_identity: null
- HasRole
- arm_role: null
where:
- node_type(?principal) in [Identity, HumanIdentity, MachineIdentity, ServiceAccount, ManagedIdentity,
ApplicationIdentity]
- node_type(?sc_identity) in [MachineIdentity, ApplicationIdentity, FederatedIdentity]
- ?arm_role.scope_type == subscription
- ?arm_role.name in ['Contributor', 'Owner', 'User Access Administrator'] OR ?arm_role.actions covers
Microsoft.Resources/*
- ?sc_identity is a DevOps pipeline service-connection identity (provider_type == Microsoft.DevOps/serviceconnections)
emit:
source_type: Identity
target_type: Subscription
source: ?principal
target: Azure subscription where ?arm_role is assigned to ?sc_identity
permissions:
- ARM Contributor/Owner at subscription scope (on the service-connection identity)
conditions: []
state_logic: 'Inherit from the CanExecuteAs contributor: ACTIVE when the principal can actively execute
code as the SC identity (CanExecuteAs is ACTIVE) AND the SC identity holds the ARM role at subscription
scope. CONDITIONAL when CanExecuteAs is CONDITIONAL (e.g. trigger-only, no direct code modification).
BLOCKED if the SC identity is disabled or the ARM role assignment is revoked. The foothold is exactly
as strong as the code-execution capability that grants it.'
confidence: 0.9
derived_from:
- ?principal CanExecuteAs ?sc_identity
- ?sc_identity HasRole ?arm_role
false_positive_note: "Only emit when the SC's ARM RBAC is genuinely at subscription scope (or management\
\ group). A resource-group-scoped SC yields CanWrite(resource_group) not CanEnterSubscription \u2014\
\ honor the actual scope. User Access Administrator without Contributor is still sufficient (can self-assign\
\ Contributor) so include it. Multiple subscriptions may be covered by the same SC if its SP has MG-level\
\ RBAC \u2014 emit once per subscription boundary the SC's role covers. FederatedIdentity (WIF service\
\ connections) are included in the source check because WIF SC identities are FederatedIdentity nodes\
\ per devops.md. This edge complements federation-chains federate-then-enter-account (which handles\
\ the WIF path via CanFederateAs) \u2014 this covers both secret-based ARM SC and the CanExecuteAs\
\ antecedent for WIF. Do not double-emit for the same (principal, subscription)."
narrative: "{principal.name} can execute code as service-connection identity {sc_identity.name} (via\
\ pipeline code injection \u2014 cicd-chains), which holds ARM {arm_role.name} at subscription scope;\
\ this gives {principal.name} a foothold in that Azure subscription."