362 * the filter returns {@link Status#REJECTED Status.REJECTED}. 363 * If the class is an array type, the class to be matched is the element type. 364 * Arrays of any number of dimensions are treated the same as the element type. 365 * For example, a pattern of "{@code !example.Foo}", 366 * rejects creation of any instance or array of {@code example.Foo}. 367 * The first pattern that matches, working from left to right, determines 368 * the {@link Status#ALLOWED Status.ALLOWED} 369 * or {@link Status#REJECTED Status.REJECTED} result. 370 * If nothing matches, the result is {@link Status#UNDECIDED Status.UNDECIDED}. 371 * 372 * @param pattern the pattern string to parse; not null 373 * @return a filter to check a class being deserialized; may be null; 374 * {@code null} if no patterns 375 * @throws IllegalArgumentException 376 * if a limit is missing the name, or the long value 377 * is not a number or is negative, 378 * or if the package is missing for ".*" and ".**" 379 */ 380 public static ObjectInputFilter createFilter(String pattern) { 381 Objects.requireNonNull(pattern, "pattern"); 382 return Global.createFilter(pattern); 383 } 384 385 /** 386 * Implementation of ObjectInputFilter that performs the checks of 387 * the process-wide serialization filter. If configured, it will be 388 * used for all ObjectInputStreams that do not set their own filters. 389 * 390 */ 391 final static class Global implements ObjectInputFilter { 392 /** 393 * The pattern used to create the filter. 394 */ 395 private final String pattern; 396 /** 397 * The list of class filters. 398 */ 399 private final List<Function<Class<?>, Status>> filters; 400 /** 401 * Maximum allowed bytes in the stream. 402 */ 403 private long maxStreamBytes; 404 /** 405 * Maximum depth of the graph allowed. 406 */ 407 private long maxDepth; 408 /** 409 * Maximum number of references in a graph. 410 */ 411 private long maxReferences; 412 /** 413 * Maximum length of any array. 414 */ 415 private long maxArrayLength; 416 417 /** 418 * Returns an ObjectInputFilter from a string of patterns. 419 * 420 * @param pattern the pattern string to parse 421 * @return a filter to check a class being deserialized; not null 422 * @throws IllegalArgumentException if the parameter is malformed 423 * if the pattern is missing the name, the long value 424 * is not a number or is negative. 425 */ 426 static ObjectInputFilter createFilter(String pattern) { 427 Global filter = new Global(pattern); 428 return filter.isEmpty() ? null : filter; 429 } 430 431 /** 432 * Construct a new filter from the pattern String. 433 * 434 * @param pattern a pattern string of filters 435 * @throws IllegalArgumentException if the pattern is malformed 436 */ 437 private Global(String pattern) { 438 this.pattern = pattern; 439 440 maxArrayLength = Long.MAX_VALUE; // Default values are unlimited 441 maxDepth = Long.MAX_VALUE; 442 maxReferences = Long.MAX_VALUE; 443 maxStreamBytes = Long.MAX_VALUE; 444 445 String[] patterns = pattern.split(";"); 446 filters = new ArrayList<>(patterns.length); 447 for (int i = 0; i < patterns.length; i++) { 448 String p = patterns[i]; 449 int nameLen = p.length(); 450 if (nameLen == 0) { 451 continue; 452 } 453 if (parseLimit(p)) { 454 // If the pattern contained a limit setting, i.e. type=value 455 continue; 456 } 457 boolean negate = p.charAt(0) == '!'; 458 576 /** 577 * {@inheritDoc} 578 */ 579 @Override 580 public Status checkInput(FilterInfo filterInfo) { 581 if (filterInfo.references() < 0 582 || filterInfo.depth() < 0 583 || filterInfo.streamBytes() < 0 584 || filterInfo.references() > maxReferences 585 || filterInfo.depth() > maxDepth 586 || filterInfo.streamBytes() > maxStreamBytes) { 587 return Status.REJECTED; 588 } 589 590 Class<?> clazz = filterInfo.serialClass(); 591 if (clazz != null) { 592 if (clazz.isArray()) { 593 if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > maxArrayLength) { 594 // array length is too big 595 return Status.REJECTED; 596 } 597 do { 598 // Arrays are decided based on the component type 599 clazz = clazz.getComponentType(); 600 } while (clazz.isArray()); 601 } 602 603 if (clazz.isPrimitive()) { 604 // Primitive types are undecided; let someone else decide 605 return Status.UNDECIDED; 606 } else { 607 // Find any filter that allowed or rejected the class 608 final Class<?> cl = clazz; 609 Optional<Status> status = filters.stream() 610 .map(f -> f.apply(cl)) 611 .filter(p -> p != Status.UNDECIDED) 612 .findFirst(); 613 return status.orElse(Status.UNDECIDED); 614 } 615 } | 362 * the filter returns {@link Status#REJECTED Status.REJECTED}. 363 * If the class is an array type, the class to be matched is the element type. 364 * Arrays of any number of dimensions are treated the same as the element type. 365 * For example, a pattern of "{@code !example.Foo}", 366 * rejects creation of any instance or array of {@code example.Foo}. 367 * The first pattern that matches, working from left to right, determines 368 * the {@link Status#ALLOWED Status.ALLOWED} 369 * or {@link Status#REJECTED Status.REJECTED} result. 370 * If nothing matches, the result is {@link Status#UNDECIDED Status.UNDECIDED}. 371 * 372 * @param pattern the pattern string to parse; not null 373 * @return a filter to check a class being deserialized; may be null; 374 * {@code null} if no patterns 375 * @throws IllegalArgumentException 376 * if a limit is missing the name, or the long value 377 * is not a number or is negative, 378 * or if the package is missing for ".*" and ".**" 379 */ 380 public static ObjectInputFilter createFilter(String pattern) { 381 Objects.requireNonNull(pattern, "pattern"); 382 return Global.createFilter(pattern, true); 383 } 384 385 /** 386 * Returns an ObjectInputFilter from a string of patterns that 387 * checks only the length for arrays, not the component type. 388 * 389 * @param pattern the pattern string to parse; not null 390 * @return a filter to check a class being deserialized; 391 * {@code null} if no patterns 392 */ 393 public static ObjectInputFilter createFilter2(String pattern) { 394 Objects.requireNonNull(pattern, "pattern"); 395 return Global.createFilter(pattern, false); 396 } 397 398 /** 399 * Implementation of ObjectInputFilter that performs the checks of 400 * the process-wide serialization filter. If configured, it will be 401 * used for all ObjectInputStreams that do not set their own filters. 402 * 403 */ 404 final static class Global implements ObjectInputFilter { 405 /** 406 * The pattern used to create the filter. 407 */ 408 private final String pattern; 409 /** 410 * The list of class filters. 411 */ 412 private final List<Function<Class<?>, Status>> filters; 413 /** 414 * Maximum allowed bytes in the stream. 415 */ 416 private long maxStreamBytes; 417 /** 418 * Maximum depth of the graph allowed. 419 */ 420 private long maxDepth; 421 /** 422 * Maximum number of references in a graph. 423 */ 424 private long maxReferences; 425 /** 426 * Maximum length of any array. 427 */ 428 private long maxArrayLength; 429 /** 430 * True to check the component type for arrays. 431 */ 432 private final boolean checkComponentType; 433 434 /** 435 * Returns an ObjectInputFilter from a string of patterns. 436 * 437 * @param pattern the pattern string to parse 438 * @param checkComponentType true if the filter should check 439 * the component type of arrays 440 * @return a filter to check a class being deserialized; not null 441 * @throws IllegalArgumentException if the parameter is malformed 442 * if the pattern is missing the name, the long value 443 * is not a number or is negative. 444 */ 445 static ObjectInputFilter createFilter(String pattern, boolean checkComponentType) { 446 Global filter = new Global(pattern, checkComponentType); 447 return filter.isEmpty() ? null : filter; 448 } 449 450 /** 451 * Construct a new filter from the pattern String. 452 * 453 * @param pattern a pattern string of filters 454 * @param checkComponentType true if the filter should check 455 * the component type of arrays 456 * @throws IllegalArgumentException if the pattern is malformed 457 */ 458 private Global(String pattern, boolean checkComponentType) { 459 this.pattern = pattern; 460 this.checkComponentType = checkComponentType; 461 462 maxArrayLength = Long.MAX_VALUE; // Default values are unlimited 463 maxDepth = Long.MAX_VALUE; 464 maxReferences = Long.MAX_VALUE; 465 maxStreamBytes = Long.MAX_VALUE; 466 467 String[] patterns = pattern.split(";"); 468 filters = new ArrayList<>(patterns.length); 469 for (int i = 0; i < patterns.length; i++) { 470 String p = patterns[i]; 471 int nameLen = p.length(); 472 if (nameLen == 0) { 473 continue; 474 } 475 if (parseLimit(p)) { 476 // If the pattern contained a limit setting, i.e. type=value 477 continue; 478 } 479 boolean negate = p.charAt(0) == '!'; 480 598 /** 599 * {@inheritDoc} 600 */ 601 @Override 602 public Status checkInput(FilterInfo filterInfo) { 603 if (filterInfo.references() < 0 604 || filterInfo.depth() < 0 605 || filterInfo.streamBytes() < 0 606 || filterInfo.references() > maxReferences 607 || filterInfo.depth() > maxDepth 608 || filterInfo.streamBytes() > maxStreamBytes) { 609 return Status.REJECTED; 610 } 611 612 Class<?> clazz = filterInfo.serialClass(); 613 if (clazz != null) { 614 if (clazz.isArray()) { 615 if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > maxArrayLength) { 616 // array length is too big 617 return Status.REJECTED; 618 } 619 if (!checkComponentType) { 620 // As revised; do not check the component type for arrays 621 return Status.UNDECIDED; 622 } 623 do { 624 // Arrays are decided based on the component type 625 clazz = clazz.getComponentType(); 626 } while (clazz.isArray()); 627 } 628 629 if (clazz.isPrimitive()) { 630 // Primitive types are undecided; let someone else decide 631 return Status.UNDECIDED; 632 } else { 633 // Find any filter that allowed or rejected the class 634 final Class<?> cl = clazz; 635 Optional<Status> status = filters.stream() 636 .map(f -> f.apply(cl)) 637 .filter(p -> p != Status.UNDECIDED) 638 .findFirst(); 639 return status.orElse(Status.UNDECIDED); 640 } 641 } |