Author: msahyoun
Date: Thu Apr  2 09:06:14 2026
New Revision: 1932731

Log:
PDFBOX-6185: improve handling of files in temp directory; Windows settings by 
Claude Sonnet 4.6

Modified:
   
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java
   pdfbox/branches/3.0/io/src/main/java/org/apache/pdfbox/io/IOUtils.java

Modified: 
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java
==============================================================================
--- 
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java
  Thu Apr  2 08:53:31 2026        (r1932730)
+++ 
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java
  Thu Apr  2 09:06:14 2026        (r1932731)
@@ -41,6 +41,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.List;
@@ -59,6 +60,11 @@ public class Tree extends JTree
     private final JPopupMenu treePopupMenu;
     private final Object rootNode;
 
+    // Temporary files are stored in a private temp directory with restricted 
permissions,
+    // which is deleted on exit using a shutdown hook.
+    // PDFBOX-6185
+    private Path tempDir;
+
     /**
      * Constructor.
      */
@@ -294,8 +300,11 @@ public class Tree extends JTree
         {
             try
             {
-                File temp = Files.createTempFile("pdfbox", "." + 
extension).toFile();
-                temp.deleteOnExit();
+                if (tempDir == null)
+                {
+                    tempDir = IOUtils.createProtectedTempDir();
+                }
+                File temp = Files.createTempFile(tempDir, "pdfbox", "." + 
extension).toFile();
 
                 try (InputStream is = cosStream.createInputStream())
                 {

Modified: pdfbox/branches/3.0/io/src/main/java/org/apache/pdfbox/io/IOUtils.java
==============================================================================
--- pdfbox/branches/3.0/io/src/main/java/org/apache/pdfbox/io/IOUtils.java      
Thu Apr  2 08:53:31 2026        (r1932730)
+++ pdfbox/branches/3.0/io/src/main/java/org/apache/pdfbox/io/IOUtils.java      
Thu Apr  2 09:06:14 2026        (r1932731)
@@ -29,6 +29,7 @@ import static java.util.Objects.nonNull;
 
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -38,11 +39,23 @@ import java.lang.invoke.MethodHandles.Lo
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclEntryPermission;
+import java.nio.file.attribute.AclEntryType;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.nio.file.attribute.UserPrincipal;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Consumer;
+import java.util.stream.Stream;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -335,4 +348,93 @@ public final class IOUtils
     {
         return MemoryUsageSetting.setupTempFileOnly().streamCache;
     }
+
+    /**
+     * Creates a temporary directory in the default temporary-file directory 
+     * with owner-only permissions and registers a shutdown hook to delete it 
on JVM exit.
+     * 
+     * <p>Note: This method is designed to be used for storing temporary files 
that may contain sensitive data
+     * in a temporary directories with restricted permissions, to mitigate the 
risk of unauthorized access by
+     * other users or processes on the same system. Used e.g. by 
PDFDebugger.</p>
+     * 
+     * @return the path to the created temporary directory
+     * @throws IOException 
+     */
+    public static Path createProtectedTempDir() throws IOException
+    {
+        // S5443: permissions are immediately restricted to owner-only by
+        // applyOwnerOnlyPermissions(), mitigating the default-permission risk.
+        @SuppressWarnings("java:S5443")
+        Path tempPath = Files.createTempDirectory("pdfbox-");
+        applyOwnerOnlyPermissions(tempPath);
+
+        // use shutdown hook instead of deleteOnExit() to ensure deletion
+        // of the entire directory in case of not automatically deleted on 
+        // JVM exit (e.g. due to open file handles or when the temp directory 
is not empty)
+        Runtime.getRuntime().addShutdownHook(new Thread(() ->
+        {
+            try (Stream<Path> entries = Files.walk(tempPath))
+            {
+                entries.sorted(Comparator.reverseOrder())
+                    .forEach(p -> p.toFile().delete());
+            }
+            catch (IOException ignored) {}
+        }));
+
+        return tempPath;
+    }
+
+    private static void applyOwnerOnlyPermissions(Path dir) throws IOException
+    {
+        if 
(FileSystems.getDefault().supportedFileAttributeViews().contains("posix"))
+        {
+            // Unix/macOS — rwx------
+            Files.setPosixFilePermissions(dir, 
PosixFilePermissions.fromString("rwx------"));
+        }
+        else
+        {
+            // Windows — replace the entire ACL with a single owner-only ALLOW 
entry
+            AclFileAttributeView aclView =
+            Files.getFileAttributeView(dir, AclFileAttributeView.class);
+
+            if (aclView == null)
+            {
+                File tempDir = dir.toFile();
+                boolean isReadable = tempDir.setReadable(true, true);
+                boolean isWritable = tempDir.setWritable(true, true);
+                boolean isExecutable = tempDir.setExecutable(true, true);
+                if (!isReadable || !isWritable || !isExecutable)
+                {
+                    LOG.warn("Unable to set owner-only permissions on 
temporary directory: " + dir +
+                            ". Please ensure that the temporary directory is 
protected against unauthorized access.");
+                }
+                return;
+            }
+
+            UserPrincipal owner = aclView.getOwner();
+
+            AclEntry ownerFullControl = AclEntry.newBuilder()
+                .setType(AclEntryType.ALLOW)
+                .setPrincipal(owner)
+                .setPermissions(
+                    AclEntryPermission.READ_DATA,
+                    AclEntryPermission.WRITE_DATA,
+                    AclEntryPermission.APPEND_DATA,
+                    AclEntryPermission.READ_NAMED_ATTRS,
+                    AclEntryPermission.WRITE_NAMED_ATTRS,
+                    AclEntryPermission.EXECUTE,
+                    AclEntryPermission.DELETE_CHILD,
+                    AclEntryPermission.READ_ATTRIBUTES,
+                    AclEntryPermission.WRITE_ATTRIBUTES,
+                    AclEntryPermission.DELETE,
+                    AclEntryPermission.READ_ACL,
+                    AclEntryPermission.WRITE_ACL,
+                    AclEntryPermission.SYNCHRONIZE
+                )
+                .build();
+
+            // Set so that only the owner has permissions, and remove any 
inherited ACL entries
+            aclView.setAcl(Collections.singletonList(ownerFullControl));
+        }
+    }
 }

Reply via email to