You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2026/08/31 16:42:53 UTC
(poi) branch trunk updated: Fix Biff8DecryptingStream.readFully to honour len on the never-encrypted path (#1232)
This is an automated email from the ASF dual-hosted git repository.
pjfanning pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/poi.git
The following commit(s) were added to refs/heads/trunk by this push:
new 3780f5646f Fix Biff8DecryptingStream.readFully to honour len on the never-encrypted path (#1232)
3780f5646f is described below
commit 3780f5646f18b655f0002d213ad16280ff93392b
Author: PJ Fanning <pj...@users.noreply.github.com>
AuthorDate: Mon Aug 31 17:42:48 2026 +0100
Fix Biff8DecryptingStream.readFully to honour len on the never-encrypted path (#1232)
readFully(buf, off, len) passed buf.length instead of len to readPlain when
reading a never-encrypted record (BOF/InterfaceHdr/FilePass). With off > 0 this
over-runs the buffer (ArrayIndexOutOfBounds); with off == 0 and len < buf.length
it over-reads the stream and misaligns subsequent reads. Pass len, matching the
encrypted branch. Added regression tests.
Co-authored-by: Claude Fable 5 <no...@anthropic.com>
---
.../hssf/record/crypto/Biff8DecryptingStream.java | 2 +-
.../poi/poifs/crypt/TestBiff8DecryptingStream.java | 64 ++++++++++++++++++++++
2 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java b/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java
index 567e75f189..139ddec727 100644
--- a/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java
+++ b/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java
@@ -111,7 +111,7 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
@Override
public void readFully(byte[] buf, int off, int len) {
if (shouldSkipEncryptionOnCurrentRecord) {
- readPlain(buf, off, buf.length);
+ readPlain(buf, off, len);
} else {
ccis.readFully(buf, off, len);
}
diff --git a/poi/src/test/java/org/apache/poi/poifs/crypt/TestBiff8DecryptingStream.java b/poi/src/test/java/org/apache/poi/poifs/crypt/TestBiff8DecryptingStream.java
index ea587d88f8..1f78061a74 100644
--- a/poi/src/test/java/org/apache/poi/poifs/crypt/TestBiff8DecryptingStream.java
+++ b/poi/src/test/java/org/apache/poi/poifs/crypt/TestBiff8DecryptingStream.java
@@ -25,6 +25,7 @@ import java.io.InputStream;
import javax.crypto.spec.SecretKeySpec;
+import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
import org.apache.poi.util.HexRead;
import org.junit.jupiter.api.Test;
@@ -208,4 +209,67 @@ final class TestBiff8DecryptingStream {
private static StreamTester createStreamTester() {
return new StreamTester(new MockStream(0x50), "BA AD F0 0D 00", 0x96C66829);
}
+
+ /**
+ * A mock stream whose first bytes are a fixed prefix, followed by the same
+ * incrementing pattern as {@link MockStream} starting at 0x50.
+ */
+ private static final class PrefixedMockStream extends InputStream {
+ private final byte[] _prefix;
+ private int _position;
+
+ PrefixedMockStream(byte[] prefix) {
+ _prefix = prefix;
+ }
+
+ @Override
+ public int read() {
+ int p = _position++;
+ return (p < _prefix.length ? _prefix[p] : 0x50 + (p - _prefix.length)) & 0xFF;
+ }
+ }
+
+ private static Biff8DecryptingStream createBDS(InputStream ms) {
+ EncryptionInfo ei = new EncryptionInfo(EncryptionMode.binaryRC4);
+ Decryptor dec = ei.getDecryptor();
+ dec.setSecretKey(new SecretKeySpec(HexRead.readFromString("BA AD F0 0D 00"), "RC4"));
+ return new Biff8DecryptingStream(ms, 0, ei);
+ }
+
+ /**
+ * On a never-encrypted record, readFully(buf, off, len) must read exactly {@code len} bytes
+ * at {@code off} - not {@code buf.length}. With a non-zero offset the old code passed
+ * buf.length and over-ran the buffer.
+ */
+ @Test
+ void readFullyWithOffsetOnNeverEncryptedRecord() {
+ // SID 0x0809 (BOF) is a never-encrypted record, so subsequent reads use the plain path
+ Biff8DecryptingStream bds = createBDS(new PrefixedMockStream(new byte[]{0x09, 0x08}));
+ assertEquals(BOFRecord.sid, bds.readRecordSID());
+ assertEquals(2, bds.getPosition());
+
+ byte[] buf = new byte[5];
+ bds.readFully(buf, 2, 3);
+
+ // exactly 3 bytes read into buf[2..5); the offset region is untouched
+ assertArrayEquals(new byte[]{0, 0, 0x50, 0x51, 0x52}, buf);
+ assertEquals(5, bds.getPosition());
+ }
+
+ /**
+ * Same record type, offset 0 but len < buf.length: the old code read buf.length bytes
+ * (over-reading the stream) rather than len.
+ */
+ @Test
+ void readFullyShorterThanBufferOnNeverEncryptedRecord() {
+ Biff8DecryptingStream bds = createBDS(new PrefixedMockStream(new byte[]{0x09, 0x08}));
+ assertEquals(BOFRecord.sid, bds.readRecordSID());
+
+ byte[] buf = new byte[5];
+ bds.readFully(buf, 0, 3);
+
+ // only 3 bytes consumed, so position is 2 (SID) + 3, not 2 + 5
+ assertArrayEquals(new byte[]{0x50, 0x51, 0x52, 0, 0}, buf);
+ assertEquals(5, bds.getPosition());
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org