Skip to content

Commit 3f250a3

Browse files
committed
Guard Public ECR candidate cleanup
1 parent b9918b8 commit 3f250a3

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

cmd/publish-public-ecr-release-tags/main.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import (
1414

1515
const defaultRegistryURI = "public.ecr.aws/conductorone"
1616

17-
var sha256DigestPattern = regexp.MustCompile(`^(?:sha256:)?([A-Fa-f0-9]{64})$`)
17+
var (
18+
sha256DigestPattern = regexp.MustCompile(`^(?:sha256:)?([A-Fa-f0-9]{64})$`)
19+
releaseCandidateTagPattern = regexp.MustCompile(`^release-candidate-[0-9]+-[0-9]+$`)
20+
)
1821

1922
type config struct {
2023
repositoryName string
@@ -265,6 +268,10 @@ func putImageTag(aws commandRunner, repositoryName, manifest, tag, digest string
265268
}
266269

267270
func deleteImageTag(aws commandRunner, repositoryName, tag string) error {
271+
if !releaseCandidateTagPattern.MatchString(tag) {
272+
return fmt.Errorf("refusing to delete non-candidate Public ECR tag %q", tag)
273+
}
274+
268275
args := []string{
269276
"ecr-public", "batch-delete-image",
270277
"--repository-name", repositoryName,

cmd/publish-public-ecr-release-tags/main_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,38 @@ func TestPublishPublicECRTagsCandidateCleanupIsBestEffort(t *testing.T) {
293293
}
294294
}
295295

296+
func TestPublishPublicECRTagsRefusesUnsafeCandidateCleanup(t *testing.T) {
297+
digestFile := writeDigestFile(t, fmt.Sprintf("%s public.ecr.aws/conductorone/bridge-client:temporary\n", digestA))
298+
fake := &fakeAWS{
299+
describeResults: []describeResult{
300+
{notFound: true},
301+
{digest: digestA},
302+
},
303+
}
304+
cfg := config{
305+
repositoryName: "bridge-client",
306+
versionTag: "1.2.3",
307+
candidateTag: "temporary",
308+
digestFile: digestFile,
309+
registryURI: defaultRegistryURI,
310+
}
311+
var stdout, stderr bytes.Buffer
312+
313+
err := publish(cfg, fake, &fakeDocker{}, &stdout, &stderr)
314+
if err != nil {
315+
t.Fatalf("publish: %v", err)
316+
}
317+
if fake.calledCommand("batch-delete-image") {
318+
t.Fatal("unsafe candidate tag must not be sent to BatchDeleteImage")
319+
}
320+
if !strings.Contains(stderr.String(), `refusing to delete non-candidate Public ECR tag "temporary"`) {
321+
t.Fatalf("stderr = %q", stderr.String())
322+
}
323+
if !strings.Contains(stdout.String(), "Published public.ecr.aws/conductorone/bridge-client:1.2.3") {
324+
t.Fatalf("stdout = %q", stdout.String())
325+
}
326+
}
327+
296328
func runPublishForTest(t *testing.T, digestFile string, fake *fakeAWS) (string, string, error) {
297329
t.Helper()
298330
return runPublishForTestWithDocker(t, digestFile, fake, &fakeDocker{})

0 commit comments

Comments
 (0)