From 161060b686d335040ae993d43e21939c75f17921 Mon Sep 17 00:00:00 2001 From: junhyeong9812 Date: Fri, 14 Aug 2026 23:18:56 +0900 Subject: [PATCH] Reject write methods not starting with "set" in Property Property.resolveName() located the "set" prefix of a write method with String.indexOf, which matches the token anywhere in the method name. A write method that merely contains "set" (for example offsetX or upset) was silently accepted and resolved to a meaningless property name derived from whatever follows the token, while only names with no "set" token at all were rejected. Match the prefix only at the start of the method name via startsWith, so that write methods that are not setters consistently throw the existing IllegalArgumentException. Extracted from the change originally bundled into gh-36911. Signed-off-by: junhyeong9812 --- .../core/convert/Property.java | 7 +- .../core/convert/PropertyTests.java | 82 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java diff --git a/spring-core/src/main/java/org/springframework/core/convert/Property.java b/spring-core/src/main/java/org/springframework/core/convert/Property.java index 09bf46f46bb9..ecab3b3fbde0 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/Property.java +++ b/spring-core/src/main/java/org/springframework/core/convert/Property.java @@ -150,12 +150,11 @@ private String resolveName() { return StringUtils.uncapitalize(this.readMethod.getName().substring(index)); } else if (this.writeMethod != null) { - int index = this.writeMethod.getName().indexOf("set"); - if (index == -1) { + String methodName = this.writeMethod.getName(); + if (!methodName.startsWith("set")) { throw new IllegalArgumentException("Not a setter method"); } - index += 3; - return StringUtils.uncapitalize(this.writeMethod.getName().substring(index)); + return StringUtils.uncapitalize(methodName.substring(3)); } else { throw new IllegalStateException("Property is neither readable nor writable"); diff --git a/spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java b/spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java new file mode 100644 index 000000000000..833ffe7e7ec6 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.core.convert; + +import java.lang.reflect.Method; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Tests for {@link Property} setter name resolution. + * + * @author Junhyeong Kim + */ +class PropertyTests { + + @Test + void resolveNameForSetter() throws Exception { + assertThat(writeProperty("setName").getName()).isEqualTo("name"); + } + + @Test // no "set" token at all: rejected before and after this change + void rejectNonSetterWriteMethod() { + assertThatIllegalArgumentException() + .isThrownBy(() -> writeProperty("updateName")) + .withMessage("Not a setter method"); + } + + @Test // "set" embedded mid-name: formerly accepted and resolved to "x" + void rejectWriteMethodEmbeddingSetInName() { + assertThatIllegalArgumentException() + .isThrownBy(() -> writeProperty("offsetX")) + .withMessage("Not a setter method"); + } + + @Test // "set" at the end of the name: formerly accepted and resolved to "" + void rejectWriteMethodEndingWithSetToken() { + assertThatIllegalArgumentException() + .isThrownBy(() -> writeProperty("upset")) + .withMessage("Not a setter method"); + } + + + private static Property writeProperty(String writeMethodName) throws Exception { + Method writeMethod = TestBean.class.getMethod(writeMethodName, String.class); + return new Property(TestBean.class, null, writeMethod); + } + + + @SuppressWarnings("unused") + static class TestBean { + + public void setName(String name) { + } + + public void updateName(String name) { + } + + public void offsetX(String value) { + } + + public void upset(String value) { + } + } + +}