Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ Licensed to the Apache Software Foundation (ASF) under one or more

import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.RecordFormatException;

public class HwmfPlaceableHeader {
public static final int WMF_HEADER_MAGIC = 0x9AC6CDD7;

final Rectangle2D bounds;
final int unitsPerInch;

protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException {
protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException, RecordFormatException {
/*
* HWmf (2 bytes): The resource handle to the metafile, when the metafile is in memory. When
* the metafile is on disk, this field MUST contain 0x0000. This attribute of the metafile is
Expand All @@ -54,7 +55,10 @@ protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException {
* Thus, a value of 720 specifies that the image SHOULD be rendered at twice its normal size,
* and a value of 2880 specifies that the image SHOULD be rendered at half its normal size.
*/
unitsPerInch = leis.readShort();
unitsPerInch = leis.readUShort();
if (unitsPerInch == 0) {
throw new RecordFormatException("Invalid WMF placeable header unitsPerInch: " + unitsPerInch);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add RecordFormatException to the throws clause

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve updated the constructor signature to explicitly declare RecordFormatException for consistency with the parsing code.

}

/*
* Reserved (4 bytes): A field that is not used and MUST be set to 0x00000000.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,29 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import static org.apache.poi.POITestCase.assertContains;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;

import org.apache.poi.POIDataSamples;
import org.apache.poi.hwmf.draw.HwmfImageRenderer;
import org.apache.poi.hwmf.record.HwmfFont;
import org.apache.poi.hwmf.record.HwmfPlaceableHeader;
import org.apache.poi.hwmf.record.HwmfRecord;
import org.apache.poi.hwmf.record.HwmfRecordType;
import org.apache.poi.hwmf.record.HwmfText;
import org.apache.poi.hwmf.usermodel.HwmfPicture;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.RecordFormatException;
Expand Down Expand Up @@ -166,4 +172,60 @@ void testLengths() {
long cnt = rebuilt.codePoints().count();
assertEquals(4, cnt);
}

@Test
void testRejectsZeroUnitsPerInchAtParserBoundary() {
byte[] malicious = createMinimalPlaceableWmf(0, 0, 0, 32767, 32767);
RecordFormatException ex = assertThrows(
RecordFormatException.class,
() -> new HwmfPicture(new ByteArrayInputStream(malicious))
);
assertContains(ex.getMessage(), "unitsPerInch");
}

@Test
void testValidUnitsProduceFiniteDimensions() throws IOException {
byte[] benign = createMinimalPlaceableWmf(1440, 0, 0, 100, 100);
HwmfImageRenderer renderer = new HwmfImageRenderer();
renderer.loadImage(benign, PictureData.PictureType.WMF.contentType);

assertTrue(Double.isFinite(renderer.getDimension().getWidth()));
assertTrue(Double.isFinite(renderer.getDimension().getHeight()));
assertTrue(renderer.getDimension().getWidth() > 0);
assertTrue(renderer.getDimension().getHeight() > 0);
}

/**
* Creates a minimal placeable WMF stream: placeable header + WMF header + EOF record.
* The payload is intentionally small because these tests target parser validation, not WMF drawing semantics.
*/
private static byte[] createMinimalPlaceableWmf(int unitsPerInch, int x1, int y1, int x2, int y2) {
ByteBuffer bb = ByteBuffer.allocate(46).order(ByteOrder.LITTLE_ENDIAN);

// Placeable header
bb.putInt(HwmfPlaceableHeader.WMF_HEADER_MAGIC);
bb.putShort((short) 0); // hwmf handle
bb.putShort((short) x1);
bb.putShort((short) y1);
bb.putShort((short) x2);
bb.putShort((short) y2);
bb.putShort((short) unitsPerInch);
bb.putInt(0); // reserved
bb.putShort((short) 0); // checksum

// WMF header (9 words)
bb.putShort((short) 1); // memory metafile
bb.putShort((short) 9); // header size in WORDs
bb.putShort((short) 0x0300); // version
bb.putInt(0); // file size in WORDs (unused by parser)
bb.putShort((short) 0); // number of objects
bb.putInt(0); // max record size
bb.putShort((short) 0); // number of members

// EOF record
bb.putInt(3); // record size in WORDs
bb.putShort((short) HwmfRecordType.eof.id);

return bb.array();
}
}