Skip to content

Commit 6c8f24e

Browse files
committed
SOLR-16949: Fix inputstream leaks
1 parent 143fa6f commit 6c8f24e

File tree

2 files changed

+81
-49
lines changed

2 files changed

+81
-49
lines changed

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

Lines changed: 51 additions & 21 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,42 +57,48 @@ public class FileTypeMagicUtil implements ContentInfoUtil.ErrorCallBack {
5857
public static void assertConfigSetFolderLegal(Path confPath) throws IOException {
5958
Files.walkFileTree(confPath, new SimpleFileVisitor<Path>() {
6059
@Override
61-
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
60+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
6261
// Read first 100 bytes of the file to determine the mime type
63-
try(InputStream fileStream = Files.newInputStream(file)) {
64-
byte[] bytes = new byte[100];
65-
fileStream.read(bytes);
66-
if (FileTypeMagicUtil.isFileForbiddenInConfigset(bytes)) {
67-
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
68-
String.format(Locale.ROOT, "Not uploading file %s to configset, as it matched the MAGIC signature of a forbidden mime type %s",
69-
file, FileTypeMagicUtil.INSTANCE.guessMimeType(bytes)));
70-
}
71-
return FileVisitResult.CONTINUE;
62+
if (FileTypeMagicUtil.isFileForbiddenInConfigset(file)) {
63+
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
64+
String.format(Locale.ROOT, "Not uploading file %s to configset, as it matched the MAGIC signature of a forbidden mime type %s",
65+
file, FileTypeMagicUtil.INSTANCE.guessMimeType(file)));
7266
}
67+
return FileVisitResult.CONTINUE;
7368
}
7469

7570
@Override
76-
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
71+
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
7772
if (SKIP_FOLDERS.contains(dir.getFileName().toString())) return FileVisitResult.SKIP_SUBTREE;
7873

7974
return FileVisitResult.CONTINUE;
8075
}
8176
});
8277
}
8378

79+
/**
80+
* Guess the mime type of file based on its magic number.
81+
*
82+
* @param file file to check
83+
* @return string with content-type or "application/octet-stream" if unknown
84+
*/
85+
public String guessMimeType(Path file) {
86+
try {
87+
return guessTypeFallbackToOctetStream(util.findMatch(file.toFile()));
88+
} catch (IOException e) {
89+
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
90+
}
91+
}
92+
8493
/**
8594
* Guess the mime type of file based on its magic number.
8695
*
8796
* @param stream input stream of the file
8897
* @return string with content-type or "application/octet-stream" if unknown
8998
*/
90-
public String guessMimeType(InputStream stream) {
99+
String guessMimeType(InputStream stream) {
91100
try {
92-
ContentInfo info = util.findMatch(stream);
93-
if (info == null) {
94-
return ContentType.OTHER.getMimeType();
95-
}
96-
return info.getContentType().getMimeType();
101+
return guessTypeFallbackToOctetStream(util.findMatch(stream));
97102
} catch (IOException e) {
98103
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
99104
}
@@ -106,7 +111,7 @@ public String guessMimeType(InputStream stream) {
106111
* @return string with content-type or "application/octet-stream" if unknown
107112
*/
108113
public String guessMimeType(byte[] bytes) {
109-
return guessMimeType(new ByteArrayInputStream(bytes));
114+
return guessTypeFallbackToOctetStream(util.findMatch(bytes));
110115
}
111116

112117
@Override
@@ -117,6 +122,24 @@ public void error(String line, String details, Exception e) {
117122
e);
118123
}
119124

125+
/**
126+
* Determine forbidden file type based on magic bytes matching of the file itself. Forbidden types
127+
* are:
128+
*
129+
* <ul>
130+
* <li><code>application/x-java-applet</code>: java class file
131+
* <li><code>application/zip</code>: jar or zip archives
132+
* <li><code>application/x-tar</code>: tar archives
133+
* <li><code>text/x-shellscript</code>: shell or bash script
134+
* </ul>
135+
*
136+
* @param file file to check
137+
* @return true if file is among the forbidden mime-types
138+
*/
139+
public static boolean isFileForbiddenInConfigset(Path file) {
140+
return forbiddenTypes.contains(FileTypeMagicUtil.INSTANCE.guessMimeType(file));
141+
}
142+
120143
/**
121144
* Determine forbidden file type based on magic bytes matching of the file itself. Forbidden types
122145
* are:
@@ -131,7 +154,7 @@ public void error(String line, String details, Exception e) {
131154
* @param fileStream stream from the file content
132155
* @return true if file is among the forbidden mime-types
133156
*/
134-
public static boolean isFileForbiddenInConfigset(InputStream fileStream) {
157+
static boolean isFileForbiddenInConfigset(InputStream fileStream) {
135158
return forbiddenTypes.contains(FileTypeMagicUtil.INSTANCE.guessMimeType(fileStream));
136159
}
137160

@@ -144,7 +167,7 @@ public static boolean isFileForbiddenInConfigset(InputStream fileStream) {
144167
public static boolean isFileForbiddenInConfigset(byte[] bytes) {
145168
if (bytes == null || bytes.length == 0)
146169
return false; // A ZK znode may be a folder with no content
147-
return isFileForbiddenInConfigset(new ByteArrayInputStream(bytes));
170+
return forbiddenTypes.contains(FileTypeMagicUtil.INSTANCE.guessMimeType(bytes));
148171
}
149172

150173
private static final Set<String> forbiddenTypes =
@@ -155,4 +178,11 @@ public static boolean isFileForbiddenInConfigset(byte[] bytes) {
155178
"application/x-java-applet,application/zip,application/x-tar,text/x-shellscript")
156179
.split(",")));
157180

181+
private String guessTypeFallbackToOctetStream(ContentInfo contentInfo) {
182+
if (contentInfo == null) {
183+
return ContentType.OTHER.getMimeType();
184+
} else {
185+
return contentInfo.getContentType().getMimeType();
186+
}
187+
}
158188
}

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)