Skip to content

Commit c79011e

Browse files
committed
SOLR-16949: Handle inputStream leaks
(cherry picked from commit c89813a)
1 parent 734920a commit c79011e

File tree

3 files changed

+96
-60
lines changed

3 files changed

+96
-60
lines changed

solr/core/src/java/org/apache/solr/core/FileSystemConfigSetService.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,11 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
214214
"Not including uploading file to config, as it is a forbidden type: {}",
215215
file.getFileName());
216216
} else {
217-
if (!FileTypeMagicUtil.isFileForbiddenInConfigset(Files.newInputStream(file))) {
217+
if (!FileTypeMagicUtil.isFileForbiddenInConfigset(file)) {
218218
Files.copy(
219219
file, target.resolve(source.relativize(file).toString()), REPLACE_EXISTING);
220220
} else {
221-
String mimeType =
222-
FileTypeMagicUtil.INSTANCE.guessMimeType(Files.newInputStream(file));
221+
String mimeType = FileTypeMagicUtil.INSTANCE.guessMimeType(file);
223222
log.warn(
224223
"Not copying file {}, as it matched the MAGIC signature of a forbidden mime type {}",
225224
file.getFileName(),

solr/core/src/java/org/apache/solr/util/FileTypeMagicUtil.java

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import com.j256.simplemagic.ContentInfo;
2121
import com.j256.simplemagic.ContentInfoUtil;
2222
import com.j256.simplemagic.ContentType;
23-
import java.io.ByteArrayInputStream;
2423
import java.io.IOException;
2524
import java.io.InputStream;
2625
import java.nio.file.FileVisitResult;
@@ -58,30 +57,23 @@ public class FileTypeMagicUtil implements ContentInfoUtil.ErrorCallBack {
5857
public static void assertConfigSetFolderLegal(Path confPath) throws IOException {
5958
Files.walkFileTree(
6059
confPath,
61-
new SimpleFileVisitor<Path>() {
60+
new SimpleFileVisitor<>() {
6261
@Override
63-
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
64-
throws IOException {
65-
// Read first 100 bytes of the file to determine the mime type
66-
try (InputStream fileStream = Files.newInputStream(file)) {
67-
byte[] bytes = new byte[100];
68-
fileStream.read(bytes);
69-
if (FileTypeMagicUtil.isFileForbiddenInConfigset(bytes)) {
70-
throw new SolrException(
71-
SolrException.ErrorCode.BAD_REQUEST,
72-
String.format(
73-
Locale.ROOT,
74-
"Not uploading file %s to configset, as it matched the MAGIC signature of a forbidden mime type %s",
75-
file,
76-
FileTypeMagicUtil.INSTANCE.guessMimeType(bytes)));
77-
}
78-
return FileVisitResult.CONTINUE;
62+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
63+
if (FileTypeMagicUtil.isFileForbiddenInConfigset(file)) {
64+
throw new SolrException(
65+
SolrException.ErrorCode.BAD_REQUEST,
66+
String.format(
67+
Locale.ROOT,
68+
"Not uploading file %s to configset, as it matched the MAGIC signature of a forbidden mime type %s",
69+
file,
70+
FileTypeMagicUtil.INSTANCE.guessMimeType(file)));
7971
}
72+
return FileVisitResult.CONTINUE;
8073
}
8174

8275
@Override
83-
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
84-
throws IOException {
76+
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
8577
if (SKIP_FOLDERS.contains(dir.getFileName().toString()))
8678
return FileVisitResult.SKIP_SUBTREE;
8779

@@ -90,19 +82,29 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
9082
});
9183
}
9284

85+
/**
86+
* Guess the mime type of file based on its magic number.
87+
*
88+
* @param file file to check
89+
* @return string with content-type or "application/octet-stream" if unknown
90+
*/
91+
public String guessMimeType(Path file) {
92+
try {
93+
return guessTypeFallbackToOctetStream(util.findMatch(file.toFile()));
94+
} catch (IOException e) {
95+
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
96+
}
97+
}
98+
9399
/**
94100
* Guess the mime type of file based on its magic number.
95101
*
96102
* @param stream input stream of the file
97103
* @return string with content-type or "application/octet-stream" if unknown
98104
*/
99-
public String guessMimeType(InputStream stream) {
105+
String guessMimeType(InputStream stream) {
100106
try {
101-
ContentInfo info = util.findMatch(stream);
102-
if (info == null) {
103-
return ContentType.OTHER.getMimeType();
104-
}
105-
return info.getContentType().getMimeType();
107+
return guessTypeFallbackToOctetStream(util.findMatch(stream));
106108
} catch (IOException e) {
107109
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
108110
}
@@ -115,7 +117,7 @@ public String guessMimeType(InputStream stream) {
115117
* @return string with content-type or "application/octet-stream" if unknown
116118
*/
117119
public String guessMimeType(byte[] bytes) {
118-
return guessMimeType(new ByteArrayInputStream(bytes));
120+
return guessTypeFallbackToOctetStream(util.findMatch(bytes));
119121
}
120122

121123
@Override
@@ -126,6 +128,31 @@ public void error(String line, String details, Exception e) {
126128
e);
127129
}
128130

131+
/**
132+
* Determine forbidden file type based on magic bytes matching of the file itself. Forbidden types
133+
* are:
134+
*
135+
* <ul>
136+
* <li><code>application/x-java-applet</code>: java class file
137+
* <li><code>application/zip</code>: jar or zip archives
138+
* <li><code>application/x-tar</code>: tar archives
139+
* <li><code>text/x-shellscript</code>: shell or bash script
140+
* </ul>
141+
*
142+
* @param file file to check
143+
* @return true if file is among the forbidden mime-types
144+
*/
145+
public static boolean isFileForbiddenInConfigset(Path file) {
146+
try (InputStream fileStream = Files.newInputStream(file)) {
147+
return isFileForbiddenInConfigset(fileStream);
148+
} catch (IOException e) {
149+
throw new SolrException(
150+
SolrException.ErrorCode.SERVER_ERROR,
151+
String.format(Locale.ROOT, "Error reading file %s", file),
152+
e);
153+
}
154+
}
155+
129156
/**
130157
* Determine forbidden file type based on magic bytes matching of the file itself. Forbidden types
131158
* are:
@@ -140,7 +167,7 @@ public void error(String line, String details, Exception e) {
140167
* @param fileStream stream from the file content
141168
* @return true if file is among the forbidden mime-types
142169
*/
143-
public static boolean isFileForbiddenInConfigset(InputStream fileStream) {
170+
static boolean isFileForbiddenInConfigset(InputStream fileStream) {
144171
return forbiddenTypes.contains(FileTypeMagicUtil.INSTANCE.guessMimeType(fileStream));
145172
}
146173

@@ -153,7 +180,7 @@ public static boolean isFileForbiddenInConfigset(InputStream fileStream) {
153180
public static boolean isFileForbiddenInConfigset(byte[] bytes) {
154181
if (bytes == null || bytes.length == 0)
155182
return false; // A ZK znode may be a folder with no content
156-
return isFileForbiddenInConfigset(new ByteArrayInputStream(bytes));
183+
return forbiddenTypes.contains(FileTypeMagicUtil.INSTANCE.guessMimeType(bytes));
157184
}
158185

159186
private static final Set<String> forbiddenTypes =
@@ -163,4 +190,12 @@ public static boolean isFileForbiddenInConfigset(byte[] bytes) {
163190
"solr.configset.upload.mimetypes.forbidden",
164191
"application/x-java-applet,application/zip,application/x-tar,text/x-shellscript")
165192
.split(",")));
193+
194+
private String guessTypeFallbackToOctetStream(ContentInfo contentInfo) {
195+
if (contentInfo == null) {
196+
return ContentType.OTHER.getMimeType();
197+
} else {
198+
return contentInfo.getContentType().getMimeType();
199+
}
200+
}
166201
}

solr/core/src/test/org/apache/solr/util/FileTypeMagicUtilTest.java

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,40 @@
1717

1818
package org.apache.solr.util;
1919

20+
import java.io.IOException;
21+
import java.io.InputStream;
2022
import org.apache.solr.SolrTestCaseJ4;
2123

2224
public class FileTypeMagicUtilTest extends SolrTestCaseJ4 {
23-
public void testGuessMimeType() {
24-
assertEquals(
25-
"application/x-java-applet",
26-
FileTypeMagicUtil.INSTANCE.guessMimeType(
27-
FileTypeMagicUtil.class.getResourceAsStream("/magic/HelloWorldJavaClass.class.bin")));
28-
assertEquals(
29-
"application/zip",
30-
FileTypeMagicUtil.INSTANCE.guessMimeType(
31-
FileTypeMagicUtil.class.getResourceAsStream(
32-
"/runtimecode/containerplugin.v.1.jar.bin")));
33-
assertEquals(
34-
"application/x-tar",
35-
FileTypeMagicUtil.INSTANCE.guessMimeType(
36-
FileTypeMagicUtil.class.getResourceAsStream("/magic/hello.tar.bin")));
37-
assertEquals(
38-
"text/x-shellscript",
39-
FileTypeMagicUtil.INSTANCE.guessMimeType(
40-
FileTypeMagicUtil.class.getResourceAsStream("/magic/shell.sh.txt")));
25+
public void testGuessMimeType() throws IOException {
26+
assertResourceMimeType("application/x-java-applet", "/magic/HelloWorldJavaClass.class.bin");
27+
assertResourceMimeType("application/zip", "/runtimecode/containerplugin.v.1.jar.bin");
28+
assertResourceMimeType("application/x-tar", "/magic/hello.tar.bin");
29+
assertResourceMimeType("text/x-shellscript", "/magic/shell.sh.txt");
4130
}
4231

43-
public void testIsFileForbiddenInConfigset() {
44-
assertTrue(
45-
FileTypeMagicUtil.isFileForbiddenInConfigset(
46-
FileTypeMagicUtil.class.getResourceAsStream("/magic/HelloWorldJavaClass.class.bin")));
47-
assertTrue(
48-
FileTypeMagicUtil.isFileForbiddenInConfigset(
49-
FileTypeMagicUtil.class.getResourceAsStream("/magic/shell.sh.txt")));
50-
assertFalse(
51-
FileTypeMagicUtil.isFileForbiddenInConfigset(
52-
FileTypeMagicUtil.class.getResourceAsStream("/magic/plain.txt")));
32+
public void testIsFileForbiddenInConfigset() throws IOException {
33+
assertResourceForbiddenInConfigset("/magic/HelloWorldJavaClass.class.bin");
34+
assertResourceForbiddenInConfigset("/magic/shell.sh.txt");
35+
assertResourceAllowedInConfigset("/magic/plain.txt");
36+
}
37+
38+
private void assertResourceMimeType(String mimeType, String resourcePath) throws IOException {
39+
try (InputStream stream =
40+
FileTypeMagicUtil.class.getResourceAsStream("/magic/HelloWorldJavaClass.class.bin")) {
41+
assertEquals("application/x-java-applet", FileTypeMagicUtil.INSTANCE.guessMimeType(stream));
42+
}
43+
}
44+
45+
private void assertResourceForbiddenInConfigset(String resourcePath) throws IOException {
46+
try (InputStream stream = FileTypeMagicUtil.class.getResourceAsStream(resourcePath)) {
47+
assertTrue(FileTypeMagicUtil.isFileForbiddenInConfigset(stream));
48+
}
49+
}
50+
51+
private void assertResourceAllowedInConfigset(String resourcePath) throws IOException {
52+
try (InputStream stream = FileTypeMagicUtil.class.getResourceAsStream(resourcePath)) {
53+
assertFalse(FileTypeMagicUtil.isFileForbiddenInConfigset(stream));
54+
}
5355
}
5456
}

0 commit comments

Comments
 (0)