1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.impl.cache;
28
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.ObjectInputStream;
34 import java.io.ObjectOutputStream;
35 import java.io.ObjectStreamClass;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.regex.Pattern;
40
41 import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
42 import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
43 import org.apache.hc.client5.http.cache.ResourceIOException;
44 import org.apache.hc.core5.annotation.Contract;
45 import org.apache.hc.core5.annotation.ThreadingBehavior;
46
47
48
49
50
51
52
53
54
55 @Contract(threading = ThreadingBehavior.STATELESS)
56 public final class ByteArrayCacheEntrySerializer implements HttpCacheEntrySerializer<byte[]> {
57
58 public static final ByteArrayCacheEntrySerializerrayCacheEntrySerializer.html#ByteArrayCacheEntrySerializer">ByteArrayCacheEntrySerializer INSTANCE = new ByteArrayCacheEntrySerializer();
59
60 @Override
61 public byte[] serialize(final HttpCacheStorageEntry cacheEntry) throws ResourceIOException {
62 if (cacheEntry == null) {
63 return null;
64 }
65 final ByteArrayOutputStream buf = new ByteArrayOutputStream();
66 try (final ObjectOutputStream oos = new ObjectOutputStream(buf)) {
67 oos.writeObject(cacheEntry);
68 } catch (final IOException ex) {
69 throw new ResourceIOException(ex.getMessage(), ex);
70 }
71 return buf.toByteArray();
72 }
73
74 @Override
75 public HttpCacheStorageEntry deserialize(final byte[] serializedObject) throws ResourceIOException {
76 if (serializedObject == null) {
77 return null;
78 }
79 try (final ObjectInputStream ois = new RestrictedObjectInputStream(new ByteArrayInputStream(serializedObject))) {
80 return (HttpCacheStorageEntry) ois.readObject();
81 } catch (final IOException | ClassNotFoundException ex) {
82 throw new ResourceIOException(ex.getMessage(), ex);
83 }
84 }
85
86
87 static class RestrictedObjectInputStream extends ObjectInputStream {
88
89 private static final List<Pattern> ALLOWED_CLASS_PATTERNS = Collections.unmodifiableList(Arrays.asList(
90 Pattern.compile("^(\\[L)?org\\.apache\\.hc\\.(.*)"),
91 Pattern.compile("^(?:\\[+L)?java\\.util\\..*$"),
92 Pattern.compile("^(?:\\[+L)?java\\.lang\\..*$"),
93 Pattern.compile("^\\[+Z$"),
94 Pattern.compile("^\\[+B$"),
95 Pattern.compile("^\\[+C$"),
96 Pattern.compile("^\\[+D$"),
97 Pattern.compile("^\\[+F$"),
98 Pattern.compile("^\\[+I$"),
99 Pattern.compile("^\\[+J$"),
100 Pattern.compile("^\\[+S$")
101 ));
102
103 private RestrictedObjectInputStream(final InputStream in) throws IOException {
104 super(in);
105 }
106
107 @Override
108 protected Class<?> resolveClass(final ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
109 final String className = objectStreamClass.getName();
110 if (!isAllowedClassName(className)) {
111 throw new ResourceIOException(String.format(
112 "Class %s is not allowed for deserialization", objectStreamClass.getName()));
113 }
114 return super.resolveClass(objectStreamClass);
115 }
116
117
118 static boolean isAllowedClassName(final String className) {
119 for (final Pattern allowedClassPattern : ALLOWED_CLASS_PATTERNS) {
120 if (allowedClassPattern.matcher(className).matches()) {
121 return true;
122 }
123 }
124 return false;
125 }
126
127 }
128
129 }