gcp-gcr-policy-set

storage.buckets.setIamPolicy on the GCR artifacts bucket = rewrite the registry IAM policy, enabling cross-project push grants, public pull, or access revocation.

derived gcp emits CanModifyPolicy

match (effective permission)

{ "action": "storage.buckets.setIamPolicy", "resource_type": "storage.googleapis.com/Bucket" }

where

resource.name matches 'artifacts.<PROJECT>.appspot.com' (the GCR artifacts backing bucket, not an arbitrary GCS bucket)

emit

source typeIdentity
target typeResourcePolicy
source<principal with effective storage.buckets.setIamPolicy on the artifacts bucket>
target<ResourcePolicy node for the artifacts.PROJECT.appspot.com bucket IAM policy>
permissionsstorage.buckets.setIamPolicy
conditionsscp_or_org_policy
state logicACTIVE when the principal holds effective storage.buckets.setIamPolicy on artifacts.PROJECT.appspot.com. BLOCKED when a GCS IAM deny policy or org policy (constraints/iam.allowedPolicyMemberDomains) prevents the rewrite. CONDITIONAL(scp_or_org_policy) when org policy constraints limit which members can be granted roles on GCS resources in this project.

Narrative

{principal.name} can set the IAM policy of the GCR artifacts bucket artifacts.{project.name}.appspot.com (storage.buckets.setIamPolicy). This allows granting push access to an external attacker (supply-chain backdoor), making images publicly pullable (credential exfil), or revoking access to disrupt workloads. The policy change takes effect immediately.

Raw rule rules/derived/gcp/gcr.yaml

id: gcp-gcr-policy-set
emits: CanModifyPolicy
description: storage.buckets.setIamPolicy on the GCR artifacts bucket = rewrite the registry IAM policy,
  enabling cross-project push grants, public pull, or access revocation.
match_effective_permission:
  action: storage.buckets.setIamPolicy
  resource_type: storage.googleapis.com/Bucket
where:
- resource.name matches 'artifacts.<PROJECT>.appspot.com' (the GCR artifacts backing bucket, not an arbitrary
  GCS bucket)
emit:
  source_type: Identity
  target_type: ResourcePolicy
  source: <principal with effective storage.buckets.setIamPolicy on the artifacts bucket>
  target: <ResourcePolicy node for the artifacts.PROJECT.appspot.com bucket IAM policy>
  permissions:
  - storage.buckets.setIamPolicy
  conditions:
  - scp_or_org_policy
  state_logic: ACTIVE when the principal holds effective storage.buckets.setIamPolicy on artifacts.PROJECT.appspot.com.
    BLOCKED when a GCS IAM deny policy or org policy (constraints/iam.allowedPolicyMemberDomains) prevents
    the rewrite. CONDITIONAL(scp_or_org_policy) when org policy constraints limit which members can be
    granted roles on GCS resources in this project.
  confidence: 0.95
  derived_from:
  - effective storage.buckets.setIamPolicy on artifacts.<PROJECT>.appspot.com
  false_positive_note: "storage.buckets.setIamPolicy at project level (roles/storage.admin at project)\
    \ applies to ALL GCS buckets, not just the artifacts bucket. Scope the target of this CanModifyPolicy\
    \ to the artifacts bucket's ResourcePolicy node only. Org policy constraints/iam.allowedPolicyMemberDomains\
    \ can restrict the ability to add external members even with setIamPolicy \u2014 emit CONDITIONAL(scp_or_org_policy)\
    \ when such a constraint is in effect. gcp/identity-escalation gcp-set-iam-policy-resource also fires\
    \ for this permission; deduplicate on (CanModifyPolicy, principal, artifacts-bucket-policy) \u2014\
    \ emit once."
  narrative: '{principal.name} can set the IAM policy of the GCR artifacts bucket artifacts.{project.name}.appspot.com
    (storage.buckets.setIamPolicy). This allows granting push access to an external attacker (supply-chain
    backdoor), making images publicly pullable (credential exfil), or revoking access to disrupt workloads.
    The policy change takes effect immediately.'
move · open · esc close