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
10 changes: 6 additions & 4 deletions src/main/java/org/verapdf/cos/COSDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.verapdf.cos.visitor.Writer;
import org.verapdf.cos.xref.COSXRefTable;
import org.verapdf.exceptions.LoopedException;
import org.verapdf.exceptions.ResourceLimitExceededException;
import org.verapdf.exceptions.VeraPDFParserException;
import org.verapdf.io.IReader;
import org.verapdf.io.InternalInputStream;
Expand Down Expand Up @@ -143,9 +144,10 @@ public void setHeader(String header) {
* Sets an upper bound on the number of indirect objects the document may declare in its
* cross-reference table. Enumerating the objects materialises one entry per key, so a document that
* declares an extreme number of objects (a small compressed input can, via an object stream) can
* exhaust the heap; with a bound set it is rejected with a {@link VeraPDFParserException} before the
* objects are materialised, instead of failing with an OutOfMemoryError. A negative value (the
* default) removes the bound, keeping the historical behaviour.
* exhaust the heap; with a bound set it is rejected with a {@link ResourceLimitExceededException}
* (a {@link VeraPDFParserException} subclass, so existing catches keep working) before the objects
* are materialised, instead of failing with an OutOfMemoryError. A negative value (the default)
* removes the bound, keeping the historical behaviour.
*
* @param max maximum number of indirect objects, or a negative value for no bound
*/
Expand All @@ -164,7 +166,7 @@ private void checkObjectCountLimit() {
if (maxNumberOfObjects >= 0) {
int declared = this.xref.getAllKeys().size();
if (declared > maxNumberOfObjects) {
throw new VeraPDFParserException("Number of indirect objects (" + declared
throw new ResourceLimitExceededException("Number of indirect objects (" + declared
+ ") exceeds the configured maximum of " + maxNumberOfObjects);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* This file is part of veraPDF Parser, a module of the veraPDF project.
* Copyright (c) 2015-2026, veraPDF Consortium <info@verapdf.org>
* All rights reserved.
*
* veraPDF Parser is free software: you can redistribute it and/or modify
* it under the terms of either:
*
* The GNU General public license GPLv3+.
* You should have received a copy of the GNU General Public License
* along with veraPDF Parser as the LICENSE.GPL file in the root of the source
* tree. If not, see http://www.gnu.org/licenses/ or
* https://www.gnu.org/licenses/gpl-3.0.en.html.
*
* The Mozilla Public License MPLv2+.
* You should have received a copy of the Mozilla Public License along with
* veraPDF Parser as the LICENSE.MPL file in the root of the source tree.
* If a copy of the MPL was not distributed with this file, you can obtain one at
* http://mozilla.org/MPL/2.0/.
*/
package org.verapdf.exceptions;

/**
* Thrown when parsing is aborted because a configured resource limit is exceeded, as opposed to the
* input being malformed. It is a subclass of {@link VeraPDFParserException}, so existing callers that
* catch the parser exception keep working unchanged; a caller that needs to tell a deliberate
* resource-limit abort (for example the caps set through {@code SeekableInputStream.setMaxStreamSize}
* or {@code COSDocument.setMaxNumberOfObjects}) apart from a genuine parse error can catch this type
* instead of matching on the message text.
*
* @author veraPDF Consortium
*/
public class ResourceLimitExceededException extends VeraPDFParserException {

public ResourceLimitExceededException() {
}

public ResourceLimitExceededException(String message) {
super(message);
}

public ResourceLimitExceededException(String message, Throwable cause) {
super(message, cause);
}

public ResourceLimitExceededException(Throwable cause) {
super(cause);
}
}
5 changes: 3 additions & 2 deletions src/main/java/org/verapdf/io/InternalInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import org.verapdf.as.filters.io.ASBufferedInFilter;
import org.verapdf.as.io.ASInputStream;
import org.verapdf.exceptions.ResourceLimitExceededException;
import org.verapdf.exceptions.VeraPDFParserException;
import org.verapdf.tools.IntReference;

Expand Down Expand Up @@ -284,7 +285,7 @@ public long getStreamLength() throws IOException {

private static File createTempFile(byte[] alreadyRead, InputStream input, Integer maxStreamSize) throws IOException {
if (maxStreamSize != null && alreadyRead.length > maxStreamSize) {
throw new VeraPDFParserException("Maximum allowed stream size exceeded");
throw new ResourceLimitExceededException("Maximum allowed stream size exceeded");
}
File tmpFile = TempFileHandler.createTempFile("tmp_pdf_file", ".pdf");
try (FileOutputStream output = new FileOutputStream(tmpFile)) {
Expand All @@ -297,7 +298,7 @@ private static File createTempFile(byte[] alreadyRead, InputStream input, Intege
while ((n = input.read(buffer, 0, ASBufferedInFilter.BF_BUFFER_SIZE)) != -1) {
totalRead += n;
if (maxStreamSize != null && totalRead > maxStreamSize) {
throw new VeraPDFParserException("Maximum allowed stream size exceeded");
throw new ResourceLimitExceededException("Maximum allowed stream size exceeded");
}
output.write(buffer, 0, n);
}
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/verapdf/io/SeekableInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import org.verapdf.as.filters.io.ASBufferedInFilter;
import org.verapdf.as.io.ASInputStream;
import org.verapdf.as.io.ASMemoryInStream;
import org.verapdf.exceptions.VeraPDFParserException;
import org.verapdf.exceptions.ResourceLimitExceededException;
import org.verapdf.parser.BaseParserInputStream;

import java.io.IOException;
Expand All @@ -44,7 +44,7 @@ public abstract class SeekableInputStream extends ASInputStream implements BaseP
* Optional hard cap, in bytes, on the size of a single stream that is spilled to a temporary file
* ({@code null} means no cap). It guards against a small input whose decoded content expands without
* bound (a "decompression bomb"): a stream that exceeds the cap is rejected with a
* {@link org.verapdf.exceptions.VeraPDFParserException} instead of being written out in full. The
* {@link org.verapdf.exceptions.ResourceLimitExceededException} instead of being written out in full. The
* limit is wired into {@link #getSeekableStream(InputStream)}, which is the path taken by decoded
* object streams and other non-seekable input. Default {@code null}, so behaviour is unchanged.
*/
Expand Down Expand Up @@ -237,7 +237,7 @@ public static SeekableInputStream getSeekableStream(InputStream stream, Integer
totalRead += read;
}
if (maxStreamSize != null && totalRead > maxStreamSize) {
throw new VeraPDFParserException("Maximum allowed stream size exceeded");
throw new ResourceLimitExceededException("Maximum allowed stream size exceeded");
}
return InternalInputStream.createConcatenated(buffer, stream, maxStreamSize);
}
Expand Down
75 changes: 75 additions & 0 deletions src/test/java/org/verapdf/cos/COSDocumentObjectLimitTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* This file is part of veraPDF Parser, a module of the veraPDF project.
* Copyright (c) 2015-2026, veraPDF Consortium <info@verapdf.org>
* All rights reserved.
*
* veraPDF Parser is free software: you can redistribute it and/or modify
* it under the terms of either:
*
* The GNU General public license GPLv3+.
* You should have received a copy of the GNU General Public License
* along with veraPDF Parser as the LICENSE.GPL file in the root of the source
* tree. If not, see http://www.gnu.org/licenses/ or
* https://www.gnu.org/licenses/gpl-3.0.en.html.
*
* The Mozilla Public License MPLv2+.
* You should have received a copy of the Mozilla Public License along with
* veraPDF Parser as the LICENSE.MPL file in the root of the source tree.
* If a copy of the MPL was not distributed with this file, you can obtain one at
* http://mozilla.org/MPL/2.0/.
*/
package org.verapdf.cos;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.verapdf.as.ASAtom;
import org.verapdf.exceptions.ResourceLimitExceededException;
import org.verapdf.pd.PDDocument;

import java.io.IOException;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;

public class COSDocumentObjectLimitTest {

private static final String FILE_PATH = "src/test/resources/org/verapdf/cos/filters/validDocument.pdf";

private int previousLimit;

@Before
public void rememberLimit() {
previousLimit = COSDocument.getMaxNumberOfObjects();
}

@After
public void restoreLimit() {
COSDocument.setMaxNumberOfObjects(previousLimit);
}

@Test
public void shouldEnumerateObjectsWhenTheLimitIsNotExceeded() throws IOException {
PDDocument document = new PDDocument(FILE_PATH);
try {
COSDocument.setMaxNumberOfObjects(Integer.MAX_VALUE);
assertFalse(document.getDocument().getObjects().isEmpty());
} finally {
document.close();
}
}

@Test
public void shouldThrowResourceLimitExceededExceptionWhenTheLimitIsExceeded() throws IOException {
PDDocument document = new PDDocument(FILE_PATH);
try {
COSDocument cosDocument = document.getDocument();
COSDocument.setMaxNumberOfObjects(1);
assertThrows(ResourceLimitExceededException.class, cosDocument::getObjects);
assertThrows(ResourceLimitExceededException.class, cosDocument::getObjectsMap);
assertThrows(ResourceLimitExceededException.class, () -> cosDocument.getObjectsByType(ASAtom.PAGE));
} finally {
document.close();
}
}
}
22 changes: 11 additions & 11 deletions src/test/java/org/verapdf/io/InternalInputStreamTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.verapdf.exceptions.VeraPDFParserException;
import org.verapdf.exceptions.ResourceLimitExceededException;

public class InternalInputStreamTest {
@Rule
Expand Down Expand Up @@ -273,8 +273,8 @@ public void shouldNotThrowExceptionWhenSumOfInputsIsNotLargerThanMaxSize() throw
public void shouldThrowExceptionWhenAlreadyReadIsLargerThanMaxSizeAndStreamIsEmpty() {
byte[] alreadyRead = "abc".getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream("".getBytes());
VeraPDFParserException thrown = assertThrows(
VeraPDFParserException.class,
ResourceLimitExceededException thrown = assertThrows(
ResourceLimitExceededException.class,
() -> InternalInputStream.createConcatenated(alreadyRead, stream, 2)
);
assertTrue(thrown.getMessage().contains("size exceeded"));
Expand All @@ -284,8 +284,8 @@ public void shouldThrowExceptionWhenAlreadyReadIsLargerThanMaxSizeAndStreamIsEmp
public void shouldThrowExceptionWhenAlreadyReadIsLargerThanMaxSizeAndStreamIsNotEmpty() {
byte[] alreadyRead = "abc".getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream("1".getBytes());
VeraPDFParserException thrown = assertThrows(
VeraPDFParserException.class,
ResourceLimitExceededException thrown = assertThrows(
ResourceLimitExceededException.class,
() -> InternalInputStream.createConcatenated(alreadyRead, stream, 2)
);
assertTrue(thrown.getMessage().contains("size exceeded"));
Expand All @@ -295,8 +295,8 @@ public void shouldThrowExceptionWhenAlreadyReadIsLargerThanMaxSizeAndStreamIsNot
public void shouldThrowExceptionWhenStreamIsLargerThanMaxSizeAndAlreadyReadIsEmpty() {
byte[] alreadyRead = "".getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes());
VeraPDFParserException thrown = assertThrows(
VeraPDFParserException.class,
ResourceLimitExceededException thrown = assertThrows(
ResourceLimitExceededException.class,
() -> InternalInputStream.createConcatenated(alreadyRead, stream, 2)
);
assertTrue(thrown.getMessage().contains("size exceeded"));
Expand All @@ -306,8 +306,8 @@ public void shouldThrowExceptionWhenStreamIsLargerThanMaxSizeAndAlreadyReadIsEmp
public void shouldThrowExceptionWhenStreamIsLargerThanMaxSizeAndAlreadyReadIsNotEmpty() {
byte[] alreadyRead = "1".getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes());
VeraPDFParserException thrown = assertThrows(
VeraPDFParserException.class,
ResourceLimitExceededException thrown = assertThrows(
ResourceLimitExceededException.class,
() -> InternalInputStream.createConcatenated(alreadyRead, stream, 2)
);
assertTrue(thrown.getMessage().contains("size exceeded"));
Expand All @@ -317,8 +317,8 @@ public void shouldThrowExceptionWhenStreamIsLargerThanMaxSizeAndAlreadyReadIsNot
public void shouldThrowExceptionWhenSumOfAlreadyReadAndStreamIsLargerThanMaxSize() {
byte[] alreadyRead = "ab".getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream("cd".getBytes());
VeraPDFParserException thrown = assertThrows(
VeraPDFParserException.class,
ResourceLimitExceededException thrown = assertThrows(
ResourceLimitExceededException.class,
() -> InternalInputStream.createConcatenated(alreadyRead, stream, 2)
);
assertTrue(thrown.getMessage().contains("size exceeded"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.junit.runners.Parameterized.Parameters;
import org.verapdf.as.io.ASInputStream;
import org.verapdf.as.io.ASMemoryInStream;
import org.verapdf.exceptions.VeraPDFParserException;
import org.verapdf.exceptions.ResourceLimitExceededException;

import java.io.ByteArrayInputStream;
import java.io.File;
Expand Down Expand Up @@ -398,7 +398,7 @@ public void shouldNotThrowExceptionsAboutExceedingMaxSizeWhenNotExeedingMaxSize(

@Test
public void shouldThrowExceptionsAboutExceedingMaxSizeWhenExeedingMaxSize() {
assertThrows(VeraPDFParserException.class, () -> SeekableInputStream.getSeekableStream(input, size - 1));
assertThrows(ResourceLimitExceededException.class, () -> SeekableInputStream.getSeekableStream(input, size - 1));
}

@Test
Expand Down
Loading