CVE-2022-41954: Insecure Temporary File (CWE-377)
value: MPXJ is an open source library to read and write project plans from a variety of file formats and databases. On Unix-like operating systems (not Windows or macos), MPXJ's use of `File.createTempFile(..)` results in temporary files being created with the permissions `-rw-r--r--`. This means that any other user on the system can read the contents of this file. When MPXJ is reading a schedule file which requires the creation of a temporary file or directory, a knowledgeable local user could locate these transient files while they are in use and would then be able to read the schedule being processed by MPXJ. The problem has been patched, MPXJ version 10.14.1 and later includes the necessary changes. Users unable to upgrade may set `java.io.tmpdir` to a directory to which only the user running the application has access will prevent other users from accessing these temporary files.
CWE description: Creating and using insecure temporary files can leave application and system data vulnerable to attack.
CVSS v3 base score: 3.3
Published: 2022-11-25T19:15Z
Repository: https://github.com/joniles/mpxj
Language: Java
Files changed: src/main/java/net/sf/mpxj/common/FileHelper.java, src/main/java/net/sf/mpxj/common/InputStreamHelper.java, src/test/java/net/sf/mpxj/junit/CustomerDataTest.java, src/test/java/net/sf/mpxj/junit/LocaleTest.java, src/test/java/net/sf/mpxj/junit/legacy/BasicTest.java
Commit date: 2022-11-19 21:19:58 +0000
Commit message: vuln-fix: Temporary File Information Disclosure



This fixes temporary file information disclosure vulnerability due to the use
of the vulnerable `File.createTempFile()` method. The vulnerability is fixed by
using the `Files.createTempFile()` method which sets the correct posix permissions.

Weakness: CWE-377: Insecure Temporary File
Severity: Medium
CVSSS: 5.5
Detection: CodeQL & OpenRewrite (https://public.moderne.io/recipes/org.openrewrite.java.security.SecureTempFileCreation)

Reported-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>
Signed-off-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>

Bug-tracker: https://github.com/JLLeitschuh/security-research/issues/18


Co-authored-by: Moderne <team@moderne.io>
Diff: diff --git a/src/main/java/net/sf/mpxj/common/FileHelper.java b/src/main/java/net/sf/mpxj/common/FileHelper.java
index 8a4dd83264..2c5f71f6a1 100644
--- a/src/main/java/net/sf/mpxj/common/FileHelper.java
+++ b/src/main/java/net/sf/mpxj/common/FileHelper.java
@@ -23,10 +23,11 @@
 
 package net.sf.mpxj.common;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
 
 /**
  * Common helper methods for working with files.
  */
 public final class FileHelper
@@ -108,11 +109,11 @@ public final class FileHelper
     *
     * @return File instance representing temporary directory
     */
    public static final File createTempDir() throws IOException
    {
-      File dir = File.createTempFile("mpxj", "tmp");
+      File dir = Files.createTempFile("mpxj", "tmp").toFile();
       delete(dir);
       mkdirs(dir);
       return dir;
    }
 
diff --git a/src/main/java/net/sf/mpxj/common/InputStreamHelper.java b/src/main/java/net/sf/mpxj/common/InputStreamHelper.java
index 7bcffc7bca..22ea8741ce 100644
--- a/src/main/java/net/sf/mpxj/common/InputStreamHelper.java
+++ b/src/main/java/net/sf/mpxj/common/InputStreamHelper.java
@@ -25,10 +25,11 @@ package net.sf.mpxj.common;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Files;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipInputStream;
 
 /**
@@ -47,11 +48,11 @@ public class InputStreamHelper
    {
       FileOutputStream outputStream = null;
 
       try
       {
-         File file = File.createTempFile("mpxj", tempFileSuffix);
+         File file = Files.createTempFile("mpxj", tempFileSuffix).toFile();
          outputStream = new FileOutputStream(file);
          byte[] buffer = new byte[1024];
          while (true)
          {
             int bytesRead = inputStream.read(buffer);
diff --git a/src/test/java/net/sf/mpxj/junit/CustomerDataTest.java b/src/test/java/net/sf/mpxj/junit/CustomerDataTest.java
index 738d289f0e..afe55b3db7 100644
--- a/src/test/java/net/sf/mpxj/junit/CustomerDataTest.java
+++ b/src/test/java/net/sf/mpxj/junit/CustomerDataTest.java
@@ -172,11 +172,11 @@ public class CustomerDataTest
          return;
       }
 
       // Accessing the database directly from (new) Google Drive is too slow.
       // Make a temporary local copy instead.
-      File file = File.createTempFile("primavera", "db");
+      File file = Files.createTempFile("primavera", "db").toFile();
       file.deleteOnExit();
       Files.copy(new File(m_primaveraFile).toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
 
       Map<Integer, String> projects = new PrimaveraDatabaseFileReader().listProjects(file);
       long failures = projects.entrySet().stream().map(entry -> testPrimaveraProject(file, entry.getKey().intValue(), entry.getValue())).filter(x -> !x.booleanValue()).count();
@@ -614,11 +614,11 @@ public class CustomerDataTest
 
       project.getProjectProperties().setCurrentDate(BASELINE_CURRENT_DATE);
 
       if (baselineFile.exists())
       {
-         File out = File.createTempFile("junit", suffix);
+         File out = Files.createTempFile("junit", suffix).toFile();
          writer.write(project, out);
          success = FileUtility.equals(baselineFile, out);
 
          if (success)
          {
diff --git a/src/test/java/net/sf/mpxj/junit/LocaleTest.java b/src/test/java/net/sf/mpxj/junit/LocaleTest.java
index ad010c6cd4..f2b326e4f7 100644
--- a/src/test/java/net/sf/mpxj/junit/LocaleTest.java
+++ b/src/test/java/net/sf/mpxj/junit/LocaleTest.java
@@ -24,10 +24,11 @@
 package net.sf.mpxj.junit;
 
 import static net.sf.mpxj.junit.MpxjAssert.*;
 
 import java.io.File;
+import java.nio.file.Files;
 import java.util.Locale;
 
 import org.junit.Test;
 
 import net.sf.mpxj.ProjectFile;
@@ -62,11 +63,11 @@ public class LocaleTest
       MPXReader reader = new MPXReader();
       MPXWriter writer = new MPXWriter();
 
       File in = new File(MpxjTestData.filePath("legacy/sample.mpx"));
       ProjectFile mpx = reader.read(in);
-      File out = File.createTempFile("junit-" + locale.getLanguage(), ".mpx");
+      File out = Files.createTempFile("junit-" + locale.getLanguage(), ".mpx").toFile();
       writer.setLocale(locale);
       writer.write(mpx, out);
 
       reader.setLocale(locale);
       reader.read(out);
diff --git a/src/test/java/net/sf/mpxj/junit/legacy/BasicTest.java b/src/test/java/net/sf/mpxj/junit/legacy/BasicTest.java
index d72382e832..62d642b664 100644
--- a/src/test/java/net/sf/mpxj/junit/legacy/BasicTest.java
+++ b/src/test/java/net/sf/mpxj/junit/legacy/BasicTest.java
@@ -24,10 +24,11 @@
 package net.sf.mpxj.junit.legacy;
 
 import static org.junit.Assert.*;
 
 import java.io.File;
+import java.nio.file.Files;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 
@@ -72,11 +73,11 @@ public class BasicTest
     */
    @Test public void testRewrite1() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample.mpx"));
       ProjectFile mpx = new MPXReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       MPXWriter writer = new MPXWriter();
       writer.setUseLocaleDefaults(false);
       writer.write(mpx, out);
       boolean success = FileUtility.equals(in, out);
       assertTrue("Files are not identical", success);
@@ -91,11 +92,11 @@ public class BasicTest
     */
    @Test public void testRewrite2() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample1.xml"));
       ProjectFile xml = new MSPDIReader().read(in);
-      File out = File.createTempFile("junit", ".xml");
+      File out = Files.createTempFile("junit", ".xml").toFile();
       new MSPDIWriter().write(xml, out);
       boolean success = FileUtility.equals(in, out);
       assertTrue("Files are not identical", success);
       out.deleteOnExit();
    }
@@ -108,11 +109,11 @@ public class BasicTest
     */
    @Test public void testRewrite3() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample1.mpx"));
       ProjectFile mpx = new MPXReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       MPXWriter writer = new MPXWriter();
       writer.setUseLocaleDefaults(false);
       writer.write(mpx, out);
       boolean success = FileUtility.equals(in, out);
       assertTrue("Files are not identical", success);
@@ -126,11 +127,11 @@ public class BasicTest
    @Test public void testRewrite4() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/empty.mpp"));
       ProjectFile mpx = new MPPReader().read(in);
       mpx.getProjectProperties().setCurrentDate(new SimpleDateFormat("dd/MM/yyyy").parse("01/03/2006"));
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       MPXWriter writer = new MPXWriter();
       writer.setUseLocaleDefaults(false);
       writer.write(mpx, out);
       boolean success = FileUtility.equals(new File(MpxjTestData.filePath("empty.mpx")), out);
       assertTrue("Files are not identical", success);
@@ -142,11 +143,11 @@ public class BasicTest
     */
    @Test public void testRewrite5() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample.mpx"));
       ProjectFile mpx = new MPXReader().read(in);
-      File out = File.createTempFile("junit", ".planner");
+      File out = Files.createTempFile("junit", ".planner").toFile();
       new PlannerWriter().write(mpx, out);
       //success = FileUtility.equals (in, out);
       //assertTrue ("Files are not identical", success);
       out.deleteOnExit();
    }
@@ -265,11 +266,11 @@ public class BasicTest
     */
    @Test public void testConversion1() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample98.mpp"));
       ProjectFile mpp = new MPPReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpp, out);
       commonTests(mpp);
       out.deleteOnExit();
    }
 
@@ -278,11 +279,11 @@ public class BasicTest
     */
    @Test public void testConversion2() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample.mpp"));
       ProjectFile mpp = new MPPReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpp, out);
       commonTests(mpp);
       out.deleteOnExit();
    }
 
@@ -291,11 +292,11 @@ public class BasicTest
     */
    @Test public void testConversion3() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample.xml"));
       ProjectFile xml = new MSPDIReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(xml, out);
       commonTests(xml);
       out.deleteOnExit();
    }
 
@@ -351,16 +352,16 @@ public class BasicTest
     */
    @Test public void testConversion4() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/sample.mpp"));
       ProjectFile mpp = new MPPReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpp, out);
 
       ProjectFile mpx = new MPXReader().read(out);
       out.deleteOnExit();
-      out = File.createTempFile("junit", ".xml");
+      out = Files.createTempFile("junit", ".xml").toFile();
       new MSPDIWriter().write(mpx, out);
       out.deleteOnExit();
    }
 
    /**
@@ -426,11 +427,11 @@ public class BasicTest
       task5.setName("Test Task 5");
       task5.setDuration(Duration.getInstance(10, TimeUnit.DAYS));
       task5.setStart(new Date());
       task5.setNotes(notes5);
 
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(file1, out);
 
       ProjectFile file2 = new MPXReader().read(out);
       String notes;
       Task task1a = file2.getTaskByUniqueID(task1.getUniqueID());
@@ -488,11 +489,11 @@ public class BasicTest
 
       Resource resource5 = file1.addResource();
       resource5.setName("Test Resource 5");
       resource5.setNotes(notes5);
 
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(file1, out);
 
       ProjectFile file2 = new MPXReader().read(out);
       String notes;
       Resource resource1a = file2.getResourceByUniqueID(resource1.getUniqueID());
@@ -522,11 +523,11 @@ public class BasicTest
     */
    @Test public void testBug1() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/bug1.mpp"));
       ProjectFile mpp = new MPPReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpp, out);
       out.deleteOnExit();
    }
 
    /**
@@ -534,11 +535,11 @@ public class BasicTest
     */
    @Test public void testBug2() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/bug2.mpp"));
       ProjectFile mpp = new MPPReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpp, out);
       out.deleteOnExit();
    }
 
    /**
@@ -561,11 +562,11 @@ public class BasicTest
     */
    @Test public void testBug4() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/bug4.mpp"));
       ProjectFile mpp = new MPPReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpp, out.getAbsolutePath());
       out.deleteOnExit();
    }
 
    /**
@@ -894,11 +895,11 @@ public class BasicTest
       }
 
       //
       // Write this out as an MSPDI file
       //
-      File out = File.createTempFile("junit", ".xml");
+      File out = Files.createTempFile("junit", ".xml").toFile();
       new MSPDIWriter().write(mpp, out);
 
       //
       // Read the MSPDI file in again, and check the
       // calendar names to ensure consistency
@@ -925,11 +926,11 @@ public class BasicTest
 
       File in = new File(MpxjTestData.filePath("legacy/alias.xml"));
       ProjectFile xml = reader.read(in);
       validateAliases(xml);
 
-      File out = File.createTempFile("junit", ".xml");
+      File out = Files.createTempFile("junit", ".xml").toFile();
       writer.write(xml, out);
 
       xml = reader.read(out);
       validateAliases(xml);
       out.deleteOnExit();
@@ -1245,11 +1246,11 @@ public class BasicTest
       task1.setNotes("Task1 Notes: Some\rExample\nText\r\nWith\n\rBreaks");
 
       //
       // Write the file
       //
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(file, out);
 
       //
       // Ensure we can read it successfully
       //
@@ -1336,11 +1337,11 @@ public class BasicTest
       MSPDIWriter writer = new MSPDIWriter();
 
       ProjectFile xml = reader.read(MpxjTestData.filePath("legacy/mspextattr.xml"));
       commonMspdiExtendedAttributeTests(xml);
 
-      File out = File.createTempFile("junit", ".xml");
+      File out = Files.createTempFile("junit", ".xml").toFile();
       writer.write(xml, out);
 
       xml = reader.read(out);
       commonMspdiExtendedAttributeTests(xml);
 
@@ -1402,11 +1403,11 @@ public class BasicTest
 
       //
       // Write the file, re-read it and test to ensure that
       // the project properties have the expected values
       //
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       writer.write(mpx, out);
       mpx = reader.read(out);
       testProperties(mpx);
       out.deleteOnExit();
 
@@ -1433,11 +1434,11 @@ public class BasicTest
 
       //
       // Write the file, re-read it and test to ensure that
       // the project properties have the expected values
       //
-      out = File.createTempFile("junit", ".xml");
+      out = Files.createTempFile("junit", ".xml").toFile();
       new MSPDIWriter().write(mpx, out);
 
       mpx = new MSPDIReader().read(out);
       testMspdiProperties(mpx);
       out.deleteOnExit();
@@ -1510,17 +1511,17 @@ public class BasicTest
       validatePriority(mpp9);
 
       ProjectFile xml = new MSPDIReader().read(MpxjTestData.filePath("legacy/mspdipriority.xml"));
       validatePriority(xml);
 
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpx, out);
       ProjectFile mpx2 = new MPXReader().read(out);
       validatePriority(mpx2);
       out.deleteOnExit();
 
-      out = File.createTempFile("junit", ".xml");
+      out = Files.createTempFile("junit", ".xml").toFile();
       new MSPDIWriter().write(mpx, out);
       ProjectFile xml3 = new MSPDIReader().read(out);
       validatePriority(xml3);
       out.deleteOnExit();
    }
@@ -1718,11 +1719,11 @@ public class BasicTest
       mpp.getResources().renumberIDs();
 
       //
       // Write the file and re-read it to ensure we get consistent results.
       //
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       new MPXWriter().write(mpp, out);
 
       ProjectFile mpx = new MPXReader().read(out);
       assertEquals(6, mpx.getTasks().size());
       assertEquals(6, mpx.getResources().size());
@@ -1735,11 +1736,11 @@ public class BasicTest
     */
    @Test public void testProjectCalendarExceptions() throws Exception
    {
       File in = new File(MpxjTestData.filePath("legacy/calendarExceptions.mpx"));
       ProjectFile mpx = new MPXReader().read(in);
-      File out = File.createTempFile("junit", ".mpx");
+      File out = Files.createTempFile("junit", ".mpx").toFile();
       MPXWriter writer = new MPXWriter();
       writer.setUseLocaleDefaults(false);
       writer.write(mpx, out);
       boolean success = FileUtility.equals(in, out);
       assertTrue("Files are not identical", success);

Vulnerable code:       File dir = File.createTempFile("mpxj", "tmp");
         File file = File.createTempFile("mpxj", tempFileSuffix);
      File file = File.createTempFile("primavera", "db");
         File out = File.createTempFile("junit", suffix);
      File out = File.createTempFile("junit-" + locale.getLanguage(), ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".xml");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".planner");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      out = File.createTempFile("junit", ".xml");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".xml");
      File out = File.createTempFile("junit", ".xml");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".xml");
      File out = File.createTempFile("junit", ".mpx");
      out = File.createTempFile("junit", ".xml");
      File out = File.createTempFile("junit", ".mpx");
      out = File.createTempFile("junit", ".xml");
      File out = File.createTempFile("junit", ".mpx");
      File out = File.createTempFile("junit", ".mpx");
Fixed code: import java.nio.file.Files;
      File dir = Files.createTempFile("mpxj", "tmp").toFile();
import java.nio.file.Files;
         File file = Files.createTempFile("mpxj", tempFileSuffix).toFile();
      File file = Files.createTempFile("primavera", "db").toFile();
         File out = Files.createTempFile("junit", suffix).toFile();
import java.nio.file.Files;
      File out = Files.createTempFile("junit-" + locale.getLanguage(), ".mpx").toFile();
import java.nio.file.Files;
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".xml").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".planner").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      out = Files.createTempFile("junit", ".xml").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".xml").toFile();
      File out = Files.createTempFile("junit", ".xml").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".xml").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      out = Files.createTempFile("junit", ".xml").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      out = Files.createTempFile("junit", ".xml").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();
      File out = Files.createTempFile("junit", ".mpx").toFile();