From f6812f151878c48fc62fb0f40b8926de5a93a7bd Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Mon, 27 Jul 2026 13:04:19 +0000
Subject: [PATCH] Fix java/zipslip CodeQL alerts by validating archive entry paths (#761)
---
opendj-server-legacy/src/test/java/org/opends/server/util/BackupManagerTestCase.java | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/util/BackupManagerTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/util/BackupManagerTestCase.java
index 31b7883..9c80de3 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/util/BackupManagerTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/util/BackupManagerTestCase.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.util;
@@ -28,6 +29,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.DirectoryServerTestCase;
@@ -35,6 +38,7 @@
import org.opends.server.api.Backupable;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
+import org.opends.server.types.DirectoryException;
import org.opends.server.types.RestoreConfig;
import org.testng.Reporter;
import org.testng.annotations.BeforeClass;
@@ -214,6 +218,47 @@
cleanDirectories(sourceDirectory, backupPath);
}
+ /**
+ * A restore must reject archive entries which resolve outside of the restore
+ * directory (zip slip) and must not write any file outside of it.
+ */
+ @Test
+ public void testRestoreRejectsZipSlipEntries() throws Exception
+ {
+ String label = "zipslip";
+ Path sourceDirectory = createSourceDirectory(label);
+ Backupable backupable = buildBackupable(sourceDirectory, 1);
+ BackupDirectory backupDir = buildBackupDir(label);
+ BackupConfig backupConfig = new BackupConfig(backupDir, BACKUP_ID, false);
+ RestoreConfig restoreConfig = new RestoreConfig(backupDir, BACKUP_ID, false);
+
+ BackupManager backupManager = new BackupManager(BACKEND_ID);
+ backupManager.createBackup(backupable, backupConfig);
+
+ // replace the archive with one containing an entry escaping the restore directory
+ File archiveFile = new File(backupDir.getPath(), getArchiveFileName(BACKUP_ID));
+ try (ZipOutputStream zipOutput = new ZipOutputStream(new FileOutputStream(archiveFile)))
+ {
+ zipOutput.putNextEntry(new ZipEntry("../evil.txt"));
+ zipOutput.write(getBytes("evil"));
+ zipOutput.closeEntry();
+ }
+
+ File escapedFile = new File(sourceDirectory.toFile().getParentFile(), "evil.txt");
+ try
+ {
+ backupManager.restoreBackup(backupable, restoreConfig);
+ fail("Expected restoreBackup to reject a zip entry escaping the restore directory");
+ }
+ catch (DirectoryException expected)
+ {
+ assertThat(expected.getMessage()).contains("evil.txt");
+ }
+ assertThat(escapedFile).doesNotExist();
+
+ cleanDirectories(sourceDirectory, backupDir.getPath());
+ }
+
@Test
public void testCreateDirectoryWithNumericSuffix() throws Exception
{
--
Gitblit v1.10.0