From a888e862eb69ae33d8bbf7956fc3f9875cb48216 Mon Sep 17 00:00:00 2001 From: Arnout Engelen Date: Thu, 9 Jul 2026 17:59:13 +0200 Subject: [PATCH] fix: make module attribute string encoding deterministic This value makes it into the `pom.xml`'s `extraDependencyAttributes` property at least for SBT, and it'd be good to have this be deterministic: aside from general hygiene, generating them bit-by-bit reproducibly helps validating no malware was sneaked into the artifacts. This is also called 'Reproducible Builds' (https://reproducible-builds.org/) --- .../org/apache/ivy/core/module/id/ModuleRevisionId.java | 3 ++- .../org/apache/ivy/core/module/id/ModuleRevisionIdTest.java | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java b/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java index aab42833..89a6ad7e 100644 --- a/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java +++ b/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java @@ -20,6 +20,7 @@ package org.apache.ivy.core.module.id; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import java.util.WeakHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -259,7 +260,7 @@ public class ModuleRevisionId extends UnmodifiableExtendableItem { public String encodeToString() { StringBuilder buf = new StringBuilder(); - Map attributes = new HashMap<>(getAttributes()); + Map attributes = new TreeMap<>(getAttributes()); attributes.keySet().removeAll(getExtraAttributes().keySet()); attributes.putAll(getQualifiedExtraAttributes()); diff --git a/test/java/org/apache/ivy/core/module/id/ModuleRevisionIdTest.java b/test/java/org/apache/ivy/core/module/id/ModuleRevisionIdTest.java index 9a7667e6..5af34d4f 100644 --- a/test/java/org/apache/ivy/core/module/id/ModuleRevisionIdTest.java +++ b/test/java/org/apache/ivy/core/module/id/ModuleRevisionIdTest.java @@ -85,6 +85,12 @@ public class ModuleRevisionIdTest { extraAttributes.put("nullatt", null); testEncodeDecodeToString( ModuleRevisionId.newInstance("org/apache", "pre/name", "1.0-dev8/2", extraAttributes)); + + String encoded = ModuleRevisionId.newInstance("org/apache", "pre/name", "1.0-dev8/2", extraAttributes).encodeToString(); + // We expect the encoding/ordering to be stable for reproducibility. + // If necessary this may vary across Ivy versions, + // but ideally only intentionally. + assertEquals("+att.name:#@#:+att.value:#@#:+att/name:#@#:+att/value:#@#:+att