From 8391d9db2a401f863adee1cd8d6d6ffdf5570582 Mon Sep 17 00:00:00 2001 From: "xinjun.jiang" Date: Thu, 8 Oct 2026 10:59:04 +0800 Subject: [PATCH 1/2] Fix truncated reads from compressed JAR entries Signed-off-by: xinjun.jiang --- .../FileInJarReadableDataSource.java | 9 +- .../FileInJarReadableDataSourceTest.java | 92 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java diff --git a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java index 9dae866a64..adeb27c964 100644 --- a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java +++ b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java @@ -100,7 +100,14 @@ public String readSource() throws Exception { throw new IllegalStateException(String.format("Size of file <%s> exceeds the bufSize (%d): %d", jarFile.getName(), buf.length, inputStream.available())); } - int len = inputStream.read(buf); + int len = 0; + while (len < buf.length) { + int read = inputStream.read(buf, len, buf.length - len); + if (read == -1) { + break; + } + len += read; + } return new String(buf, 0, len, charset); } } diff --git a/sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java b/sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java new file mode 100644 index 0000000000..b3eb9018f2 --- /dev/null +++ b/sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 + * + * 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 com.alibaba.csp.sentinel.datasource; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Random; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.junit.Assert.assertEquals; + +public class FileInJarReadableDataSourceTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void testReadCompleteCompressedEntry() throws Exception { + StringBuilder content = new StringBuilder(); + Random random = new Random(0); + for (int i = 0; i < 65536; i++) { + content.append((char) ('a' + random.nextInt(26))); + } + assertReadSource(content.toString(), 65536); + } + + @Test + public void testReadEmptyEntry() throws Exception { + assertReadSource("", 16); + } + + @Test + public void testReadUtf8Entry() throws Exception { + String content = "{\"resource\":\"资源\"}"; + assertReadSource(content, content.getBytes(StandardCharsets.UTF_8).length); + } + + @Test(expected = IllegalStateException.class) + public void testRejectEntryLargerThanBuffer() throws Exception { + File jar = createJar("12345678"); + FileInJarReadableDataSource dataSource = new FileInJarReadableDataSource<>( + jar.getAbsolutePath(), "rules.json", source -> source, 7); + try { + dataSource.readSource(); + } finally { + dataSource.close(); + } + } + + private void assertReadSource(String content, int bufferSize) throws Exception { + File jar = createJar(content); + FileInJarReadableDataSource dataSource = new FileInJarReadableDataSource<>( + jar.getAbsolutePath(), "rules.json", source -> source, bufferSize); + try { + String source = dataSource.readSource(); + assertEquals(content.length(), source.length()); + assertEquals(content, source); + assertEquals(content, dataSource.loadConfig()); + } finally { + dataSource.close(); + } + } + + private File createJar(String content) throws Exception { + File jar = temporaryFolder.newFile(); + try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jar))) { + output.putNextEntry(new JarEntry("rules.json")); + output.write(content.getBytes(StandardCharsets.UTF_8)); + output.closeEntry(); + } + return jar; + } +} From d09b7ccfa00bd66140580b6e89fdbe7ae5748dfb Mon Sep 17 00:00:00 2001 From: "xinjun.jiang" Date: Tue, 13 Oct 2026 11:04:39 +0800 Subject: [PATCH 2/2] Check EOF before accepting a full JAR read buffer Signed-off-by: xinjun.jiang --- .../FileInJarReadableDataSource.java | 4 ++ .../FileInJarReadableDataSourceTest.java | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java index adeb27c964..7257690dea 100644 --- a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java +++ b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java @@ -108,6 +108,10 @@ public String readSource() throws Exception { } len += read; } + if (len == buf.length && inputStream.read() != -1) { + throw new IllegalStateException(String.format("Size of file <%s> exceeds the bufSize (%d)", + jarFile.getName(), buf.length)); + } return new String(buf, 0, len, charset); } } diff --git a/sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java b/sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java index b3eb9018f2..eb9f5e9182 100644 --- a/sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java +++ b/sentinel-extension/sentinel-datasource-extension/src/test/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSourceTest.java @@ -17,10 +17,16 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.util.Random; import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; import org.junit.Rule; import org.junit.Test; @@ -66,6 +72,41 @@ public void testRejectEntryLargerThanBuffer() throws Exception { } } + @Test(expected = IllegalStateException.class) + public void testRejectEntryLargerThanBufferWhenAvailableUnderReports() throws Exception { + readSourceWithUnderReportingAvailable("12345678", 7); + } + + @Test + public void testReadEntryFillingBufferWhenAvailableUnderReports() throws Exception { + assertEquals("12345678", readSourceWithUnderReportingAvailable("12345678", 8)); + } + + private String readSourceWithUnderReportingAvailable(String content, int bufferSize) throws Exception { + File jar = createJar(content); + FileInJarReadableDataSource dataSource = new FileInJarReadableDataSource<>( + jar.getAbsolutePath(), "rules.json", source -> source, bufferSize); + Field jarFileField = FileInJarReadableDataSource.class.getDeclaredField("jarFile"); + jarFileField.setAccessible(true); + ((JarFile) jarFileField.get(dataSource)).close(); + try (JarFile jarFile = new JarFile(jar) { + @Override + public InputStream getInputStream(ZipEntry entry) throws IOException { + return new FilterInputStream(super.getInputStream(entry)) { + @Override + public int available() { + return 0; + } + }; + } + }) { + jarFileField.set(dataSource, jarFile); + return dataSource.readSource(); + } finally { + dataSource.close(); + } + } + private void assertReadSource(String content, int bufferSize) throws Exception { File jar = createJar(content); FileInJarReadableDataSource dataSource = new FileInJarReadableDataSource<>(