diff --git a/src/main/java/land/oras/ContainerRef.java b/src/main/java/land/oras/ContainerRef.java index 1e0d9f5c..b680bd9d 100644 --- a/src/main/java/land/oras/ContainerRef.java +++ b/src/main/java/land/oras/ContainerRef.java @@ -274,10 +274,7 @@ public static ContainerRef parse(String name) { // Validate digest algorithm if (digest != null) { - String prefix = digest.split(":")[0]; - if (!SupportedAlgorithm.isSupported(prefix)) { - throw new OrasException("Unsupported digest algorithm: " + prefix); - } + SupportedAlgorithm.fromDigest(digest); } return new ContainerRef(registry, namespace, repository, tag, digest); diff --git a/src/main/java/land/oras/utils/SupportedAlgorithm.java b/src/main/java/land/oras/utils/SupportedAlgorithm.java index 77b48cfe..4f6efca6 100644 --- a/src/main/java/land/oras/utils/SupportedAlgorithm.java +++ b/src/main/java/land/oras/utils/SupportedAlgorithm.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.nio.file.Path; +import java.util.regex.Pattern; import land.oras.exception.OrasException; import org.jspecify.annotations.NullMarked; @@ -53,6 +54,12 @@ public enum SupportedAlgorithm { */ private final String prefix; + /** + * Regex for a digest + * Digests + */ + private static final Pattern DIGEST_REGEX = Pattern.compile("^[a-z0-9]+(?:[+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$"); + /** * Get the algorithm * @param algorithm The algorithm @@ -98,26 +105,15 @@ public String digest(InputStream inputStream) { return DigestUtils.digest(algorithm, inputStream); } - /** - * Check if the algorithm is supported - * @param prefix The algorithm prefix - * @return True if supported - */ - public static boolean isSupported(String prefix) { - for (SupportedAlgorithm supportedAlgorithm : SupportedAlgorithm.values()) { - if (supportedAlgorithm.getPrefix().equals(prefix)) { - return true; - } - } - return false; - } - /** * Get the algorithm from a digest * @param digest The digest * @return The algorithm */ public static SupportedAlgorithm fromDigest(String digest) { + if (!DIGEST_REGEX.matcher(digest).matches()) { + throw new OrasException("Invalid digest: " + digest); + } for (SupportedAlgorithm algorithm : SupportedAlgorithm.values()) { if (digest.startsWith(algorithm.getPrefix())) { return algorithm; diff --git a/src/test/java/land/oras/ContainerRefTest.java b/src/test/java/land/oras/ContainerRefTest.java index 714554a8..616b56cc 100644 --- a/src/test/java/land/oras/ContainerRefTest.java +++ b/src/test/java/land/oras/ContainerRefTest.java @@ -49,6 +49,10 @@ void shouldFailWithUnSupportedAlgorithm() { OrasException.class, () -> ContainerRef.parse("docker.io/library/foo/alpine:latest@test:1234567890abcdef"), "Unsupported algorithm: test"); + assertThrows( + OrasException.class, + () -> ContainerRef.parse("docker.io/library/foo/alpine:latest@sha256:sha256:1234567890abcdef"), + "Unsupported algorithm: test"); } @Test diff --git a/src/test/java/land/oras/utils/SupportedAlgorithmTest.java b/src/test/java/land/oras/utils/SupportedAlgorithmTest.java index 9f93c7c4..4f18fbe1 100644 --- a/src/test/java/land/oras/utils/SupportedAlgorithmTest.java +++ b/src/test/java/land/oras/utils/SupportedAlgorithmTest.java @@ -20,6 +20,7 @@ package land.oras.utils; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import land.oras.exception.OrasException; @@ -34,4 +35,24 @@ public class SupportedAlgorithmTest { void shouldThrowIfInvalidDigest() { assertThrows(OrasException.class, () -> SupportedAlgorithm.fromDigest("invalid")); } + + @Test + void shouldPreventDuplicatePrefix() { + assertThrows( + OrasException.class, + () -> SupportedAlgorithm.fromDigest( + "sha256:sha256:245d81d351d8d3b00ae1880ac480c593abd357d5bae561052ae23cbffbecbfe8")); + } + + @Test + void shouldWorkWithRegisteredDigest() { + assertEquals( + SupportedAlgorithm.SHA256, + SupportedAlgorithm.fromDigest( + "sha256:245d81d351d8d3b00ae1880ac480c593abd357d5bae561052ae23cbffbecbfe8")); + assertEquals( + SupportedAlgorithm.SHA512, + SupportedAlgorithm.fromDigest( + "sha512:cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); + } }