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.
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. |
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