federation-maps-to-identity
A realized external->internal identity mapping whose trust condition an attacker-controlled external identity can satisfy yields federation into the internal identity.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?ext) in [
ExternalIdentity, FederatedIdentity]
node_type(?internal) in [Role, ServiceAccount, MachineIdentity]
exists TrustsExternalIdentity(?anchor -> ?ext) with condition C backing this mapping
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'ext': None}
TrustsExternalIdentity {'internal': None}
emit
| source type | FederatedIdentity |
|---|---|
| target type | Role |
| source | ?ext |
| target | ?internal |
| conditions | trust_condition |
| state logic | if the trust condition is BROAD/UNPINNED so an attacker can present a matching external identity (unpinned sub / wildcard or shared aud / attacker-influenceable issuer / missing cert-subject constraint) OR the attacker demonstrably controls a matching ?ext: ACTIVE. If the condition is PINNED to a specific subject/issuer/cert the attacker does NOT control: CONDITIONAL(trust_condition) — realizable only once the attacker obtains a matching external identity. Inherit weakest with the backing TrustsExternalIdentity / ExternalIdentityMapsTo: a BLOCKED trust (issuer disabled, provider deleted, condition explicitly denies) => BLOCKED. |
Narrative
{ext.name} is mapped to the internal identity {internal.name} by {anchor.name}'s federation trust; because that trust's condition is {trust_condition_summary}, an identity the attacker controls satisfies it and can federate in as {internal.name} with no static credential.
Raw rule rules/derived/federation-chains.yaml
id: federation-maps-to-identity
emits: CanFederateAs
description: A realized external->internal identity mapping whose trust condition an attacker-controlled
external identity can satisfy yields federation into the internal identity.
applies_to:
- '*'
match:
- - ext: null
- ExternalIdentityMapsTo
- internal: null
where:
- node_type(?ext) in [ExternalIdentity, FederatedIdentity]
- node_type(?internal) in [Role, ServiceAccount, MachineIdentity]
- exists TrustsExternalIdentity(?anchor -> ?ext) with condition C backing this mapping
optional:
- - ext: null
- TrustsExternalIdentity
- internal: null
emit:
source_type: FederatedIdentity
target_type: Role
source: ?ext
target: ?internal
permissions: []
conditions:
- trust_condition
state_logic: "if the trust condition is BROAD/UNPINNED so an attacker can present a matching external\
\ identity (unpinned sub / wildcard or shared aud / attacker-influenceable issuer / missing cert-subject\
\ constraint) OR the attacker demonstrably controls a matching ?ext: ACTIVE. If the condition is PINNED\
\ to a specific subject/issuer/cert the attacker does NOT control: CONDITIONAL(trust_condition) \u2014\
\ realizable only once the attacker obtains a matching external identity. Inherit weakest with the\
\ backing TrustsExternalIdentity / ExternalIdentityMapsTo: a BLOCKED trust (issuer disabled, provider\
\ deleted, condition explicitly denies) => BLOCKED."
confidence: min(contributing_confidences)
derived_from:
- ?ext ExternalIdentityMapsTo ?internal
- ?anchor TrustsExternalIdentity ?ext (backing trust, condition C)
false_positive_note: "The whole judgment is in the trust CONDITION \u2014 do not emit ACTIVE for a well-pinned\
\ trust. UNPINNED/broad => ACTIVE: e.g. an AWS OIDC role trust with no sub condition (or `sub` a wildcard),\
\ a shared/`sts.amazonaws.com` default aud, a GitHub-Actions trust missing the repo/branch `sub` pin,\
\ a GCP WIF provider whose attribute mapping accepts any `assertion.sub`, an Azure federated credential\
\ with a broad issuer/subject, or an X.509 trust anchor with no cert-subject condition. PINNED =>\
\ CONDITIONAL(trust_condition): the `sub` bound to a specific repo:owner/repo:ref, a specific SPIFFE\
\ ID, or a specific cert CN the attacker cannot present. Do NOT treat mere existence of a federation\
\ as compromise \u2014 a correctly pinned trust is a legitimate control. If ?internal is a plain User\
\ (not Role/SA/MachineIdentity) the CanFederateAs target set excludes it \u2014 keep it as FederatesTo\
\ and let identity-specific rules handle it. A disabled/deleted OIDC provider or rotated issuer key\
\ => the adapter must not emit the trust => this rule does not fire."
narrative: '{ext.name} is mapped to the internal identity {internal.name} by {anchor.name}''s federation
trust; because that trust''s condition is {trust_condition_summary}, an identity the attacker controls
satisfies it and can federate in as {internal.name} with no static credential.'