2323import java .io .InvalidClassException ;
2424import java .io .ObjectInputStream ;
2525import java .io .ObjectStreamClass ;
26+ import java .lang .annotation .Annotation ;
2627import java .util .regex .Pattern ;
2728
2829import org .apache .commons .io .build .AbstractStreamBuilder ;
@@ -145,6 +146,8 @@ public static class Builder extends AbstractStreamBuilder<ValidatingObjectInputS
145146
146147 private ObjectStreamClassPredicate predicate = new ObjectStreamClassPredicate ();
147148
149+ private boolean strict ;
150+
148151 /**
149152 * Constructs a new builder of {@link ValidatingObjectInputStream}.
150153 *
@@ -157,6 +160,9 @@ public Builder() {
157160
158161 /**
159162 * Accepts the specified classes for deserialization, unless they are otherwise rejected.
163+ * <p>
164+ * The reject list takes precedence over the accept list.
165+ * </p>
160166 *
161167 * @param classes Classes to accept.
162168 * @return this object.
@@ -169,6 +175,9 @@ public Builder accept(final Class<?>... classes) {
169175
170176 /**
171177 * Accepts class names where the supplied ClassNameMatcher matches for deserialization, unless they are otherwise rejected.
178+ * <p>
179+ * The reject list takes precedence over the accept list.
180+ * </p>
172181 *
173182 * @param matcher a class name matcher to <em>accept</em> objects.
174183 * @return {@code this} instance.
@@ -181,6 +190,9 @@ public Builder accept(final ClassNameMatcher matcher) {
181190
182191 /**
183192 * Accepts class names that match the supplied pattern for deserialization, unless they are otherwise rejected.
193+ * <p>
194+ * The reject list takes precedence over the accept list.
195+ * </p>
184196 *
185197 * @param pattern a Pattern for compiled regular expression.
186198 * @return {@code this} instance.
@@ -193,6 +205,9 @@ public Builder accept(final Pattern pattern) {
193205
194206 /**
195207 * Accepts the wildcard specified classes for deserialization, unless they are otherwise rejected.
208+ * <p>
209+ * The reject list takes precedence over the accept list.
210+ * </p>
196211 *
197212 * @param patterns Wildcard file name patterns as defined by {@link org.apache.commons.io.FilenameUtils#wildcardMatch(String, String)
198213 * FilenameUtils.wildcardMatch}
@@ -207,6 +222,9 @@ public Builder accept(final String... patterns) {
207222 /**
208223 * Builds a new {@link ValidatingObjectInputStream}.
209224 * <p>
225+ * The reject list takes precedence over the accept list.
226+ * </p>
227+ * <p>
210228 * You must set an aspect that supports {@link #getInputStream()} on this builder, otherwise, this method throws an exception.
211229 * </p>
212230 * <p>
@@ -242,6 +260,9 @@ public ObjectStreamClassPredicate getPredicate() {
242260
243261 /**
244262 * Rejects the specified classes for deserialization, even if they are otherwise accepted.
263+ * <p>
264+ * The reject list takes precedence over the accept list.
265+ * </p>
245266 *
246267 * @param classes Classes to reject.
247268 * @return {@code this} instance.
@@ -254,6 +275,9 @@ public Builder reject(final Class<?>... classes) {
254275
255276 /**
256277 * Rejects class names where the supplied ClassNameMatcher matches for deserialization, even if they are otherwise accepted.
278+ * <p>
279+ * The reject list takes precedence over the accept list.
280+ * </p>
257281 *
258282 * @param matcher the matcher to use.
259283 * @return {@code this} instance.
@@ -266,6 +290,9 @@ public Builder reject(final ClassNameMatcher matcher) {
266290
267291 /**
268292 * Rejects class names that match the supplied pattern for deserialization, even if they are otherwise accepted.
293+ * <p>
294+ * The reject list takes precedence over the accept list.
295+ * </p>
269296 *
270297 * @param pattern standard Java regexp.
271298 * @return {@code this} instance.
@@ -278,6 +305,9 @@ public Builder reject(final Pattern pattern) {
278305
279306 /**
280307 * Rejects the wildcard specified classes for deserialization, even if they are otherwise accepted.
308+ * <p>
309+ * The reject list takes precedence over the accept list.
310+ * </p>
281311 *
282312 * @param patterns Wildcard file name patterns as defined by {@link org.apache.commons.io.FilenameUtils#wildcardMatch(String, String)
283313 * FilenameUtils.wildcardMatch}
@@ -291,6 +321,9 @@ public Builder reject(final String... patterns) {
291321
292322 /**
293323 * Sets the predicate, null resets to an empty new ObjectStreamClassPredicate.
324+ * <p>
325+ * The reject list takes precedence over the accept list.
326+ * </p>
294327 *
295328 * @param predicate the predicate.
296329 * @return {@code this} instance.
@@ -301,10 +334,39 @@ public Builder setPredicate(final ObjectStreamClassPredicate predicate) {
301334 return this ;
302335 }
303336
337+ /**
338+ * If true, checks that all interfaces and annotations for a given type are not rejected and accepted.
339+ * <p>
340+ * For compatibility with previous versions, this is false by default.
341+ * </p>
342+ * <p>
343+ * Checks:
344+ * </p>
345+ * <ol>
346+ * <li>The type name.</li>
347+ * <li>The interfaces implemented by the class, recursively.</li>
348+ * <li>The annotation on this type, recursively.</li>
349+ * </ol>
350+ * <p>
351+ * The reject list takes precedence over the accept list.
352+ * </p>
353+ *
354+ * @param strict If true, checks that all interfaces and annotations for a given type are not rejected and accepted.
355+ * @return {@code this} instance.
356+ * @since 2.23.0
357+ */
358+ public Builder setStrict (final boolean strict ) {
359+ this .strict = strict ;
360+ return this ;
361+ }
362+
304363 }
305364
306365 /**
307366 * Constructs a new {@link Builder}.
367+ * <p>
368+ * The reject list takes precedence over the accept list.
369+ * </p>
308370 *
309371 * @return a new {@link Builder}.
310372 * @since 2.18.0
@@ -314,36 +376,38 @@ public static Builder builder() {
314376 }
315377
316378 private final ObjectStreamClassPredicate predicate ;
317-
318- @ SuppressWarnings ("resource" ) // caller closes/
319- private ValidatingObjectInputStream (final Builder builder ) throws IOException {
320- this (builder .getInputStream (), builder .predicate );
321- }
379+ private final boolean strict ;
322380
323381 /**
324382 * Constructs an instance to deserialize the specified input stream. At least one accept method needs to be called to specify which classes can be
325383 * deserialized, as by default no classes are accepted.
384+ * <p>
385+ * The reject list takes precedence over the accept list.
386+ * </p>
326387 *
327- * @param input an input stream.
328- * @throws IOException if an I/O error occurs while reading stream header.
329- * @deprecated Use {@link #builder()}.
388+ * @param builder The builder.
330389 */
331- @ Deprecated
332- public ValidatingObjectInputStream (final InputStream input ) throws IOException {
333- this (input , new ObjectStreamClassPredicate ());
390+ @ SuppressWarnings ("resource" ) // caller closes
391+ private ValidatingObjectInputStream (final Builder builder ) throws IOException {
392+ super (builder .getInputStream ());
393+ this .predicate = builder .predicate ;
394+ this .strict = builder .strict ;
334395 }
335396
336397 /**
337398 * Constructs an instance to deserialize the specified input stream. At least one accept method needs to be called to specify which classes can be
338399 * deserialized, as by default no classes are accepted.
400+ * <p>
401+ * The reject list takes precedence over the accept list.
402+ * </p>
339403 *
340- * @param input an input stream.
341- * @param predicate how to accept and reject classes.
404+ * @param input an input stream.
342405 * @throws IOException if an I/O error occurs while reading stream header.
406+ * @deprecated Use {@link #builder()}.
343407 */
344- private ValidatingObjectInputStream ( final InputStream input , final ObjectStreamClassPredicate predicate ) throws IOException {
345- super ( input );
346- this . predicate = predicate ;
408+ @ Deprecated
409+ public ValidatingObjectInputStream ( final InputStream input ) throws IOException {
410+ this ( builder (). setInputStream ( input ). setPredicate ( new ObjectStreamClassPredicate ())) ;
347411 }
348412
349413 /**
@@ -404,15 +468,38 @@ public ValidatingObjectInputStream accept(final String... patterns) {
404468 }
405469
406470 /**
407- * Checks that the class name conforms to requirements.
471+ * Checks that the given type can be legally resolved as configured.
472+ * <p>
473+ * Checks:
474+ * <ol>
475+ * <li>The type name.</li>
476+ * <li>The interfaces implemented by the class, recursively.</li>
477+ * <li>The annotation on this type, recursively.</li>
478+ * </ol>
479+ *
480+ * @param result
481+ * @throws InvalidClassException
482+ */
483+ private void check (final Class <?> result ) throws InvalidClassException {
484+ checkTypeName (result .getName ());
485+ for (final Class <?> iface : result .getInterfaces ()) {
486+ check (iface );
487+ }
488+ for (final Annotation annotation : result .getAnnotations ()) {
489+ check (annotation .annotationType ());
490+ }
491+ }
492+
493+ /**
494+ * Checks that the type name conforms to requirements.
408495 * <p>
409496 * The reject list takes precedence over the accept list.
410497 * </p>
411498 *
412- * @param name The class name to test.
499+ * @param name The type name to test.
413500 * @throws InvalidClassException Thrown when a rejected or non-accepted class is found.
414501 */
415- private void checkClassName (final String name ) throws InvalidClassException {
502+ private void checkTypeName (final String name ) throws InvalidClassException {
416503 if (!predicate .test (name )) {
417504 invalidClassNameFound (name );
418505 }
@@ -509,8 +596,14 @@ public ValidatingObjectInputStream reject(final String... patterns) {
509596 */
510597 @ Override
511598 protected Class <?> resolveClass (final ObjectStreamClass osc ) throws IOException , ClassNotFoundException {
512- checkClassName (osc .getName ());
513- return super .resolveClass (osc );
599+ checkTypeName (osc .getName ());
600+ // resolveClass() calls Class.forName(String, boolean, ClassLoader) with initialize set to false.
601+ // The result Class is therefore not initialized.
602+ final Class <?> result = super .resolveClass (osc );
603+ if (strict ) {
604+ check (result );
605+ }
606+ return result ;
514607 }
515608
516609 /**
@@ -522,7 +615,7 @@ protected Class<?> resolveClass(final ObjectStreamClass osc) throws IOException,
522615 @ Override
523616 protected Class <?> resolveProxyClass (final String [] interfaces ) throws IOException , ClassNotFoundException {
524617 for (final String interfaceName : interfaces ) {
525- checkClassName (interfaceName );
618+ checkTypeName (interfaceName );
526619 }
527620 return super .resolveProxyClass (interfaces );
528621 }
0 commit comments