From 6c576a5f99597768ea6b1a33652020c5cadd9eee Mon Sep 17 00:00:00 2001 From: konstantinosGkilas Date: Thu, 2 Apr 2026 10:06:45 +0300 Subject: [PATCH] Fix DockerImageName compatibility check when digest is present (#10527) Strip tag from repository when parsing images with both tag and digest (e.g., postgres:16.8@sha256:...) to prevent tag leaking into repository name. --- .../utility/DockerImageName.java | 3 +- .../DockerImageNameCompatibilityTest.java | 27 ++++++++++++++ .../utility/DockerImageNameTest.java | 36 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index e3a35855bf8..0d16eaeb35e 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -87,7 +87,8 @@ public DockerImageName(String fullImageName) { } if (remoteName.contains("@sha256:")) { - repository = remoteName.split("@sha256:")[0]; + String beforeDigest = remoteName.split("@sha256:")[0]; + repository = beforeDigest.contains(":") ? beforeDigest.split(":")[0] : beforeDigest; versioning = new Sha256Versioning(remoteName.split("@sha256:")[1]); } else if (remoteName.contains(":")) { repository = remoteName.split(":")[0]; diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index dc7bf9ba1ab..2c3471ef203 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -85,6 +85,33 @@ void testImageWithClaimedCompatibilityForVersion() { .isFalse(); } + @Test + void testDigestOnlyImageIsCompatible() { + DockerImageName subject = DockerImageName.parse("postgres@sha256:1234abcd1234abcd1234abcd1234abcd"); + + assertThat(subject.isCompatibleWith(DockerImageName.parse("postgres"))) + .as("postgres@sha256:... ~= postgres") + .isTrue(); + } + + @Test + void testTagAndDigestImageIsCompatible() { + DockerImageName subject = DockerImageName.parse("postgres:16.8@sha256:1234abcd1234abcd1234abcd1234abcd"); + + assertThat(subject.isCompatibleWith(DockerImageName.parse("postgres"))) + .as("postgres:16.8@sha256:... ~= postgres") + .isTrue(); + } + + @Test + void testTagAndDigestImageWithRegistryIsCompatible() { + DockerImageName subject = DockerImageName.parse("registry.foo.com/repo:tag@sha256:1234abcd1234abcd1234abcd1234abcd"); + + assertThat(subject.isCompatibleWith(DockerImageName.parse("registry.foo.com/repo"))) + .as("registry.foo.com/repo:tag@sha256:... ~= registry.foo.com/repo") + .isTrue(); + } + @Test void testAssertMethodAcceptsCompatible() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar"); diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index 7d6f8cd2633..c7c20553a62 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -1,6 +1,7 @@ package org.testcontainers.utility; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -24,6 +25,8 @@ public static String[] getNames() { "registry.foo.com:1234/repo_here/my-name:1.0", "registry.foo.com:1234/repo-here/my-name@sha256:1234abcd1234abcd1234abcd1234abcd", "registry.foo.com:1234/my-name@sha256:1234abcd1234abcd1234abcd1234abcd", + "myname:latest@sha256:1234abcd1234abcd1234abcd1234abcd", + "registry.foo.com:1234/repo-here/my-name:1.0@sha256:1234abcd1234abcd1234abcd1234abcd", "1.2.3.4/my-name:1.0", "1.2.3.4:1234/my-name:1.0", "1.2.3.4/repo-here/my-name:1.0", @@ -147,4 +150,37 @@ void testParsing( } } } + + @Nested + class TagAndDigestParsing { + + @Test + void testTagAndDigestStripsTagFromRepository() { + DockerImageName imageName = DockerImageName.parse("myname:latest@sha256:1234abcd1234abcd1234abcd1234abcd"); + + assertThat(imageName.getRegistry()).isEqualTo(""); + assertThat(imageName.getUnversionedPart()).isEqualTo("myname"); + assertThat(imageName.getVersionPart()).isEqualTo("sha256:1234abcd1234abcd1234abcd1234abcd"); + } + + @Test + void testTagAndDigestWithRepoPath() { + DockerImageName imageName = DockerImageName.parse("repo/myname:1.0@sha256:1234abcd1234abcd1234abcd1234abcd"); + + assertThat(imageName.getRegistry()).isEqualTo(""); + assertThat(imageName.getUnversionedPart()).isEqualTo("repo/myname"); + assertThat(imageName.getVersionPart()).isEqualTo("sha256:1234abcd1234abcd1234abcd1234abcd"); + } + + @Test + void testTagAndDigestWithRegistry() { + DockerImageName imageName = DockerImageName.parse( + "registry.foo.com:1234/repo-here/my-name:1.0@sha256:1234abcd1234abcd1234abcd1234abcd" + ); + + assertThat(imageName.getRegistry()).isEqualTo("registry.foo.com:1234"); + assertThat(imageName.getUnversionedPart()).isEqualTo("registry.foo.com:1234/repo-here/my-name"); + assertThat(imageName.getVersionPart()).isEqualTo("sha256:1234abcd1234abcd1234abcd1234abcd"); + } + } }