This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-6701-5da2a719104856b988b8d0eb45fd71606ff111c4
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 08126417c16e2def054813a1fc1df50ebe16b75c
Author: Matthew B. <[email protected]>
AuthorDate: Tue Jul 21 18:57:47 2026 -0700

    test(file-service): cover DatasetResource path validation (#6701)
    
    ### What changes were proposed in this PR?
    - Add DatasetResourcePathSpec, a lightweight unit spec calling the
    companion validateAndNormalizeFilePathOrThrow directly.
    - Cover the empty-path, normalize-to-null, absolute-path, and
    normalized-relative-path branches.
    ### Any related issues, documentation, discussions?
    Closes: #6700
    ### How was this PR tested?
    - Run: `sbt "FileService/testOnly *DatasetResourcePathSpec"`, expect all
    7 tests passing.
    - Test-only change; no production code is modified.
    ### Was this PR authored or co-authored using generative AI tooling?
    Co-authored with Claude Opus 4.8 in compliance with ASF
---
 .../service/resource/DatasetResourcePathSpec.scala | 80 ++++++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git 
a/file-service/src/test/scala/org/apache/texera/service/resource/DatasetResourcePathSpec.scala
 
b/file-service/src/test/scala/org/apache/texera/service/resource/DatasetResourcePathSpec.scala
new file mode 100644
index 0000000000..63bea046f3
--- /dev/null
+++ 
b/file-service/src/test/scala/org/apache/texera/service/resource/DatasetResourcePathSpec.scala
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.service.resource
+
+import jakarta.ws.rs.BadRequestException
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+// Focused unit tests for the DatasetResource companion helper
+// validateAndNormalizeFilePathOrThrow, which guards every upload/lookup path.
+// These call the pure companion method directly and avoid the heavy
+// DatasetResourceSpec integration harness (DB + LakeFS).
+class DatasetResourcePathSpec extends AnyFlatSpec with Matchers {
+
+  "validateAndNormalizeFilePathOrThrow" should "reject a null path" in {
+    val ex = intercept[BadRequestException] {
+      DatasetResource.validateAndNormalizeFilePathOrThrow(null)
+    }
+    ex.getMessage shouldBe "Path cannot be empty"
+  }
+
+  it should "reject an empty path" in {
+    val ex = intercept[BadRequestException] {
+      DatasetResource.validateAndNormalizeFilePathOrThrow("")
+    }
+    ex.getMessage shouldBe "Path cannot be empty"
+  }
+
+  it should "reject a whitespace-only path" in {
+    val ex = intercept[BadRequestException] {
+      DatasetResource.validateAndNormalizeFilePathOrThrow("   ")
+    }
+    ex.getMessage shouldBe "Path cannot be empty"
+  }
+
+  it should "reject a path that normalizes to null (escapes above root)" in {
+    // FilenameUtils.normalize returns null when the path traverses above the
+    // root, e.g. a leading "../".
+    val ex = intercept[BadRequestException] {
+      DatasetResource.validateAndNormalizeFilePathOrThrow("../secret.txt")
+    }
+    ex.getMessage shouldBe "Invalid path"
+  }
+
+  it should "reject an absolute path" in {
+    val ex = intercept[BadRequestException] {
+      DatasetResource.validateAndNormalizeFilePathOrThrow("/etc/passwd")
+    }
+    ex.getMessage shouldBe "Absolute paths not allowed"
+  }
+
+  it should "return a normalized relative path unchanged when already clean" 
in {
+    DatasetResource.validateAndNormalizeFilePathOrThrow(
+      "california/irvine/tw1.csv"
+    ) shouldBe "california/irvine/tw1.csv"
+  }
+
+  it should "collapse interior '.' and '..' segments in a relative path" in {
+    DatasetResource.validateAndNormalizeFilePathOrThrow(
+      "a/./b/../c.csv"
+    ) shouldBe "a/c.csv"
+  }
+}

Reply via email to