From 130188204537ac734313de57b5bfa6292880e8d5 Mon Sep 17 00:00:00 2001 From: Aizal Khan Date: Fri, 26 Jun 2026 19:40:07 +0530 Subject: [PATCH] guard short handle string in XsbReader.readHandle --- .../xmlbeans/impl/schema/XsbReader.java | 10 +++ .../impl/schema/XsbReaderHandleTest.java | 67 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/test/java/org/apache/xmlbeans/impl/schema/XsbReaderHandleTest.java diff --git a/src/main/java/org/apache/xmlbeans/impl/schema/XsbReader.java b/src/main/java/org/apache/xmlbeans/impl/schema/XsbReader.java index ebfb0ce00..4552a8432 100644 --- a/src/main/java/org/apache/xmlbeans/impl/schema/XsbReader.java +++ b/src/main/java/org/apache/xmlbeans/impl/schema/XsbReader.java @@ -437,10 +437,20 @@ SchemaComponent.Ref readHandle() { return null; } + if (handle.isEmpty()) { + throw new SchemaTypeLoaderException("Cannot resolve handle " + handle, typeSystem.getName(), _handle, SchemaTypeLoaderException.BAD_HANDLE); + } + if (handle.charAt(0) != '_') { return typeSystem.getTypePool().refForHandle(handle); } + // every '_'-prefixed handle is a 4-char tag ("_BI_", "_XT_", ...) plus a + // payload; charAt(2) and forPretty(handle, 4) below read past a shorter one + if (handle.length() < 4) { + throw new SchemaTypeLoaderException("Cannot resolve handle " + handle, typeSystem.getName(), _handle, SchemaTypeLoaderException.BAD_HANDLE); + } + switch (handle.charAt(2)) { case 'I': // _BI_ - built-in schema type system SchemaType st = (SchemaType) BuiltinSchemaTypeSystem.get().resolveHandle(handle); diff --git a/src/test/java/org/apache/xmlbeans/impl/schema/XsbReaderHandleTest.java b/src/test/java/org/apache/xmlbeans/impl/schema/XsbReaderHandleTest.java new file mode 100644 index 000000000..0538afb16 --- /dev/null +++ b/src/test/java/org/apache/xmlbeans/impl/schema/XsbReaderHandleTest.java @@ -0,0 +1,67 @@ +/* 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 + * + * http://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.xmlbeans.impl.schema; + +import org.apache.xmlbeans.SchemaTypeLoaderException; +import org.apache.xmlbeans.impl.util.LongUTFDataInputStream; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Field; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class XsbReaderHandleTest { + + // Builds an XsbReader whose next readString() returns the given component + // handle, exactly as a crafted/corrupt .xsb would supply it. + private static XsbReader readerForHandle(String handle) throws Exception { + XsbReader reader = new XsbReader(new SchemaTypeSystemImpl("test"), "h"); + + Field poolF = XsbReader.class.getDeclaredField("_stringPool"); + poolF.setAccessible(true); + SchemaTypeSystemImpl.StringPool pool = (SchemaTypeSystemImpl.StringPool) poolF.get(reader); + int code = pool.codeForString(handle); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + dos.writeShort(code); // readString -> readUnsignedShortOrInt -> stringForCode(code) + dos.flush(); + + Field inputF = XsbReader.class.getDeclaredField("_input"); + inputF.setAccessible(true); + inputF.set(reader, new LongUTFDataInputStream(new ByteArrayInputStream(bos.toByteArray()))); + + return reader; + } + + @Test + void rejectsEmptyHandle() throws Exception { + // an empty handle used to throw StringIndexOutOfBoundsException at handle.charAt(0) + XsbReader reader = readerForHandle(""); + assertThrows(SchemaTypeLoaderException.class, reader::readHandle); + } + + @Test + void rejectsShortUnderscoreHandle() throws Exception { + // "_X" starts with '_' but is too short for handle.charAt(2) + XsbReader reader = readerForHandle("_X"); + assertThrows(SchemaTypeLoaderException.class, reader::readHandle); + } +}