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.
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 |
| 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) — 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.'