gcp-spanner-set-iam-policy

spanner.databases.setIamPolicy lets a principal grant any role on a specific database to any principal, including itself - self-granting roles/spanner.databaseAdmin yields full data-plane access plus further IAM mutation rights on that database.

derived gcp emits CanGrantPermission

match (effective permission)

{ "action": "spanner.databases.setIamPolicy", "resource_type": "google.spanner.admin.database.v1.Database" }

emit

source typeIdentity
target typeIdentity
source<principal>
target<any principal (self-grant or other)>
permissionsspanner.databases.setIamPolicy
conditionsiam_permission deny_assignment scp_or_org_policy
state logicACTIVE when spanner.databases.setIamPolicy is confirmed as an ACTIVE effective permission on the database scope and no org policy or IAM deny policy blocks IAM mutations; CONDITIONAL(scp_or_org_policy) if an org policy restricts IAM policy modifications; BLOCKED when an IAM deny policy (resourcemanager.projects.setIamPolicy deny or spanner.databases.setIamPolicy deny) explicitly denies the action. Reference /rules/derived/gcp/iam-deny.yaml for deny-policy modeling.

Narrative

{principal.name} holds spanner.databases.setIamPolicy on database {database.name} and can grant itself or any principal roles/spanner.databaseAdmin, gaining full read/write data access and further IAM mutation rights on that database.

Raw rule rules/derived/gcp/spanner.yaml

id: gcp-spanner-set-iam-policy
emits: CanGrantPermission
description: "spanner.databases.setIamPolicy lets a principal grant any role on a specific database to\
  \ any principal, including itself \u2014 self-granting roles/spanner.databaseAdmin yields full data-plane\
  \ access plus further IAM mutation rights on that database."
match_effective_permission:
  action: spanner.databases.setIamPolicy
  resource_type: google.spanner.admin.database.v1.Database
emit:
  source_type: Identity
  target_type: Identity
  source: <principal>
  target: <any principal (self-grant or other)>
  permissions:
  - spanner.databases.setIamPolicy
  conditions:
  - iam_permission
  - deny_assignment
  - scp_or_org_policy
  state_logic: ACTIVE when spanner.databases.setIamPolicy is confirmed as an ACTIVE effective permission
    on the database scope and no org policy or IAM deny policy blocks IAM mutations; CONDITIONAL(scp_or_org_policy)
    if an org policy restricts IAM policy modifications; BLOCKED when an IAM deny policy (resourcemanager.projects.setIamPolicy
    deny or spanner.databases.setIamPolicy deny) explicitly denies the action. Reference /rules/derived/gcp/iam-deny.yaml
    for deny-policy modeling.
  false_positive_note: "spanner.databases.setIamPolicy is present in roles/spanner.admin (project scope)\
    \ and roles/spanner.databaseAdmin (instance or database scope). It allows modifying the Cloud IAM\
    \ policy of the database resource. This is a self-escalation path: the attacker self-grants roles/spanner.databaseAdmin,\
    \ which itself includes spanner.databases.setIamPolicy, spanner.databases.read, spanner.databases.select,\
    \ spanner.databases.write, and spanner.sessions.create \u2014 yielding full data-plane access plus\
    \ further IAM mutations. spanner.instances.setIamPolicy is a separate, broader permission modeled\
    \ in a separate rule (gcp-spanner-set-iam-policy-instance). Do NOT conflate the two \u2014 database-scope\
    \ setIamPolicy does not grant instance-level IAM mutation. VPC-SC perimeters do not block IAM mutations\
    \ (IAM is not part of the Spanner service perimeter scope); only Spanner DATA API calls are blocked."
  narrative: '{principal.name} holds spanner.databases.setIamPolicy on database {database.name} and can
    grant itself or any principal roles/spanner.databaseAdmin, gaining full read/write data access and
    further IAM mutation rights on that database.'
  derived_from:
  - spanner.databases.setIamPolicy effective permission on Database
move · open · esc close