From d37d7abb1768c3fe8a71ba291f2e24082b26c27e Mon Sep 17 00:00:00 2001 From: daguimu Date: Fri, 27 Mar 2026 13:29:37 +0800 Subject: [PATCH] Reject unbalanced parentheses in profile expressions ProfilesParser.parseTokens() silently accepts unbalanced parentheses in profile expressions such as "dev)" or "(dev", treating them as valid. This can lead to unexpected behavior where malformed @Profile annotations are silently interpreted instead of being rejected. This commit tightens the validation in parseTokens() to reject: - Unmatched closing parenthesis at the top level - Unmatched opening parenthesis when tokens are exhausted Also fixes an existing test that inadvertently relied on this lenient behavior by using "spring&framework)" instead of "(spring&framework)". Closes gh-36550 Signed-off-by: daguimu --- .../org/springframework/core/env/ProfilesParser.java | 8 +++----- .../org/springframework/core/env/ProfilesTests.java | 11 ++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java b/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java index 9d477c9d7d9..326f6f659c4 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java +++ b/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java @@ -88,13 +88,10 @@ final class ProfilesParser { } case "!" -> elements.add(not(parseTokens(expression, tokens, Context.NEGATE))); case ")" -> { - Profiles merged = merge(expression, elements, operator); if (context == Context.PARENTHESIS) { - return merged; + return merge(expression, elements, operator); } - elements.clear(); - elements.add(merged); - operator = null; + assertWellFormed(expression, false); } default -> { Profiles value = equals(token); @@ -105,6 +102,7 @@ final class ProfilesParser { } } } + assertWellFormed(expression, context != Context.PARENTHESIS); return merge(expression, elements, operator); } diff --git a/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java b/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java index dde5aa676a9..7cfcb837dce 100644 --- a/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java @@ -155,7 +155,7 @@ class ProfilesTests { @Test void ofAndExpressionWithoutSpaces() { - Profiles profiles = Profiles.of("spring&framework)"); + Profiles profiles = Profiles.of("(spring&framework)"); assertAndExpression(profiles); } @@ -292,6 +292,15 @@ class ProfilesTests { assertMalformed(() -> Profiles.of("a & b | c")); } + @Test + void malformedExpressionsWithUnbalancedParentheses() { + assertMalformed(() -> Profiles.of("dev)")); + assertMalformed(() -> Profiles.of("(dev")); + assertMalformed(() -> Profiles.of("spring&framework)")); + assertMalformed(() -> Profiles.of("((dev)")); + assertMalformed(() -> Profiles.of("(dev))")); + } + @Test void sensibleToString() { assertThat(Profiles.of("spring")).hasToString("spring");