This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 764b42f87 re-assert CharRange start <= end invariant in readObject
(#1693)
764b42f87 is described below
commit 764b42f876be88b595e8e7deb5dce37566df9efa
Author: alhuda <[email protected]>
AuthorDate: Tue Jun 9 01:31:51 2026 +0530
re-assert CharRange start <= end invariant in readObject (#1693)
Co-authored-by: alhudz <[email protected]>
---
.../java/org/apache/commons/lang3/CharRange.java | 19 +++++++
.../commons/lang3/CharRangeReadObjectTest.java | 63 ++++++++++++++++++++++
2 files changed, 82 insertions(+)
diff --git a/src/main/java/org/apache/commons/lang3/CharRange.java
b/src/main/java/org/apache/commons/lang3/CharRange.java
index e38efeb32..88bb0aa4a 100644
--- a/src/main/java/org/apache/commons/lang3/CharRange.java
+++ b/src/main/java/org/apache/commons/lang3/CharRange.java
@@ -16,6 +16,9 @@
*/
package org.apache.commons.lang3;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
@@ -343,6 +346,22 @@ public Iterator<Character> iterator() {
return new CharacterIterator(this);
}
+ /**
+ * Re-asserts the {@code start <= end} invariant after default
deserialization. The constructor reverses reversed endpoints, so a legitimately
serialized
+ * instance always has {@code start <= end}; a stream that violates this
did not come from the constructor and is rejected.
+ *
+ * @param in See {@link Serializable}.
+ * @throws IOException See {@link Serializable}.
+ * @throws ClassNotFoundException See {@link Serializable}.
+ * @throws InvalidObjectException If {@code start} is greater than {@code
end}.
+ */
+ private void readObject(final ObjectInputStream in) throws IOException,
ClassNotFoundException {
+ in.defaultReadObject();
+ if (start > end) {
+ throw new InvalidObjectException("CharRange start is greater than
end.");
+ }
+ }
+
/**
* Gets a string representation of the character range.
*
diff --git
a/src/test/java/org/apache/commons/lang3/CharRangeReadObjectTest.java
b/src/test/java/org/apache/commons/lang3/CharRangeReadObjectTest.java
new file mode 100644
index 000000000..b79be865f
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/CharRangeReadObjectTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.lang3;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that a serialized {@link CharRange} can't carry an inverted ({@code
start > end}) range.
+ */
+class CharRangeReadObjectTest {
+
+ private static Object deserialize(final byte[] bytes) throws IOException,
ClassNotFoundException {
+ try (ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(bytes))) {
+ return ois.readObject();
+ }
+ }
+
+ @Test
+ void testForgedInvertedRangeIsRejected() throws Exception {
+ // Negative control: the factory reverses reversed endpoints, so no
inverted range can be built directly.
+ assertEquals(CharRange.isIn('a', 'e'), CharRange.isIn('e', 'a'));
+ final CharRange seed = CharRange.isIn('a', 'e');
+ FieldUtils.writeDeclaredField(seed, "start", 'z', true);
+ FieldUtils.writeDeclaredField(seed, "end", 'a', true);
+ assertThrows(InvalidObjectException.class, () ->
deserialize(SerializationUtils.serialize(seed)));
+ assertThrows(SerializationException.class, () ->
SerializationUtils.roundtrip(seed));
+ }
+
+ @Test
+ void testRoundTripPreservesRange() throws Exception {
+ final CharRange range = CharRange.isIn('a', 'e');
+ final CharRange roundtrip = SerializationUtils.roundtrip(range);
+ assertEquals(range, roundtrip);
+ assertTrue(roundtrip.contains('c'));
+ assertFalse(roundtrip.contains('z'));
+ }
+}