You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ho...@apache.org on 2026/09/01 08:35:53 UTC
(spark) branch branch-4.3 updated: Validate app store path in history server disk management
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch branch-4.3
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.3 by this push:
new 4ce42aa585d7 Validate app store path in history server disk management
4ce42aa585d7 is described below
commit 4ce42aa585d71845bf1d844a490087926b2e46b2
Author: Holden Karau <ho...@snowflake.com>
AuthorDate: Sun Aug 30 05:59:54 2026 +0000
Validate app store path in history server disk management
Co-authored-by: Holden Karau <ho...@pigscanfly.ca>
(cherry picked from commit e89662931e265d3dead9864e1235b6e40edf0d2a)
---
.../spark/deploy/history/HistoryServerDiskManager.scala | 11 +++++++++--
.../deploy/history/HistoryServerDiskManagerSuite.scala | 17 +++++++++++++++++
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/core/src/main/scala/org/apache/spark/deploy/history/HistoryServerDiskManager.scala b/core/src/main/scala/org/apache/spark/deploy/history/HistoryServerDiskManager.scala
index f5abedf76362..35cdd7880f23 100644
--- a/core/src/main/scala/org/apache/spark/deploy/history/HistoryServerDiskManager.scala
+++ b/core/src/main/scala/org/apache/spark/deploy/history/HistoryServerDiskManager.scala
@@ -266,8 +266,15 @@ private class HistoryServerDiskManager(
}
private[history] def appStorePath(appId: String, attemptId: Option[String]): File = {
- val fileName = appId + attemptId.map("_" + _).getOrElse("") + extension
- new File(appStoreDir, fileName)
+ val fileName = Utils.sanitizeDirName(appId) +
+ attemptId.map("_" + Utils.sanitizeDirName(_)).getOrElse("") + extension
+ val storePath = new File(appStoreDir, fileName)
+ // Validate the app store path is valid
+ if (storePath.getCanonicalFile.getParentFile != appStoreDir.getCanonicalFile) {
+ throw new IllegalArgumentException(
+ s"Store path for app $appId / $attemptId escapes the application store directory")
+ }
+ storePath
}
private def updateApplicationStoreInfo(
diff --git a/core/src/test/scala/org/apache/spark/deploy/history/HistoryServerDiskManagerSuite.scala b/core/src/test/scala/org/apache/spark/deploy/history/HistoryServerDiskManagerSuite.scala
index 24d509988404..c82a0e4321d3 100644
--- a/core/src/test/scala/org/apache/spark/deploy/history/HistoryServerDiskManagerSuite.scala
+++ b/core/src/test/scala/org/apache/spark/deploy/history/HistoryServerDiskManagerSuite.scala
@@ -257,6 +257,23 @@ abstract class HistoryServerDiskManagerSuite extends SparkFunSuite with BeforeAn
val manager = new HistoryServerDiskManager(conf, testDir, store, new ManualClock())
assert(manager.appStorePath("appId", None).getName.endsWith(extension))
}
+
+ test("appStorePath neutralizes path traversal in log-derived app and attempt ids") {
+ val conf = new SparkConf().set(HYBRID_STORE_DISK_BACKEND, backend.toString)
+ val manager = new HistoryServerDiskManager(conf, testDir, store, new ManualClock())
+ val appsDir = new File(testDir, "apps").getCanonicalFile
+
+ // Legitimate cluster-manager-generated ids keep their store name.
+ assert(manager.appStorePath("app-20260811120000-0001", Some("1")).getName ===
+ "app-20260811120000-0001_1" + extension)
+
+ // Reject the non-standard paths
+ Seq("../listing", "..", "../../tmp/evil", "a/b").foreach { appId =>
+ val storePath = manager.appStorePath(appId, Some("../1"))
+ assert(storePath.getCanonicalFile.getParentFile === appsDir,
+ s"appId '$appId' escaped the store directory: $storePath")
+ }
+ }
}
@ExtendedLevelDBTest
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org