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
56
57 @Deprecated
58 @Contract(threading = ThreadingBehavior.STATELESS)
59 public final class ByteArrayCacheEntrySerializer implements HttpCacheEntrySerializer<byte[]> {
60
61
62
63
64 public static final ByteArrayCacheEntrySerializer INSTANCE = new ByteArrayCacheEntrySerializer();
65
66 @Override
67 public byte[] serialize(final HttpCacheStorageEntry cacheEntry) throws ResourceIOException {
68 if (cacheEntry == null) {
69 return null;
70 }
71 final ByteArrayOutputStream buf = new ByteArrayOutputStream();
72 try (final ObjectOutputStream oos = new ObjectOutputStream(buf)) {
73 oos.writeObject(cacheEntry);
74 } catch (final IOException ex) {
75 throw new ResourceIOException(ex.getMessage(), ex);
76 }
77 return buf.toByteArray();
78 }
79
80 @Override
81 public HttpCacheStorageEntry deserialize(final byte[] serializedObject) throws ResourceIOException {
82 if (serializedObject == null) {
83 return null;
84 }
85 try (final ObjectInputStream ois = new RestrictedObjectInputStream(new ByteArrayInputStream(serializedObject))) {
86 return (HttpCacheStorageEntry) ois.readObject();
87 } catch (final IOException | ClassNotFoundException ex) {
88 throw new ResourceIOException(ex.getMessage(), ex);
89 }
90 }
91
92
93 static class RestrictedObjectInputStream extends ObjectInputStream {
94
95 private static final List<Pattern> ALLOWED_CLASS_PATTERNS = Collections.unmodifiableList(Arrays.asList(
96 Pattern.compile("^(\\[L)?org\\.apache\\.hc\\.(.*)"),
97 Pattern.compile("^(?:\\[+L)?java\\.util\\..*$"),
98 Pattern.compile("^(?:\\[+L)?java\\.lang\\..*$"),
99 Pattern.compile("^(?:\\[+L)?java\\.time\\..*$"),
100 Pattern.compile("^\\[+Z$"),
101 Pattern.compile("^\\[+B$"),
102 Pattern.compile("^\\[+C$"),
103 Pattern.compile("^\\[+D$"),
104 Pattern.compile("^\\[+F$"),
105 Pattern.compile("^\\[+I$"),
106 Pattern.compile("^\\[+J$"),
107 Pattern.compile("^\\[+S$")
108 ));
109
110 private RestrictedObjectInputStream(final InputStream in) throws IOException {
111 super(in);
112 }
113
114 @Override
115 protected Class<?> resolveClass(final ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
116 final String className = objectStreamClass.getName();
117 if (!isAllowedClassName(className)) {
118 throw new ResourceIOException(String.format(
119 "Class %s is not allowed for deserialization", objectStreamClass.getName()));
120 }
121 return super.resolveClass(objectStreamClass);
122 }
123
124
125 static boolean isAllowedClassName(final String className) {
126 for (final Pattern allowedClassPattern : ALLOWED_CLASS_PATTERNS) {
127 if (allowedClassPattern.matcher(className).matches()) {
128 return true;
129 }
130 }
131 return false;
132 }
133
134 }
135
136 }