cicd-external-ci-federates-as

An external CI system (GitHub Actions/GitLab CI/Azure DevOps) with an OIDC/WIF trust into a cloud role/SA can federate in as that identity; escalation roll-ups are federation-chains'.

derived any cloud emits CanFederateAs

match

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

{'ci_ext': None} ExternalIdentityMapsTo {'internal': None}

where

node_type(?ci_ext) in [ExternalIdentity, FederatedIdentity] # the CI provider's OIDC identity ?ci_ext is an external CI issuer (GitHub Actions / GitLab CI / Azure DevOps OIDC) node_type(?internal) in [Role, ServiceAccount, MachineIdentity] # the cloud identity the CI deploys as exists TrustsExternalIdentity(?anchor -> ?ci_ext) backing this mapping (the role/SA OIDC trust)

emit

source typeFederatedIdentity
target typeRole
source?ci_ext
target?internal
conditionstrust_condition
state logicDELEGATED to federation-chains federation-maps-to-identity: ACTIVE when the CI OIDC trust condition is BROAD/UNPINNED (no `sub` pin so any repo/project/branch workflow token matches, a wildcard/shared `aud`, an attacker-runnable branch), CONDITIONAL(trust_condition) when pinned to a specific repo:owner/repo:ref / project / pipeline the attacker cannot run, BLOCKED if the OIDC provider is disabled/deleted. This rule inherits that exact state — it is the same collapse keyed to a CI-provider ?ci_ext; do not compute a stronger state here.

Narrative

The external CI system {ci_ext.name} holds an OIDC/WIF trust into the cloud identity {internal.name}; a workflow it runs presents a token {internal.name}'s trust accepts, letting the CI federate in as {internal.name} with no static credential (federation-chains rolls up the foothold/impersonation).

Raw rule rules/derived/cicd-chains.yaml

id: cicd-external-ci-federates-as
emits: CanFederateAs
description: An external CI system (GitHub Actions/GitLab CI/Azure DevOps) with an OIDC/WIF trust into
  a cloud role/SA can federate in as that identity; escalation roll-ups are federation-chains'.
applies_to:
- '*'
match:
- - ci_ext: null
  - ExternalIdentityMapsTo
  - internal: null
where:
- 'node_type(?ci_ext) in [ExternalIdentity, FederatedIdentity]  # the CI provider''s OIDC identity'
- ?ci_ext is an external CI issuer (GitHub Actions / GitLab CI / Azure DevOps OIDC)
- 'node_type(?internal) in [Role, ServiceAccount, MachineIdentity]  # the cloud identity the CI deploys
  as'
- exists TrustsExternalIdentity(?anchor -> ?ci_ext) backing this mapping (the role/SA OIDC trust)
emit:
  source_type: FederatedIdentity
  target_type: Role
  source: ?ci_ext
  target: ?internal
  permissions: []
  conditions:
  - trust_condition
  state_logic: "DELEGATED to federation-chains federation-maps-to-identity: ACTIVE when the CI OIDC trust\
    \ condition is BROAD/UNPINNED (no `sub` pin so any repo/project/branch workflow token matches, a wildcard/shared\
    \ `aud`, an attacker-runnable branch), CONDITIONAL(trust_condition) when pinned to a specific repo:owner/repo:ref\
    \ / project / pipeline the attacker cannot run, BLOCKED if the OIDC provider is disabled/deleted.\
    \ This rule inherits that exact state \u2014 it is the same collapse keyed to a CI-provider ?ci_ext;\
    \ do not compute a stronger state here."
  confidence: min(contributing_confidences)
  derived_from:
  - ?ci_ext ExternalIdentityMapsTo ?internal
  - ?anchor TrustsExternalIdentity ?ci_ext (CI OIDC trust)
  false_positive_note: "CITE, DO NOT DUPLICATE federation-chains: this rule exists only to name the external-CI\
    \ case explicitly and dedupes with federation-chains federation-maps-to-identity on (CanFederateAs,\
    \ ?ci_ext, ?internal) \u2014 emit once. The CanEnter*/CanImpersonate/escalation roll-ups off this\
    \ CanFederateAs are federation-chains' (federate-then-enter-*, federate-inherits-capabilities) \u2014\
    \ never re-derive them here. The entire judgment is the OIDC `sub`/`aud`/issuer pin: a correctly pinned\
    \ GitHub-Actions trust (sub == repo:owner/repo:ref:...) is a legitimate control => CONDITIONAL, not\
    \ ACTIVE. Do not treat the mere existence of a CI federation as compromise. If ?internal is a plain\
    \ User, CanFederateAs's target set excludes it \u2014 keep it as FederatesTo (see federation-chains)."
  narrative: The external CI system {ci_ext.name} holds an OIDC/WIF trust into the cloud identity {internal.name};
    a workflow it runs presents a token {internal.name}'s trust accepts, letting the CI federate in as
    {internal.name} with no static credential (federation-chains rolls up the foothold/impersonation).
move · open · esc close