diff --git a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java index 0f6c8499..b04257c1 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java +++ b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java @@ -72,18 +72,25 @@ public static SpiffeId parse(final String id) { throw new IllegalArgumentException(EMPTY); } - if (!id.startsWith(SCHEME_PREFIX)) { + int schemeSeparatorIndex = id.indexOf("://"); + if (schemeSeparatorIndex <= 0) { throw new InvalidSpiffeIdException(WRONG_SCHEME); } - String rest = id.substring(SCHEME_PREFIX.length()); + String scheme = id.substring(0, schemeSeparatorIndex); + if (!SPIFFE_SCHEME.equalsIgnoreCase(scheme)) { + throw new InvalidSpiffeIdException(WRONG_SCHEME); + } + + String rest = id.substring(schemeSeparatorIndex + 3); int i = 0; for (char c : rest.toCharArray()) { if (c == '/') { break; } - if (!isValidTrustDomainChar(c)) { + char normalized = Character.toLowerCase(c); + if (!isValidTrustDomainChar(normalized)) { throw new InvalidSpiffeIdException(BAD_TRUST_DOMAIN_CHAR); } i++; @@ -100,7 +107,8 @@ public static SpiffeId parse(final String id) { validatePath(path); } - return new SpiffeId(new TrustDomain(td), path); + String normalizedTrustDomain = td.toLowerCase(); + return new SpiffeId(new TrustDomain(normalizedTrustDomain), path); } /** diff --git a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java index 7a26ff26..0eca961e 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java +++ b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java @@ -41,8 +41,9 @@ public static TrustDomain parse(String idOrName) { return SpiffeId.parse(idOrName).getTrustDomain(); } - validateTrustDomainName(idOrName); - return new TrustDomain(idOrName); + String normalizedName = idOrName.toLowerCase(); + validateTrustDomainName(normalizedName); + return new TrustDomain(normalizedName); } /** diff --git a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java index fd6829f6..6ab69519 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java @@ -64,7 +64,9 @@ static Stream provideTestValidSpiffeIds() { Arguments.of("spiffe://trustdomain/path", TrustDomain.parse("trustdomain"), "/path"), Arguments.of("spiffe://trustdomain/path1/path2", TrustDomain.parse("trustdomain"), "/path1/path2"), Arguments.of("spiffe://trustdomain/PATH1/PATH2", TrustDomain.parse("trustdomain"), "/PATH1/PATH2"), - Arguments.of("spiffe://trustdomain/9eebccd2-12bf-40a6-b262-65fe0487d453", TrustDomain.parse("trustdomain"), "/9eebccd2-12bf-40a6-b262-65fe0487d453") + Arguments.of("spiffe://trustdomain/9eebccd2-12bf-40a6-b262-65fe0487d453", TrustDomain.parse("trustdomain"), "/9eebccd2-12bf-40a6-b262-65fe0487d453"), + Arguments.of("SPIFFE://trustdomain/path", TrustDomain.parse("trustdomain"), "/path"), + Arguments.of("SpIfFe://TrUsTdOmAiN/Workload", TrustDomain.parse("trustdomain"), "/Workload") ); } @@ -92,7 +94,7 @@ static Stream provideInvalidSpiffeIds() { Arguments.of("spiffe://user:password@test.org/path/element", "Trust domain characters are limited to lowercase letters, numbers, dots, dashes, and underscores"), Arguments.of("spiffe:path/element", "Scheme is missing or invalid"), Arguments.of("spiffe:/path/element", "Scheme is missing or invalid"), - Arguments.of("SPIFFE://path/element", "Scheme is missing or invalid"), + Arguments.of("spiffe://", "Trust domain is missing"), Arguments.of("spiffe://domain.test/path/elem%5uent", "Path segment characters are limited to letters, numbers, dots, dashes, and underscores"), Arguments.of("spiffe://trustdomain/path//", "Path cannot contain empty segments"), Arguments.of("spiffe://trustdomain/./other", "Path cannot contain dot segments"), @@ -173,9 +175,10 @@ void testParseWithAllChars() { String td = "spiffe://trustdomain" + c; - if (TD_CHARS.contains(c)) { + char normalizedTdChar = Character.toLowerCase(c); + if (TrustDomain.isValidTrustDomainChar(normalizedTdChar)) { SpiffeId spiffeId = SpiffeId.parse(td); - assertEquals(spiffeId.toString(), td); + assertEquals("spiffe://trustdomain" + normalizedTdChar, spiffeId.toString()); } else { try { SpiffeId.parse(td); diff --git a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java index 5dc9bde8..8e4cc8b9 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java @@ -7,8 +7,6 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; - -import static io.spiffe.spiffeid.SpiffeIdTest.TD_CHARS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; @@ -22,6 +20,12 @@ void testTrustDomainFromName() { assertEquals("trustdomain", trustDomain.getName()); } + @Test + void testTrustDomainFromNameMixedCase_isNormalizedToLowercase() { + TrustDomain trustDomain = TrustDomain.parse("TrUsTdOmAiN"); + assertEquals("trustdomain", trustDomain.getName()); + } + @Test void testFromIdStringWithoutPath() { TrustDomain trustDomain = TrustDomain.parse("spiffe://trustdomain"); @@ -41,9 +45,10 @@ void testAllChars() { char c = (char) i; String td = "trustdomain" + c; - if (TD_CHARS.contains(c)) { + char normalizedTdChar = Character.toLowerCase(c); + if (TrustDomain.isValidTrustDomainChar(normalizedTdChar)) { TrustDomain trustDomain = TrustDomain.parse(td); - assertEquals(td, trustDomain.getName()); + assertEquals("trustdomain" + normalizedTdChar, trustDomain.getName()); } else { try { TrustDomain.parse(td); @@ -70,8 +75,8 @@ static Stream provideInvalidTrustDomain() { Arguments.of("", "Trust domain is missing"), Arguments.of("spiffe://", "Trust domain is missing"), Arguments.of(null, "idOrName must not be null"), - Arguments.of("Trustdomain", "Trust domain characters are limited to lowercase letters, numbers, dots, dashes, and underscores"), - Arguments.of("spiffe://Domain.test", "Trust domain characters are limited to lowercase letters, numbers, dots, dashes, and underscores"), + Arguments.of("trustdomain!", "Trust domain characters are limited to lowercase letters, numbers, dots, dashes, and underscores"), + Arguments.of("spiffe://domain!.test", "Trust domain characters are limited to lowercase letters, numbers, dots, dashes, and underscores"), Arguments.of("spiffe://domain.test/spiffe://domain.test/path/element", "Path segment characters are limited to letters, numbers, dots, dashes, and underscores"), Arguments.of("http://domain.test", "Scheme is missing or invalid"), Arguments.of("spiffe:// domain.test ", "Trust domain characters are limited to lowercase letters, numbers, dots, dashes, and underscores"), @@ -112,6 +117,12 @@ void testParseFromSpiffeIdWithPath_extractsTrustDomain() { assertEquals("example.org", trustDomain.getName()); } + @Test + void testParseFromSpiffeIdWithPath_mixedCaseSchemeAndTrustDomain_normalizesTrustDomain() { + TrustDomain trustDomain = TrustDomain.parse("SpIfFe://ExAmPlE.OrG/foo"); + assertEquals("example.org", trustDomain.getName()); + } + @Test void testParseInvalidScheme_spiffeWithSingleSlash_throwsInvalidScheme() { assertThrows(InvalidSpiffeIdException.class,