gcp-ar-upload-is-code-modify

A principal with artifactregistry.repositories.uploadArtifacts can push/overwrite images or packages into the repo - the code-supply-chain write primitive feeding container-chains fan-out.

derived gcp emits CanModifyCode

match (effective permission)

{ "action": "artifactregistry.repositories.uploadArtifacts", "resource_type": "google.artifactregistry.Repository" }

emit

source typeIdentity
target type*
source<principal>
target<Artifact Registry repository in scope>
permissionsartifactregistry.repositories.uploadArtifacts
state logicACTIVE when the permission is effective (not denied by IAM deny policy or VPC-SC perimeter enforcement). CONDITIONAL(push_succeeds) if the repo has immutable tags enabled (ImmutableTags: true) — a push to an EXISTING tag fails; a push creating a NEW tag would succeed but reaches only consumers referencing that new tag. BLOCKED if an IAM deny policy denies artifactregistry.repositories.uploadArtifacts or a VPC-SC perimeter rejects the API call from the principal's project. Inherit weakest with any contributing role binding.

Narrative

{principal.name} holds artifactregistry.repositories.uploadArtifacts on repository {repo.name}; it can push or overwrite container images (or package artifacts) in that repo. container-chains then propagates this CanModifyCode to every workload that pulls from {repo.name} via a mutable tag, injecting attacker code into those workloads on next pull/redeploy.

Raw rule rules/derived/gcp/artifactregistry.yaml

id: gcp-ar-upload-is-code-modify
emits: CanModifyCode
description: "A principal with artifactregistry.repositories.uploadArtifacts can push/overwrite images\
  \ or packages into the repo \u2014 the code-supply-chain write primitive feeding container-chains fan-out."
match_effective_permission:
  action: artifactregistry.repositories.uploadArtifacts
  resource_type: google.artifactregistry.Repository
emit:
  source_type: Identity
  target_type: '*'
  source: <principal>
  target: <Artifact Registry repository in scope>
  permissions:
  - artifactregistry.repositories.uploadArtifacts
  conditions: []
  state_logic: "ACTIVE when the permission is effective (not denied by IAM deny policy or VPC-SC perimeter\
    \ enforcement). CONDITIONAL(push_succeeds) if the repo has immutable tags enabled (ImmutableTags:\
    \ true) \u2014 a push to an EXISTING tag fails; a push creating a NEW tag would succeed but reaches\
    \ only consumers referencing that new tag. BLOCKED if an IAM deny policy denies artifactregistry.repositories.uploadArtifacts\
    \ or a VPC-SC perimeter rejects the API call from the principal's project. Inherit weakest with any\
    \ contributing role binding."
  confidence: 0.95
  derived_from:
  - 'effective permission: artifactregistry.repositories.uploadArtifacts on <repo>'
  false_positive_note: "Immutable-tag repos (ImmutableTags: true) block overwrite of existing tags \u2014\
    \ downgrade the fan-out to CONDITIONAL(push_succeeds) for consumers that reference a mutable (non-digest-pinned)\
    \ tag, because while the push of a NEW tag succeeds, an existing-tag consumer is not automatically\
    \ redirected. Digest-pinned consumers (@sha256) are not reached regardless of immutability \u2014\
    \ container-chains sets those fan-out edges to BLOCKED. VPC-SC enforcement on the repo's project rejects\
    \ push calls from outside the perimeter even with the IAM permission \u2014 emit BLOCKED for such\
    \ principals. Binary Authorization on consuming GKE clusters makes the supply-chain consequence CONDITIONAL(binauthorz_attestation)\
    \ even when this push is ACTIVE. Do not conflate project-level role grant (all repos) with a single-repo-scoped\
    \ CanModifyCode \u2014 scope edges per repo. For non-OCI repos (Maven, npm, PyPI, etc.), the target_type\
    \ is resolved at emit time from the repository's generic_type (ArtifactRepository for package repos,\
    \ ContainerRegistry for Docker/OCI)."
  narrative: '{principal.name} holds artifactregistry.repositories.uploadArtifacts on repository {repo.name};
    it can push or overwrite container images (or package artifacts) in that repo. container-chains then
    propagates this CanModifyCode to every workload that pulls from {repo.name} via a mutable tag, injecting
    attacker code into those workloads on next pull/redeploy.'
move · open · esc close