about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/castore/src/path.rs17
-rw-r--r--tvix/castore/src/proto/mod.rs2
2 files changed, 13 insertions, 6 deletions
diff --git a/tvix/castore/src/path.rs b/tvix/castore/src/path.rs
index 2da1413c4836..4a6d82f68158 100644
--- a/tvix/castore/src/path.rs
+++ b/tvix/castore/src/path.rs
@@ -10,6 +10,8 @@ use std::{
 
 use bstr::ByteSlice;
 
+use crate::proto::validate_node_name;
+
 /// Represents a Path in the castore model.
 /// These are always relative, and platform-independent, which distinguishes
 /// them from the ones provided in the standard library.
@@ -34,12 +36,9 @@ impl Path {
 
     fn from_bytes(bytes: &[u8]) -> Option<&Path> {
         if !bytes.is_empty() {
-            // Ensure there's no empty components (aka, double forward slashes),
-            // and all components individually validate.
+            // Ensure all components are valid castore node names.
             for component in bytes.split_str(b"/") {
-                if component.is_empty() {
-                    return None;
-                }
+                validate_node_name(component).ok()?;
             }
         }
 
@@ -201,6 +200,14 @@ mod test {
     #[case::two_forward_slashes_start("//a/b")]
     #[case::two_forward_slashes_middle("a/b//c/d")]
     #[case::trailing_slash("a/b/")]
+    #[case::dot(".")]
+    #[case::dotdot("..")]
+    #[case::dot_start("./a")]
+    #[case::dotdot_start("../a")]
+    #[case::dot_middle("a/./b")]
+    #[case::dotdot_middle("a/../b")]
+    #[case::dot_end("a/b/.")]
+    #[case::dotdot_end("a/b/..")]
     pub fn from_str_fail(#[case] s: &str) {
         s.parse::<PathBuf>().expect_err("must fail");
     }
diff --git a/tvix/castore/src/proto/mod.rs b/tvix/castore/src/proto/mod.rs
index 39c1bcc6faa0..5374e3ae5a80 100644
--- a/tvix/castore/src/proto/mod.rs
+++ b/tvix/castore/src/proto/mod.rs
@@ -66,7 +66,7 @@ pub enum ValidateStatBlobResponseError {
 
 /// Checks a Node name for validity as an intermediate node.
 /// We disallow slashes, null bytes, '.', '..' and the empty string.
-fn validate_node_name(name: &[u8]) -> Result<(), ValidateNodeError> {
+pub(crate) fn validate_node_name(name: &[u8]) -> Result<(), ValidateNodeError> {
     if name.is_empty()
         || name == b".."
         || name == b"."