You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@storm.apache.org by rz...@apache.org on 2026/08/19 11:55:41 UTC
(storm) branch fix/nimbus-submitted-jar-location-check created (now 06f6d631d)
This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a change to branch fix/nimbus-submitted-jar-location-check
in repository https://gitbox.apache.org/repos/asf/storm.git
at 06f6d631d Reject submitted jar locations that are not inside the Nimbus inbox
This branch includes the following new commits:
new 06f6d631d Reject submitted jar locations that are not inside the Nimbus inbox
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(storm) 01/01: Reject submitted jar locations that are not inside the Nimbus inbox
Posted by rz...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a commit to branch fix/nimbus-submitted-jar-location-check
in repository https://gitbox.apache.org/repos/asf/storm.git
commit 06f6d631d0c42e05dafcdc15e743feaf13aeebbf
Author: Richard Zowalla <rz...@apache.org>
AuthorDate: Wed Aug 19 13:27:11 2026 +0200
Reject submitted jar locations that are not inside the Nimbus inbox
---
.../org/apache/storm/daemon/nimbus/Nimbus.java | 24 +++++++++++++
.../org/apache/storm/daemon/nimbus/NimbusTest.java | 39 ++++++++++++++++++++++
2 files changed, 63 insertions(+)
diff --git a/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java b/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java
index 0463c78a3..f59b27943 100644
--- a/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java
+++ b/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java
@@ -40,6 +40,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1835,6 +1836,28 @@ public class Nimbus implements Iface, Shutdownable, DaemonCommon {
return ServerConfigUtils.masterInbox(conf);
}
+ /**
+ * Check that a client supplied jar location names a file inside the nimbus inbox, i.e. one that was handed out by
+ * beginFileUpload and written through uploadChunk/finishFileUpload. Both paths are canonicalized first so that
+ * ".." segments and symlinks cannot point outside of the inbox.
+ *
+ * @param inboxLocation the nimbus inbox directory
+ * @param uploadedJarLocation the client supplied jar location
+ * @throws AuthorizationException if uploadedJarLocation is not inside the inbox
+ * @throws IOException if the paths could not be resolved
+ */
+ @VisibleForTesting
+ static void validateUploadedJarLocation(String inboxLocation, String uploadedJarLocation)
+ throws AuthorizationException, IOException {
+ Path inboxDir = new File(inboxLocation).getCanonicalFile().toPath();
+ Path uploadedJar = new File(uploadedJarLocation).getCanonicalFile().toPath();
+ if (uploadedJar.equals(inboxDir) || !uploadedJar.startsWith(inboxDir)) {
+ throw new WrappedAuthorizationException("uploadedJarLocation " + uploadedJarLocation
+ + " is not inside the nimbus inbox. Topology jars must be uploaded through beginFileUpload/uploadChunk"
+ + "/finishFileUpload before the topology is submitted.");
+ }
+ }
+
/**
* Used for local cluster.
*
@@ -1927,6 +1950,7 @@ public class Nimbus implements Iface, Shutdownable, DaemonCommon {
String jarKey = ConfigUtils.masterStormJarKey(topoId);
if (tmpJarLocation != null) {
//in local mode there is no jar
+ validateUploadedJarLocation(getInbox(), tmpJarLocation);
try (FileInputStream fin = new FileInputStream(tmpJarLocation)) {
store.createBlob(jarKey, fin, new SettableBlobMeta(BlobStoreAclHandler.DEFAULT), subject);
}
diff --git a/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java b/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java
index 792ec3aa1..2380d49a8 100644
--- a/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java
+++ b/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java
@@ -18,17 +18,22 @@
package org.apache.storm.daemon.nimbus;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import org.apache.commons.io.FileUtils;
import org.apache.storm.Config;
import org.apache.storm.DaemonConfig;
import org.apache.storm.blobstore.BlobStore;
import org.apache.storm.blobstore.KeySequenceNumber;
import org.apache.storm.blobstore.LocalFsBlobStore;
import org.apache.storm.cluster.IStormClusterState;
+import org.apache.storm.generated.AuthorizationException;
import org.apache.storm.generated.InvalidTopologyException;
import org.apache.storm.generated.KeyNotFoundException;
import org.apache.storm.generated.StormTopology;
@@ -200,4 +205,38 @@ class NimbusTest {
verify(stormClusterState, never()).setupBlob(eq(BLOB_FILE_KEY), eq(nimbusInfo), any());
}
}
+
+ @Test
+ void testValidateUploadedJarLocationRejectsLocationsOutsideTheInbox() throws Exception {
+ Path inbox = Files.createTempDirectory("nimbus-inbox");
+ Path sibling = Paths.get(inbox + "evil");
+ try {
+ Path jar = Files.write(inbox.resolve("stormjar-cafebabe.jar"), new byte[]{ 1 });
+ Path outside = Files.write(Files.createDirectory(sibling).resolve("stormjar-cafebabe.jar"), new byte[]{ 1 });
+
+ // a location handed out by beginFileUpload is accepted, and so is one that only walks inside the inbox
+ Nimbus.validateUploadedJarLocation(inbox.toString(), jar.toString());
+ Files.createDirectory(inbox.resolve("nested"));
+ Nimbus.validateUploadedJarLocation(inbox.toString(), inbox + "/nested/../stormjar-cafebabe.jar");
+
+ // an absolute path elsewhere, a ".." walk out of the inbox, the inbox itself and a sibling directory
+ // whose name merely starts with the inbox path are all rejected
+ assertThrows(AuthorizationException.class,
+ () -> Nimbus.validateUploadedJarLocation(inbox.toString(), "/etc/passwd"));
+ assertThrows(AuthorizationException.class,
+ () -> Nimbus.validateUploadedJarLocation(inbox.toString(), inbox + "/../../etc/passwd"));
+ assertThrows(AuthorizationException.class,
+ () -> Nimbus.validateUploadedJarLocation(inbox.toString(), inbox.toString()));
+ assertThrows(AuthorizationException.class,
+ () -> Nimbus.validateUploadedJarLocation(inbox.toString(), outside.toString()));
+
+ // a symlink inside the inbox pointing back out of it is rejected too
+ Path link = Files.createSymbolicLink(inbox.resolve("stormjar-link.jar"), outside);
+ assertThrows(AuthorizationException.class,
+ () -> Nimbus.validateUploadedJarLocation(inbox.toString(), link.toString()));
+ } finally {
+ FileUtils.deleteQuietly(inbox.toFile());
+ FileUtils.deleteQuietly(sibling.toFile());
+ }
+ }
}