From e7c6b923308fb3b28ec68b3155010779bac49e0c Mon Sep 17 00:00:00 2001 From: Valentin Delaye Date: Sat, 7 Feb 2026 08:33:14 +0100 Subject: [PATCH] Do not rely on Docker-Content-Digest when getting manifests Signed-off-by: Valentin Delaye --- src/main/java/land/oras/Registry.java | 21 ++++++++++++++----- .../oras/GitHubContainerRegistryITCase.java | 9 ++++++++ src/test/java/land/oras/HarborS3ITCase.java | 16 +++++++++++++- src/test/java/land/oras/PublicECRITCase.java | 18 ++++++++++------ 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/main/java/land/oras/Registry.java b/src/main/java/land/oras/Registry.java index 33be6a2..3240256 100644 --- a/src/main/java/land/oras/Registry.java +++ b/src/main/java/land/oras/Registry.java @@ -553,8 +553,19 @@ public Manifest getManifest(ContainerRef containerRef) { throw new OrasException( "Expected manifest but got index. Probably a multi-platform image instead of artifact"); } - ManifestDescriptor manifestDescriptor = ManifestDescriptor.of(descriptor); - return Manifest.fromJson(descriptor.getJson()).withDescriptor(manifestDescriptor); + String json = descriptor.getJson(); + String digest = descriptor.getDigest(); + if (digest == null) { + LOG.debug("Digest missing from header, using from reference"); + digest = containerRef.getDigest(); + if (digest == null) { + LOG.debug("Digest missing from reference, computing from content"); + digest = containerRef.getAlgorithm().digest(json.getBytes(StandardCharsets.UTF_8)); + LOG.debug("Computed index digest: {}", digest); + } + } + ManifestDescriptor manifestDescriptor = ManifestDescriptor.of(descriptor, digest); + return Manifest.fromJson(json).withDescriptor(manifestDescriptor); } @Override @@ -649,7 +660,7 @@ private void validateDockerContentDigest(HttpClient.ResponseWrapper resp // We need a way to propagate the headers like scoped. // For now just skip validation if (digest == null) { - LOG.warn("Docker-Content-Digest header not found in response. Skipping validation."); + LOG.debug("Docker-Content-Digest header not found in response. Skipping validation."); return; } String computedDigest = SupportedAlgorithm.fromDigest(digest).digest(data); @@ -662,7 +673,7 @@ private void validateDockerContentDigest(HttpClient.ResponseWrapper respon // We need a way to propagate the headers like scoped. // For now just skip validation if (digest == null) { - LOG.warn("Docker-Content-Digest header not found in response. Skipping validation."); + LOG.debug("Docker-Content-Digest header not found in response. Skipping validation."); return; } String computedDigest = SupportedAlgorithm.fromDigest(digest).digest(path); @@ -679,7 +690,7 @@ private void validateDockerContentDigest(HttpClient.ResponseWrapper respon // We need a way to propagate the headers like scoped. // For now just skip validation if (digest == null) { - LOG.warn("Docker-Content-Digest header not found in response. Skipping validation."); + LOG.debug("Docker-Content-Digest header not found in response. Skipping validation."); return null; } SupportedAlgorithm.fromDigest(digest); diff --git a/src/test/java/land/oras/GitHubContainerRegistryITCase.java b/src/test/java/land/oras/GitHubContainerRegistryITCase.java index c1f15e9..367038b 100644 --- a/src/test/java/land/oras/GitHubContainerRegistryITCase.java +++ b/src/test/java/land/oras/GitHubContainerRegistryITCase.java @@ -43,6 +43,15 @@ void shouldPullIndex() { assertNotNull(index); } + @Test + void shouldPUllManifest() { + Registry registry = Registry.builder().build(); + ContainerRef containerRef1 = ContainerRef.parse( + "ghcr.io/oras-project/oras@sha256:fd4c818e80ea594cbd39ca47dc05067c8c5690c4eee6c8aee48c508290a5a0c0"); + Manifest manifest = registry.getManifest(containerRef1); + assertNotNull(manifest); + } + @Test void shouldPullOneBlob() throws IOException { Registry registry = Registry.builder().build(); diff --git a/src/test/java/land/oras/HarborS3ITCase.java b/src/test/java/land/oras/HarborS3ITCase.java index c387467..31d5f5c 100644 --- a/src/test/java/land/oras/HarborS3ITCase.java +++ b/src/test/java/land/oras/HarborS3ITCase.java @@ -29,6 +29,11 @@ import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; +/** + * Test disabled due to authentication + * mkdir lib && touch lib/a + * oras push demo.goharbor.io/oras/lib:foo lib + */ @Execution(ExecutionMode.CONCURRENT) class HarborS3ITCase { @@ -36,7 +41,16 @@ class HarborS3ITCase { Path tempDir; @Test - @Disabled("Only to test with demo Harbor S3 instance") + @Disabled("Only to test with demo Harbor demo instance") + void shouldGetManifest() { + Registry registry = Registry.builder().defaults().build(); + ContainerRef containerRef1 = ContainerRef.parse("demo.goharbor.io/oras/lib:foo"); + Manifest manifest = registry.getManifest(containerRef1); + assertNotNull(manifest); + } + + @Test + @Disabled("Only to test with demo Harbor demo instance") void shouldPullOneBlob() { Registry registry = Registry.builder().defaults().build(); ContainerRef containerRef1 = ContainerRef.parse("demo.goharbor.io/oras/lib:foo"); diff --git a/src/test/java/land/oras/PublicECRITCase.java b/src/test/java/land/oras/PublicECRITCase.java index bb76a24..19ae91c 100644 --- a/src/test/java/land/oras/PublicECRITCase.java +++ b/src/test/java/land/oras/PublicECRITCase.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; @@ -47,13 +46,20 @@ void shouldPullAnonymousIndex() { } @Test - @Disabled("https://github.com/oras-project/oras-java/issues/522") void shouldPullManifest() { - Registry registry = Registry.builder().build(); - ContainerRef containerRef = ContainerRef.parse( + + // Via tag + Registry registry1 = Registry.builder().build(); + ContainerRef containerRef1 = ContainerRef.parse("public.ecr.aws/bitnami/contour-operator:latest"); + Manifest manifest1 = registry1.getManifest(containerRef1); + assertNotNull(manifest1); + + // Via digest + Registry registry2 = Registry.builder().build(); + ContainerRef containerRef2 = ContainerRef.parse( "public.ecr.aws/docker/library/alpine@sha256:59855d3dceb3ae53991193bd03301e082b2a7faa56a514b03527ae0ec2ce3a95"); - Manifest manifest = registry.getManifest(containerRef); - assertNotNull(manifest); + Manifest manifest2 = registry2.getManifest(containerRef2); + assertNotNull(manifest2); } @Test