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 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("^(?:\\[+L)?java\\.time\\..*$"),
94 Pattern.compile("^\\[+Z$"),
95 Pattern.compile("^\\[+B$"),
96 Pattern.compile("^\\[+C$"),
97 Pattern.compile("^\\[+D$"),
98 Pattern.compile("^\\[+F$"),
99 Pattern.compile("^\\[+I$"),
100 Pattern.compile("^\\[+J$"),
101 Pattern.compile("^\\[+S$")
102 ));
103
104 private RestrictedObjectInputStream(final InputStream in) throws IOException {
105 super(in);
106 }
107
108 @Override
109 protected Class<?> resolveClass(final ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
110 final String className = objectStreamClass.getName();
111 if (!isAllowedClassName(className)) {
112 throw new ResourceIOException(String.format(
113 "Class %s is not allowed for deserialization", objectStreamClass.getName()));
114 }
115 return super.resolveClass(objectStreamClass);
116 }
117
118
119 static boolean isAllowedClassName(final String className) {
120 for (final Pattern allowedClassPattern : ALLOWED_CLASS_PATTERNS) {
121 if (allowedClassPattern.matcher(className).matches()) {
122 return true;
123 }
124 }
125 return false;
126 }
127
128 }
129
130 }