Skip to content

Commit 7d93ef8

Browse files
authored
Filter classes accepted by TransformUpgrader deserialization (apache#40438)
1 parent ea27faf commit 7d93ef8

3 files changed

Lines changed: 73 additions & 1 deletion

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"comment": "Modify this file in a trivial way to cause this test suite to run",
3-
"revision": 5,
3+
"revision": 2,
44
"https://github.com/apache/beam/pull/39990": "removing dead code from FnApiDoFnRunner"
55
}

‎sdks/java/core/src/main/java/org/apache/beam/sdk/util/construction/TransformUpgrader.java‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.ByteArrayOutputStream;
2525
import java.io.IOException;
2626
import java.io.InvalidClassException;
27+
import java.io.ObjectInputFilter;
2728
import java.io.ObjectInputStream;
2829
import java.io.ObjectOutputStream;
2930
import java.net.ServerSocket;
@@ -69,6 +70,29 @@ public class TransformUpgrader implements AutoCloseable {
6970
private static final Logger LOG = LoggerFactory.getLogger(TransformUpgrader.class);
7071
private static final String UPGRADE_NAMESPACE = "transform:upgrade:";
7172

73+
public static final String DESERIALIZATION_FILTER_PROPERTY =
74+
"beam.transformUpgrader.deserializationFilter";
75+
76+
private static final String DEFAULT_DESERIALIZATION_FILTER_PATTERN =
77+
"maxdepth=100;maxrefs=10000;maxbytes=10485760;maxarray=10000;"
78+
+ "org.apache.beam.**;"
79+
+ "com.google.**;"
80+
+ "org.apache.kafka.**;"
81+
+ "org.apache.avro.**;"
82+
+ "org.joda.time.**;"
83+
+ "!java.lang.ProcessBuilder;"
84+
+ "!java.lang.Runtime;"
85+
+ "!java.lang.Thread;"
86+
+ "java.lang.**;"
87+
+ "java.util.**;"
88+
+ "java.io.**;"
89+
+ "java.math.**;"
90+
+ "java.time.**;"
91+
+ "!*";
92+
93+
private static final ObjectInputFilter DEFAULT_DESERIALIZATION_FILTER =
94+
ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER_PATTERN);
95+
7296
@VisibleForTesting static final String UPGRADE_KEY = "upgraded_to_version";
7397

7498
private ExpansionServiceClientFactory clientFactory;
@@ -463,12 +487,21 @@ public static byte[] toByteArray(Object object) {
463487
* A utility method that converts a byte array obtained by invoking {@link #toByteArray(Object)}
464488
* back to a Java object.
465489
*
490+
* <p>Applies a deserialization filter with resource limits and a package allowlist. The filter
491+
* pattern can be overridden via the {@link #DESERIALIZATION_FILTER_PROPERTY} system property.
492+
*
466493
* @param bytes an array of bytes generated by invoking the {@link #toByteArray(Object)} method.
467494
* @return re-generated object.
468495
*/
469496
public static Object fromByteArray(byte[] bytes) throws InvalidClassException {
470497
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
471498
ObjectInputStream in = new ObjectInputStream(bis)) {
499+
String customPattern = System.getProperty(DESERIALIZATION_FILTER_PROPERTY);
500+
ObjectInputFilter filter =
501+
(customPattern != null && !customPattern.isEmpty())
502+
? ObjectInputFilter.Config.createFilter(customPattern)
503+
: DEFAULT_DESERIALIZATION_FILTER;
504+
in.setObjectInputFilter(filter);
472505
return in.readObject();
473506
} catch (InvalidClassException e) {
474507
LOG.info(

‎sdks/java/core/src/test/java/org/apache/beam/sdk/util/construction/TransformUpgraderTest.java‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
package org.apache.beam.sdk.util.construction;
1919

2020
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertThrows;
2122
import static org.junit.Assert.assertTrue;
2223

2324
import com.google.auto.service.AutoService;
2425
import java.io.ByteArrayInputStream;
2526
import java.io.IOException;
27+
import java.io.InvalidClassException;
2628
import java.io.ObjectInputStream;
2729
import java.io.ObjectOutputStream;
30+
import java.net.URI;
2831
import java.nio.charset.Charset;
2932
import java.util.Collections;
3033
import java.util.List;
@@ -487,4 +490,40 @@ public void testVersionComparison() throws Exception {
487490
assertTrue(TransformUpgrader.compareVersions("2.55.0-SNAPSHOT", "2.53.0") > 0);
488491
assertTrue(TransformUpgrader.compareVersions("2.55.0.dev", "2.53.0") > 0);
489492
}
493+
494+
@Test
495+
public void testFromByteArrayAllowedAndBlockedClasses() throws Exception {
496+
byte[] stringBytes = TransformUpgrader.toByteArray("hello");
497+
assertEquals("hello", TransformUpgrader.fromByteArray(stringBytes));
498+
499+
byte[] beamObjectBytes = TransformUpgrader.toByteArray(new TestTransform(5));
500+
TestTransform deserializedTransform =
501+
(TestTransform) TransformUpgrader.fromByteArray(beamObjectBytes);
502+
assertEquals(Integer.valueOf(5), deserializedTransform.getTestParam());
503+
504+
byte[] disallowedPackageBytes = TransformUpgrader.toByteArray(URI.create("http://example.com"));
505+
assertThrows(
506+
InvalidClassException.class, () -> TransformUpgrader.fromByteArray(disallowedPackageBytes));
507+
508+
byte[] disallowedClassBytes = TransformUpgrader.toByteArray(ProcessBuilder.class);
509+
assertThrows(
510+
InvalidClassException.class, () -> TransformUpgrader.fromByteArray(disallowedClassBytes));
511+
}
512+
513+
@Test
514+
public void testFromByteArrayCustomFilterProperty() throws Exception {
515+
URI uri = URI.create("http://example.com");
516+
byte[] uriBytes = TransformUpgrader.toByteArray(uri);
517+
byte[] largeArrayBytes = TransformUpgrader.toByteArray(new byte[100]);
518+
519+
try {
520+
System.setProperty(
521+
TransformUpgrader.DESERIALIZATION_FILTER_PROPERTY, "maxarray=10;java.net.**;!*");
522+
assertEquals(uri, TransformUpgrader.fromByteArray(uriBytes));
523+
assertThrows(
524+
InvalidClassException.class, () -> TransformUpgrader.fromByteArray(largeArrayBytes));
525+
} finally {
526+
System.clearProperty(TransformUpgrader.DESERIALIZATION_FILTER_PROPERTY);
527+
}
528+
}
490529
}

0 commit comments

Comments
 (0)