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 <daguimu.geek@gmail.com>
This commit is contained in:
daguimu
2026-03-27 13:29:37 +08:00
committed by Brian Clozel
parent 2a1678f246
commit d37d7abb17
2 changed files with 13 additions and 6 deletions
@@ -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);
}
@@ -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");