sign-as-yields-impersonation

Signing tokens as an identity yields impersonation when a downstream trust validates those signatures.

derived any cloud emits CanImpersonate

match

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

{'principal': None} CanSignAs {'signer': None}

where

node_type(?identity) in [ServiceAccount, MachineIdentity, ManagedIdentity, WorkloadIdentity, ApplicationIdentity] # Azure SP is modeled as ApplicationIdentity

optional

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

{'identity': None} AuthenticatesTo {'relying_party': None}

emit

source typeIdentity
source?principal
target?identity
conditionstrust_relationship
state logicif matched(optional[0]) (a relying party validates ?identity's tokens/assertions): ACTIVE; else CONDITIONAL(trust_exists) — the signing capability is real but no modeled trust consumes the signature yet, so no concrete authentication target is proven. Inherit weakest with the CanSignAs input; BLOCKED if CanSignAs is BLOCKED.

Narrative

{principal.name} can sign tokens as {identity.name} ({perm:CanSignAs}); where a relying party validates {identity.name}'s signatures, {principal.name} can forge authentication and impersonate {identity.name}.

Raw rule rules/derived/credential-chains.yaml

id: sign-as-yields-impersonation
emits: CanImpersonate
description: Signing tokens as an identity yields impersonation when a downstream trust validates those
  signatures.
applies_to:
- '*'
match:
- - principal: null
  - CanSignAs
  - signer: null
where:
- 'node_type(?identity) in [ServiceAccount, MachineIdentity, ManagedIdentity, WorkloadIdentity, ApplicationIdentity]  #
  Azure SP is modeled as ApplicationIdentity'
optional:
- - identity: null
  - AuthenticatesTo
  - relying_party: null
emit:
  source_type: Identity
  source: ?principal
  target: ?identity
  permissions: []
  conditions:
  - trust_relationship
  state_logic: "if matched(optional[0]) (a relying party validates ?identity's tokens/assertions): ACTIVE;\
    \ else CONDITIONAL(trust_exists) \u2014 the signing capability is real but no modeled trust consumes\
    \ the signature yet, so no concrete authentication target is proven. Inherit weakest with the CanSignAs\
    \ input; BLOCKED if CanSignAs is BLOCKED."
  confidence: min(contributing_confidences) * 0.9
  derived_from:
  - ?principal CanSignAs ?signer
  - ?signer -> ?identity (signs-for resolution)
  - ?identity AuthenticatesTo ?relying_party?
  false_positive_note: 'Conservative by design. Signing capability alone is not impersonation: a forged
    JWT/SAML assertion authenticates only where some relying party trusts the signer''s key/issuer. Emit
    ACTIVE only when such a trust is modeled (AuthenticatesTo / CanFederateAs / a validated OIDC issuer);
    otherwise CONDITIONAL(trust_exists). Do not chain to escalation off a CONDITIONAL sign-as impersonation
    until the consuming trust is confirmed. A SigningKey that no relying party trusts (e.g. an inactive/legacy
    key) yields nothing.'
  narrative: '{principal.name} can sign tokens as {identity.name} ({perm:CanSignAs}); where a relying
    party validates {identity.name}''s signatures, {principal.name} can forge authentication and impersonate
    {identity.name}.'
move · open · esc close