Skip to content
Open
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,6 +22,13 @@ org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

# Optional: restrict which classes JDBCJobStore will deserialize from the database
# (job data maps, triggers, calendars) using a JEP-290 look-ahead ObjectInputFilter.
# It is off by default; when enabled, only classes matching the pattern are allowed.
# Tailor the allow-list to the classes your jobs actually persist, then reject the
# rest with "!*". The value uses java.io.ObjectInputFilter pattern syntax, e.g.:
#org.quartz.jobStore.driverDelegateInitString=objectInputFilter=java.lang.*;java.util.*;org.quartz.**;com.myapp.jobs.**;!*
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.tablePrefix=QRTZ_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

# Optional: restrict which classes JDBCJobStore will deserialize from the database
# (job data maps, triggers, calendars) using a JEP-290 look-ahead ObjectInputFilter.
# It is off by default; when enabled, only classes matching the pattern are allowed.
# Tailor the allow-list to the classes your jobs actually persist, then reject the
# rest with "!*". The value uses java.io.ObjectInputFilter pattern syntax, e.g.:
#org.quartz.jobStore.driverDelegateInitString=objectInputFilter=java.lang.*;java.util.*;org.quartz.**;com.myapp.jobs.**;!*
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.tablePrefix=QRTZ_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)
if (bytes != null && bytes.length != 0) {
binaryInput = new ByteArrayInputStream(bytes);

try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);
}

return obj;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.sql.Blob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
Expand Down Expand Up @@ -80,9 +79,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName) throws ClassNot
} else if (binaryInput instanceof ByteArrayInputStream && ((ByteArrayInputStream) binaryInput).available() == 0 ) {
return null;
} else {
try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
return in.readObject();
}
return readObjectFromBinaryStream(binaryInput);
}
}
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.sql.ResultSet;
import java.sql.SQLException;

Expand Down Expand Up @@ -68,9 +67,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)
if(bytes != null && bytes.length != 0) {
binaryInput = new ByteArrayInputStream(bytes);

try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.sql.ResultSet;
import java.sql.SQLException;

Expand Down Expand Up @@ -67,9 +66,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)

Object obj;

try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);

return obj;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
Expand Down Expand Up @@ -74,9 +73,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)

Object obj;

try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);

return obj;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
Expand Down Expand Up @@ -410,9 +409,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)
InputStream binaryInput = new ByteArrayInputStream(binaryData);

if (binaryInput.available() != 0) {
try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);
}

return obj;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.sql.ResultSet;
import java.sql.SQLException;

Expand Down Expand Up @@ -66,9 +65,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)
if(bytes != null && bytes.length != 0) {
binaryInput = new ByteArrayInputStream(bytes);

try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.NotSerializableException;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
Expand Down Expand Up @@ -96,7 +97,16 @@ public class StdJDBCDelegate implements DriverDelegate, StdJDBCConstants {
protected String schedName;

protected boolean useProperties;


/**
* Optional look-ahead deserialization filter (JEP 290) applied to every object
* read back from a JDBCJobStore BLOB (job data map, trigger, calendar). Configured
* via the delegate init-string setting {@code objectInputFilter=<pattern>} (see
* {@link #initialize}). When {@code null} (the default), deserialization behavior
* is unchanged.
*/
protected ObjectInputFilter objectInputFilter;

protected ClassLoadHelper classLoadHelper;

protected final List<TriggerPersistenceDelegate> triggerPersistenceDelegates = new LinkedList<>();
Expand Down Expand Up @@ -148,7 +158,7 @@ public void initialize(Logger logger, String tablePrefix, String schedName, Stri
String[] settings = initString.split("\\|");

for(String setting: settings) {
String[] parts = setting.split("=");
String[] parts = setting.split("=", 2);
String name = parts[0];
if(parts.length == 1 || parts[1] == null || parts[1].isEmpty())
continue;
Expand All @@ -165,6 +175,9 @@ public void initialize(Logger logger, String tablePrefix, String schedName, Stri
}
}
}
else if(name.equals("objectInputFilter")) {
this.objectInputFilter = ObjectInputFilter.Config.createFilter(parts[1]);
}
else
throw new NoSuchDelegateException("Unknown setting: '" + name + "'");
}
Expand Down Expand Up @@ -3420,16 +3433,41 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)
&& ((ByteArrayInputStream) binaryInput).available() == 0 ) {
//do nothing
} else {
try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);
}
}

}
return obj;
}

/**
* Deserialize an object from the given binary stream, applying the configured
* {@link ObjectInputFilter} (if any) as a look-ahead deserialization guard
* (JEP 290). This is the single choke point through which every JDBCJobStore
* BLOB (job data map, trigger, calendar) is deserialized, so that a filter can
* be enforced consistently across the base delegate and every database-specific
* subclass rather than being duplicated (or omitted) per delegate. When no
* filter is configured, behavior is identical to a plain {@code ObjectInputStream}.
*
* @param binaryInput
* the stream containing the serialized object
* @return the deserialized Object
* @throws ClassNotFoundException
* if a class found during deserialization cannot be found
* @throws IOException
* if deserialization causes an error (including rejection by the filter)
*/
protected Object readObjectFromBinaryStream(InputStream binaryInput)
throws ClassNotFoundException, IOException {
try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
if (objectInputFilter != null) {
in.setObjectInputFilter(objectInputFilter);
}
return in.readObject();
}
}

/**
* <p>
* This method should be overridden by any delegate subclasses that need
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
Expand Down Expand Up @@ -69,9 +68,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)

Object obj;

try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);

return obj;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.sql.Blob;
import java.sql.ResultSet;
import java.sql.SQLException;
Expand Down Expand Up @@ -72,9 +71,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)
}

if (null != binaryInput) {
try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);
}

return obj;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Connection;
Expand Down Expand Up @@ -135,9 +134,7 @@ protected Object getObjectFromBlob(ResultSet rs, String colName)
Object obj = null;
InputStream binaryInput = rs.getBinaryStream(colName);
if (binaryInput != null) {
try (ObjectInputStream in = new ObjectInputStream(binaryInput)) {
obj = in.readObject();
}
obj = readObjectFromBinaryStream(binaryInput);
}

return obj;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.NotSerializableException;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
Expand Down Expand Up @@ -72,6 +78,49 @@ void testSerializeJobData() throws IOException, NoSuchDelegateException {
}
}

@Test
void testReadObjectFromBinaryStreamWithoutFilterIsBehaviorPreserving() throws Exception {
StdJDBCDelegate delegate = new StdJDBCDelegate();
delegate.initialize(LoggerFactory.getLogger(getClass()), "QRTZ_", "TESTSCHED", "INSTANCE", new SimpleClassLoadHelper(), false, "");

Map<String, Object> data = new HashMap<>();
data.put("key", "value");
data.put("count", 42);

Object read = delegate.readObjectFromBinaryStream(new ByteArrayInputStream(serialize(data)));
assertEquals(data, read);
}

@Test
void testReadObjectFromBinaryStreamAppliesConfiguredFilter() throws Exception {
StdJDBCDelegate delegate = new StdJDBCDelegate();
// Restrict deserialization to the JDK types a job-data map round-trips, rejecting anything else.
delegate.initialize(LoggerFactory.getLogger(getClass()), "QRTZ_", "TESTSCHED", "INSTANCE", new SimpleClassLoadHelper(), false,
"objectInputFilter=java.util.*;java.lang.*;!*");

// A legitimate, allow-listed payload still deserializes unchanged.
Map<String, Object> data = new HashMap<>();
data.put("key", "value");
assertEquals(data, delegate.readObjectFromBinaryStream(new ByteArrayInputStream(serialize(data))));

// A class outside the allow-list is rejected by the filter before it is constructed.
byte[] disallowed = serialize(new UnexpectedType());
assertThrows(InvalidClassException.class,
() -> delegate.readObjectFromBinaryStream(new ByteArrayInputStream(disallowed)));
}

private static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(obj);
}
return baos.toByteArray();
}

private static class UnexpectedType implements java.io.Serializable {
private static final long serialVersionUID = 1L;
}

@Test
void testSelectBlobTriggerWithNoBlobContent() throws JobPersistenceException, SQLException, IOException, ClassNotFoundException {
StdJDBCDelegate jdbcDelegate = new StdJDBCDelegate();
Expand Down