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.

derived azure emits CanEnterSubscription

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 typeIdentity
target typeSubscription
source?principal
targetAzure subscription where ?arm_role is assigned to ?sc_identity
permissionsARM Contributor/Owner at subscription scope (on the service-connection identity)
state logicInherit 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."
move · open · esc close