aws-codepipeline-artifact-s3-supply-chain
Write access to the pipeline's artifact store S3 bucket/prefix lets an attacker inject a malicious artifact that the next pipeline stage processes as the stage's action role - supply-chain code injection into the pipeline.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?artifact_bucket) ==
ObjectStorage
?artifact_bucket is the pipeline's artifactStore bucket (confirmed by GetPipeline[].pipeline.artifactStore.location)
?principal has EFFECTIVE s3:PutObject on the artifact prefix
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'artifact_bucket': None}
CanTrigger {'pipeline': None}
emit
| source type | Identity |
|---|---|
| target type | Compute |
| source | ?principal |
| target | ?pipeline |
| permissions | s3:PutObject |
| conditions | run_path |
| state logic | ACTIVE if matched(optional[0]) — the artifact store has an EventBridge ObjectCreated trigger that starts the pipeline automatically on artifact write. Otherwise CONDITIONAL(run_path): the attacker writes the malicious artifact but the pipeline must be separately triggered (manually or via another event). Do NOT emit ACTIVE when the pipeline Source stage uses S3 with change detection DISABLED (artifact polling only) and the attacker cannot also StartPipelineExecution. |
Narrative
{principal.name} can write to the artifact store {artifact_bucket.name} (s3:PutObject) used by pipeline {pipeline.name}. The next stage that reads those artifacts processes the attacker's content as the stage's action role{if optional[0]: '; the S3 ObjectCreated trigger starts the pipeline automatically, making this ACTIVE'}.
Raw rule rules/derived/aws/codepipeline.yaml
id: aws-codepipeline-artifact-s3-supply-chain
emits: CanModifyCode
description: "Write access to the pipeline's artifact store S3 bucket/prefix lets an attacker inject a\
\ malicious artifact that the next pipeline stage processes as the stage's action role \u2014 supply-chain\
\ code injection into the pipeline."
match:
- - principal: null
- CanWrite
- artifact_bucket: null
where:
- node_type(?artifact_bucket) == ObjectStorage
- ?artifact_bucket is the pipeline's artifactStore bucket (confirmed by GetPipeline[].pipeline.artifactStore.location)
- ?principal has EFFECTIVE s3:PutObject on the artifact prefix
optional:
- - artifact_bucket: null
- CanTrigger
- pipeline: null
emit:
source_type: Identity
target_type: Compute
source: ?principal
target: ?pipeline
permissions:
- s3:PutObject
conditions:
- run_path
state_logic: "ACTIVE if matched(optional[0]) \u2014 the artifact store has an EventBridge ObjectCreated\
\ trigger that starts the pipeline automatically on artifact write. Otherwise CONDITIONAL(run_path):\
\ the attacker writes the malicious artifact but the pipeline must be separately triggered (manually\
\ or via another event). Do NOT emit ACTIVE when the pipeline Source stage uses S3 with change detection\
\ DISABLED (artifact polling only) and the attacker cannot also StartPipelineExecution."
confidence: min(contributing_confidences) * 0.9
derived_from:
- ?principal CanWrite ?artifact_bucket
- ?artifact_bucket is the artifactStore for ?pipeline
false_positive_note: Only target pipelines whose artifactStore bucket matches ?artifact_bucket AND where
the written prefix is the input artifact for a stage the pipeline actually runs (not an output-only
artifact). The artifact write does NOT immediately run the pipeline unless an S3 source trigger is
configured. Confirm the trigger exists before emitting ACTIVE. An artifact store protected by a CMK
also requires the attacker to have kms:GenerateDataKey AND kms:Encrypt (for writing encrypted objects
to S3); if the bucket is CMK-encrypted, gate on CanDecrypt only as a check, but the actual write permission
depends on CanDecrypt + CanWrite both succeeding (CanDecrypt gates reading; CanGenerateDataKey gates
writing). Ensure the principal has both kms:GenerateDataKey and kms:Encrypt on the CMK.
narrative: '{principal.name} can write to the artifact store {artifact_bucket.name} (s3:PutObject) used
by pipeline {pipeline.name}. The next stage that reads those artifacts processes the attacker''s content
as the stage''s action role{if optional[0]: ''; the S3 ObjectCreated trigger starts the pipeline automatically,
making this ACTIVE''}.'