diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/web/ArtifactDownloadTokenSignerTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/web/ArtifactDownloadTokenSignerTest.java index 7c76f048..7b7cfb80 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/web/ArtifactDownloadTokenSignerTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/web/ArtifactDownloadTokenSignerTest.java @@ -47,4 +47,18 @@ class ArtifactDownloadTokenSignerTest { String sig = signer.signRaw(id, pastExp); assertThat(signer.verify(id, pastExp, sig)).isFalse(); } + + /** Forces the constant-time-compare branch by flipping one char at the same length — + * exercises {@code MessageDigest.isEqual}'s byte-by-byte path, not the length short-circuit. */ + @Test + void rejectsSameLengthTamperedSignature() { + var signer = new ArtifactDownloadTokenSigner(secret, clock); + UUID id = UUID.randomUUID(); + var token = signer.sign(id, Duration.ofMinutes(5)); + char last = token.sig().charAt(token.sig().length() - 1); + char swapped = last == 'A' ? 'B' : 'A'; + String tampered = token.sig().substring(0, token.sig().length() - 1) + swapped; + assertThat(tampered).hasSameSizeAs(token.sig()); + assertThat(signer.verify(id, token.exp(), tampered)).isFalse(); + } }