gcp-secretmanager-impersonate-via-sakey

Reading a secret whose payload is an SA JSON key yields long-lived credentials for that SA.

derived gcp emits CanImpersonate

match

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

{'principal': None} CanReadSecret {'secret': None} {'secret': None} CredentialsFor {'sa': None}

where

node_type(?sa) == ServiceAccount ?secret payload is classified as a service-account JSON key

emit

source typeIdentity
source<principal>
target<service account the key authenticates>
permissionssecretmanager.versions.access
conditionsiam_permission resource_policy key_permission
state logicInherits the CanReadSecret state (ACTIVE / CONDITIONAL(key_permission) for CMEK / BLOCKED). ACTIVE read of an SA-key payload = holding that SA's credentials.

Narrative

{principal.name} can read secret {secret.name}, whose payload is a JSON key for {sa.name}, obtaining long-lived credentials to act as {sa.name}.

Raw rule rules/derived/gcp/secretmanager.yaml

id: gcp-secretmanager-impersonate-via-sakey
emits: CanImpersonate
description: Reading a secret whose payload is an SA JSON key yields long-lived credentials for that SA.
match:
- - principal: null
  - CanReadSecret
  - secret: null
- - secret: null
  - CredentialsFor
  - sa: null
where:
- node_type(?sa) == ServiceAccount
- ?secret payload is classified as a service-account JSON key
emit:
  source_type: Identity
  source: <principal>
  target: <service account the key authenticates>
  permissions:
  - secretmanager.versions.access
  conditions:
  - iam_permission
  - resource_policy
  - key_permission
  state_logic: Inherits the CanReadSecret state (ACTIVE / CONDITIONAL(key_permission) for CMEK / BLOCKED).
    ACTIVE read of an SA-key payload = holding that SA's credentials.
  confidence: min(contributing_confidences) * 0.9
  derived_from:
  - <CanReadSecret edge_id>
  - <CredentialsFor edge_id>
  false_positive_note: Only when the payload is genuinely an SA JSON key (not a password/token). The key
    must not be disabled/deleted at the SA. Same-privilege SAs are lateral, not escalation (see the CanEscalateTo
    roll-up). Inherits CMEK gating from the CanReadSecret input.
  narrative: '{principal.name} can read secret {secret.name}, whose payload is a JSON key for {sa.name},
    obtaining long-lived credentials to act as {sa.name}.'
move · open · esc close