gcp-ar-policy-self-grant-write

A principal that can set a repo's IAM policy can self-grant roles/artifactregistry.writer and then push, reaching CanModifyCode on the repo via policy manipulation.

derived gcp emits CanModifyCode

match

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

{'principal': None} CanModifyPolicy {'repo_policy': None}

where

?repo_policy is the ResourcePolicy of an Artifact Registry repository ?repo node_type(?repo) in [ContainerRegistry, ArtifactRepository]

emit

source typeIdentity
target type*
source?principal
target<Artifact Registry repository whose ResourcePolicy is ?repo_policy>
permissionsartifactregistry.repositories.setIamPolicy artifactregistry.repositories.uploadArtifacts
conditionsiam_permission
state logicCONDITIONAL(iam_permission): the self-grant step requires a successful setIamPolicy call followed by uploadArtifacts, a sequential two-step action. The iam_permission condition gates both steps — if either setIamPolicy or uploadArtifacts is denied by IAM deny policy, the derivation is BLOCKED. ACTIVE when the setIamPolicy CanModifyPolicy is ACTIVE and no deny shadows the uploadArtifacts permission. Inherit weakest from the contributing CanModifyPolicy.

Narrative

{principal.name} can set the IAM policy of {repo.name} (artifactregistry.repositories.setIamPolicy), granting itself writer access, and then push a malicious image (artifactregistry.repositories. uploadArtifacts) - policy manipulation yields the same supply-chain CanModifyCode as a direct writer grant.

Raw rule rules/derived/gcp/artifactregistry.yaml

id: gcp-ar-policy-self-grant-write
emits: CanModifyCode
description: A principal that can set a repo's IAM policy can self-grant roles/artifactregistry.writer
  and then push, reaching CanModifyCode on the repo via policy manipulation.
match:
- - principal: null
  - CanModifyPolicy
  - repo_policy: null
where:
- ?repo_policy is the ResourcePolicy of an Artifact Registry repository ?repo
- node_type(?repo) in [ContainerRegistry, ArtifactRepository]
emit:
  source_type: Identity
  target_type: '*'
  source: ?principal
  target: <Artifact Registry repository whose ResourcePolicy is ?repo_policy>
  permissions:
  - artifactregistry.repositories.setIamPolicy
  - artifactregistry.repositories.uploadArtifacts
  conditions:
  - iam_permission
  state_logic: "CONDITIONAL(iam_permission): the self-grant step requires a successful setIamPolicy call\
    \ followed by uploadArtifacts, a sequential two-step action. The iam_permission condition gates both\
    \ steps \u2014 if either setIamPolicy or uploadArtifacts is denied by IAM deny policy, the derivation\
    \ is BLOCKED. ACTIVE when the setIamPolicy CanModifyPolicy is ACTIVE and no deny shadows the uploadArtifacts\
    \ permission. Inherit weakest from the contributing CanModifyPolicy."
  confidence: 0.9
  derived_from:
  - ?principal CanModifyPolicy ?repo_policy (gcp-ar-set-iam-policy)
  false_positive_note: "The self-grant is a two-step action (setIamPolicy then uploadArtifacts); both\
    \ steps must succeed. An IAM deny blocking setIamPolicy keeps this BLOCKED regardless of the upload\
    \ permission. The CanModifyCode emitted here is the SAME capability as gcp-ar-upload-is-code-modify\
    \ but reached via policy manipulation; graph deduplication should treat them as the same (CanModifyCode,\
    \ ?principal, ?repo) edge \u2014 pick higher confidence, do not double-count weight. container-chains\
    \ fan-out applies equally. The target_type is resolved at emit time from the repository's generic_type\
    \ (ContainerRegistry for Docker/OCI, ArtifactRepository for package repos)."
  narrative: "{principal.name} can set the IAM policy of {repo.name} (artifactregistry.repositories.setIamPolicy),\
    \ granting itself writer access, and then push a malicious image (artifactregistry.repositories. uploadArtifacts)\
    \ \u2014 policy manipulation yields the same supply-chain CanModifyCode as a direct writer grant."
move · open · esc close