aws-ecr-cross-account-push-can-modify-code
An external principal granted ecr:PutImage via the repository's resource policy can push images (cross-account supply-chain attack).
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?resource_policy) ==
ResourcePolicy
?resource_policy is a policy of a ContainerRegistry (?repository)
?resource_policy grants ecr:PutImage (directly or via ecr:*) to ?external
?external.account != ?repository.account
emit
| source type | Identity |
|---|---|
| target type | ContainerRegistry |
| source | ?external |
| target | ?repository |
| permissions | ecr:PutImage ecr:BatchCheckLayerAvailability ecr:InitiateLayerUpload ecr:UploadLayerPart ecr:CompleteLayerUpload |
| conditions | resource_policy |
| state logic | Inherit state from the matched CrossAccountTrust. ACTIVE when the resource policy statement granting PutImage to the external principal is present and not blocked by an SCP in the owning account's organization. CONDITIONAL(resource_policy) when the policy includes a Condition gate (aws:SourceArn, aws:PrincipalOrgID, etc.) — the cross-account push is then conditional on those conditions holding. BLOCKED when an SCP denies cross-account ECR push to this external principal. |
Narrative
{external.name} (account {external.account}) is explicitly granted ecr:PutImage to {repository.name} in account {repository.account} via the repository's resource policy. The external principal can push images into this repository, poisoning every workload that re-pulls from it (container-chains propagates this to each consumer in the owning account).
Raw rule rules/derived/aws/ecr.yaml
id: aws-ecr-cross-account-push-can-modify-code
emits: CanModifyCode
description: An external principal granted ecr:PutImage via the repository's resource policy can push
images (cross-account supply-chain attack).
match:
- - resource_policy: null
- CrossAccountTrust
- external: null
where:
- node_type(?resource_policy) == ResourcePolicy
- ?resource_policy is a policy of a ContainerRegistry (?repository)
- ?resource_policy grants ecr:PutImage (directly or via ecr:*) to ?external
- ?external.account != ?repository.account
emit:
source_type: Identity
target_type: ContainerRegistry
source: ?external
target: ?repository
permissions:
- ecr:PutImage
- ecr:BatchCheckLayerAvailability
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
conditions:
- resource_policy
state_logic: "Inherit state from the matched CrossAccountTrust. ACTIVE when the resource policy statement\
\ granting PutImage to the external principal is present and not blocked by an SCP in the owning account's\
\ organization. CONDITIONAL(resource_policy) when the policy includes a Condition gate (aws:SourceArn,\
\ aws:PrincipalOrgID, etc.) \u2014 the cross-account push is then conditional on those conditions\
\ holding. BLOCKED when an SCP denies cross-account ECR push to this external principal."
confidence: min(contributing_confidences) * 0.92
derived_from:
- ?resource_policy CrossAccountTrust ?external (from aws-ecr-cross-account-push)
- ?resource_policy grants ecr:PutImage in its statement
false_positive_note: "This rule bridges the CrossAccountTrust fact to CanModifyCode so that container-chains\
\ image-push-poisons-consumers can consume it and fan out to consumers. Do not emit if the policy\
\ grants only pull (BatchGetImage + GetDownloadUrlForLayer) without PutImage \u2014 that is pull-only\
\ trust, not push trust."
narrative: '{external.name} (account {external.account}) is explicitly granted ecr:PutImage to {repository.name}
in account {repository.account} via the repository''s resource policy. The external principal can
push images into this repository, poisoning every workload that re-pulls from it (container-chains
propagates this to each consumer in the owning account).'