azure-swa-token-enables-runner-code-modify

A holder of the SWA deployment token can modify the build runner's deployment when a runner with a service identity exists.

derived azure emits CanModifyCode

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanReadSecret {'token': None}

where

?token is the SWA deployment token of ?swa (SWA APIKey returned by listSecrets/action) node_type(?swa) == ApplicationPlatform # Microsoft.Web/staticSites ?runner exists and is a CI build worker (BuildWorker) that the SWA pipeline runs on, with ExecutesAs(?runner, ?identity) satisfied

optional

Present → upgrade the emitted state; absent → downgrade (not a hard requirement).

{'principal': None} CanTrigger {'runner': None}

emit

source typeIdentity
target typeCompute
source?principal
target?runner
conditionstrigger_exists
state logicACTIVE when the token can be used to inject definitions the runner compiles/deploys AND the attacker can also trigger the runner (matched(optional[0])), OR a pre-existing trigger (branch webhook, schedule, PR build) will execute the modified content. CONDITIONAL(trigger_exists) when the token is available but the attacker cannot ensure the runner executes the injected definitions. Inherit weakest state with contributing CanReadSecret: a CONDITIONAL/BLOCKED CanReadSecret yields at most CONDITIONAL/BLOCKED. BLOCKED if a deny assignment covers listSecrets/action or the runner has no ExecutesAs.

Narrative

{principal.name} holds the SWA deployment token of {swa.name} and can inject deployment definitions into the build runner {runner.name} (via direct SWA CDN deploy API), modifying what the runner compiles/deploys; cicd-chains rolls up the CanExecuteAs consequence for the runner's service identity {identity.name} when a run path exists.

Raw rule rules/derived/azure/staticwebapps.yaml

id: azure-swa-token-enables-runner-code-modify
emits: CanModifyCode
description: A holder of the SWA deployment token can modify the build runner's deployment when a runner
  with a service identity exists.
applies_to:
- azure
match:
- - principal: null
  - CanReadSecret
  - token: null
where:
- ?token is the SWA deployment token of ?swa (SWA APIKey returned by listSecrets/action)
- 'node_type(?swa) == ApplicationPlatform   # Microsoft.Web/staticSites'
- ?runner exists and is a CI build worker (BuildWorker) that the SWA pipeline runs on, with ExecutesAs(?runner,
  ?identity) satisfied
optional:
- - principal: null
  - CanTrigger
  - runner: null
emit:
  source_type: Identity
  target_type: Compute
  source: ?principal
  target: ?runner
  permissions: []
  conditions:
  - trigger_exists
  state_logic: 'ACTIVE when the token can be used to inject definitions the runner compiles/deploys AND
    the attacker can also trigger the runner (matched(optional[0])), OR a pre-existing trigger (branch
    webhook, schedule, PR build) will execute the modified content. CONDITIONAL(trigger_exists) when the
    token is available but the attacker cannot ensure the runner executes the injected definitions. Inherit
    weakest state with contributing CanReadSecret: a CONDITIONAL/BLOCKED CanReadSecret yields at most
    CONDITIONAL/BLOCKED. BLOCKED if a deny assignment covers listSecrets/action or the runner has no ExecutesAs.'
  confidence: 0.8
  derived_from:
  - ?principal CanReadSecret ?token (azure-swa-list-secrets)
  - 'cicd-chains: cicd-modify-buildspec-executes-as'
  false_positive_note: "A direct/out-of-band CDN content push using this token (without a build runner)\
    \ is a static-content modification only \u2014 not CanModifyCode against a BuildWorker, and NOT fed\
    \ to cicd-chains. Only emit when a runner with ExecutesAs exists. If the pipeline uses GitHub-hosted\
    \ runners with no persistent identity (OIDC/WIF only), the runner identity is the GitHub Actions OIDC\
    \ subject; federation-chains handles that separately. cicd-chains produces the CanExecuteAs roll-up\
    \ for the runner's service identity."
  narrative: '{principal.name} holds the SWA deployment token of {swa.name} and can inject deployment
    definitions into the build runner {runner.name} (via direct SWA CDN deploy API), modifying what the
    runner compiles/deploys; cicd-chains rolls up the CanExecuteAs consequence for the runner''s service
    identity {identity.name} when a run path exists.'
move · open · esc close