Description: CVE-2020-15250 local information disclosure vulnerability --- Origin: https://github.com/junit-team/junit4/commit/610155b8c22138329f0723eec22521627dbc52ae Bug-Debian: https://bugs.debian.org/972231 Last-Update: 2020-11-01 --- a/src/main/java/org/junit/rules/TemporaryFolder.java +++ b/src/main/java/org/junit/rules/TemporaryFolder.java @@ -2,6 +2,9 @@ package org.junit.rules; import java.io.File; import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import org.junit.Rule; @@ -130,7 +133,45 @@ public class TemporaryFolder extends Ext return createTemporaryFolderIn(getRoot()); } - private File createTemporaryFolderIn(File parentFolder) throws IOException { + private static File createTemporaryFolderIn(File parentFolder) throws IOException { + try { + return createTemporaryFolderWithNioApi(parentFolder); + } catch (ClassNotFoundException ignore) { + // Fallback for Java 5 and 6 + return createTemporaryFolderWithFileApi(parentFolder); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof IOException) { + throw (IOException) cause; + } + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + IOException exception = new IOException("Failed to create temporary folder in " + parentFolder); + exception.initCause(cause); + throw exception; + } catch (Exception e) { + throw new RuntimeException("Failed to create temporary folder in " + parentFolder, e); + } + } + + private static File createTemporaryFolderWithNioApi(File parentFolder) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class<?> filesClass = Class.forName("java.nio.file.Files"); + Object fileAttributeArray = Array.newInstance(Class.forName("java.nio.file.attribute.FileAttribute"), 0); + Class<?> pathClass = Class.forName("java.nio.file.Path"); + Object tempDir; + if (parentFolder != null) { + Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", pathClass, String.class, fileAttributeArray.getClass()); + Object parentPath = File.class.getDeclaredMethod("toPath").invoke(parentFolder); + tempDir = createTempDirectoryMethod.invoke(null, parentPath, "junit", fileAttributeArray); + } else { + Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", String.class, fileAttributeArray.getClass()); + tempDir = createTempDirectoryMethod.invoke(null, "junit", fileAttributeArray); + } + return (File) pathClass.getDeclaredMethod("toFile").invoke(tempDir); + } + + private static File createTemporaryFolderWithFileApi(File parentFolder) throws IOException { File createdFolder = File.createTempFile("junit", "", parentFolder); createdFolder.delete(); createdFolder.mkdir(); --- a/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java +++ b/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java @@ -2,22 +2,30 @@ package org.junit.tests.experimental.rul import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.junit.experimental.results.PrintableResult.testResult; import static org.junit.experimental.results.ResultMatchers.failureCountIs; import static org.junit.experimental.results.ResultMatchers.isSuccessful; import java.io.File; import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import org.junit.rules.TemporaryFolder; import org.junit.After; +import org.junit.AssumptionViolatedException; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; public class TempFolderRuleTest { private static File[] createdFiles = new File[20]; @@ -175,6 +183,34 @@ public class TempFolderRuleTest { assertFalse(folder.getRoot().exists()); } + @Test + public void tempFolderIsOnlyAccessibleByOwner() throws IOException { + TemporaryFolder folder = new TemporaryFolder(); + folder.create(); + + Set<String> expectedPermissions = new TreeSet<String>(Arrays.asList("OWNER_READ", "OWNER_WRITE", "OWNER_EXECUTE")); + Set<String> actualPermissions = getPosixFilePermissions(folder.getRoot()); + assertEquals(expectedPermissions, actualPermissions); + } + + private Set<String> getPosixFilePermissions(File root) { + try { + Class<?> pathClass = Class.forName("java.nio.file.Path"); + Object linkOptionArray = Array.newInstance(Class.forName("java.nio.file.LinkOption"), 0); + Class<?> filesClass = Class.forName("java.nio.file.Files"); + Object path = File.class.getDeclaredMethod("toPath").invoke(root); + Method posixFilePermissionsMethod = filesClass.getDeclaredMethod("getPosixFilePermissions", pathClass, linkOptionArray.getClass()); + Set<?> permissions = (Set<?>) posixFilePermissionsMethod.invoke(null, path, linkOptionArray); + SortedSet<String> convertedPermissions = new TreeSet<String>(); + for (Object item : permissions) { + convertedPermissions.add(item.toString()); + } + return convertedPermissions; + } catch (Exception e) { + throw new AssumptionViolatedException("Test requires at least Java 1.7", e); + } + } + public static class NameClashes { @Rule public TemporaryFolder folder = new TemporaryFolder();