guix_mirror_bot pushed a commit to branch master
in repository guix.

commit 69b37a8e5d166328fa3d9a7c61b7ca1dfa772552
Author: Reepca Russelstein <[email protected]>
AuthorDate: Sun Jun 21 07:19:56 2026 -0500

    daemon: libstore: add assertStorePathStrict and helpers.
    
    isStorePath is a lightweight check that only verifies that the given path is
    in the store and doesn't have any '/' characters after the store prefix.  
This
    is insufficiently strict for verifying that a given path is actually a valid
    store path.
    
    isStoreName is extracted from checkStoreName, and to preserve the error
    messages may have a second argument giving a string to write to with the
    problem description in the case that isStoreName returns false.
    
    * nix/libstore/store-api.hh (assertStorePathStrict, isStoreName,
      isStoreBasenameStrict, isStorePathStrict): new functions.
    * nix/libstore/worker-protocol.hh (readStorePathStrict): new function.
      (readStorePathsStrict): new template.
    * nix/libstore/store-api.cc (assertStorePathStrict, isStoreName,
      isStoreBasenameStrict, isStorePathStrict, readStorePathStrict,
      readStorePathsStrict): provide implementations.
    
    Signed-off-by: Ludovic Courtès <[email protected]>
---
 nix/libstore/store-api.cc       | 111 ++++++++++++++++++++++++++++++++++------
 nix/libstore/store-api.hh       |  18 +++++++
 nix/libstore/worker-protocol.hh |   3 ++
 3 files changed, 116 insertions(+), 16 deletions(-)

diff --git a/nix/libstore/store-api.cc b/nix/libstore/store-api.cc
index 26d44f78b1..6445209652 100644
--- a/nix/libstore/store-api.cc
+++ b/nix/libstore/store-api.cc
@@ -29,6 +29,71 @@ bool isStorePath(const Path & path)
 }
 
 
+bool isStoreName(const string & name, string & problemDescription)
+{
+    const string validChars = "+-._?=";
+    if (name.empty()) {
+        problemDescription = "empty string is not a valid name";
+        return false;
+    }
+    /* Disallow names starting with a dot for possible security
+       reasons (e.g., "." and ".."). */
+    if (name.starts_with(".")) {
+        problemDescription = std::format("invalid name: `{}' (can't begin with 
dot)",
+                                         name);
+        return false;
+    }
+    for (const auto& i : name)
+        if (!((i >= 'A' && i <= 'Z') ||
+              (i >= 'a' && i <= 'z') ||
+              (i >= '0' && i <= '9') ||
+              validChars.find(i) != string::npos))
+        {
+            problemDescription = std::format("invalid character `{}' in name 
`{}'",
+                                             i,
+                                             name);
+            return false;
+        }
+    return true;
+}
+
+
+bool isStoreName(const string & name)
+{
+    string problemDescription; /* Placeholder */
+    return isStoreName(name, problemDescription);
+}
+
+
+void checkStoreName(const string & name)
+{
+    string problemDescription;
+    if (!isStoreName(name, problemDescription))
+        throw Error(problemDescription);
+}
+
+
+bool isStoreBasenameStrict(const string & name)
+{
+    /* 1. At least 34 characters long
+       2. First 32 characters are all nix-base32 characters
+       3. 33rd character (index 32) is a dash
+       4. 34th character (index 33) and those following it form a valid store
+          name. */
+    return name.size() >= 34
+        && isHash32(string(name, 0, 32))
+        && name[32] == '-'
+        && isStoreName(string(name, 33));
+}
+
+
+bool isStorePathStrict(const Path & path)
+{
+    return isStorePath(path)
+        && isStoreBasenameStrict(string(path, settings.nixStore.size() + 1));
+}
+
+
 void assertStorePath(const Path & path)
 {
     if (!isStorePath(path))
@@ -36,6 +101,13 @@ void assertStorePath(const Path & path)
 }
 
 
+void assertStorePathStrict(const Path & path)
+{
+    if (!isStorePathStrict(path))
+        throw Error(std::format("path `{}' is not a valid store path", path));
+}
+
+
 Path toStorePath(const Path & path)
 {
     if (!isInStore(path))
@@ -55,22 +127,7 @@ string storePathToName(const Path & path)
 }
 
 
-void checkStoreName(const string & name)
-{
-    string validChars = "+-._?=";
-    /* Disallow names starting with a dot for possible security
-       reasons (e.g., "." and ".."). */
-    if (name.starts_with("."))
-        throw Error(std::format("invalid name: `{}' (can't begin with dot)", 
name));
-    for (const auto& i : name)
-        if (!((i >= 'A' && i <= 'Z') ||
-              (i >= 'a' && i <= 'z') ||
-              (i >= '0' && i <= '9') ||
-              validChars.find(i) != string::npos))
-        {
-            throw Error(std::format("invalid character `{}' in name `{}'", i, 
name));
-        }
-}
+
 
 
 /* Store paths have the following form:
@@ -250,6 +307,28 @@ template<class T> T readStorePaths(Source & from)
     return paths;
 }
 
+
 template PathSet readStorePaths(Source & from);
 
+
+Path readStorePathStrict(Source & from)
+{
+    Path path = readString(from);
+    assertStorePathStrict(path);
+    return path;
+}
+
+
+template<class T> T readStorePathsStrict(Source & from)
+{
+    T paths = readStrings<T>(from);
+    for (auto& i : paths) assertStorePathStrict(i);
+    return paths;
+}
+
+
+template PathSet readStorePathsStrict(Source & from);
+
 }
+
+
diff --git a/nix/libstore/store-api.hh b/nix/libstore/store-api.hh
index b57f1e1084..bc90d145a2 100644
--- a/nix/libstore/store-api.hh
+++ b/nix/libstore/store-api.hh
@@ -301,6 +301,12 @@ public:
 
 /* Throw an exception if `path' is not directly in the Nix store. */
 void assertStorePath(const Path & path);
+/* Throw an exception if `path' is not a valid path for a store item.  This
+   requires not only that it is directly in the store (like with
+   assertStorePath), but also that it has a valid hash part (32 nix-base32
+   characters) followed by a dash ('-') followed by a valid store name as per
+   isStoreName and checkStoreName. */
+void assertStorePathStrict(const Path & path);
 
 bool isInStore(const Path & path);
 bool isStorePath(const Path & path);
@@ -308,8 +314,20 @@ bool isStorePath(const Path & path);
 /* Extract the name part of the given store path. */
 string storePathToName(const Path & path);
 
+/* Return true iff `name' is a valid name for a store item.  If false is
+   returned, `problemDescription' is set to a string suitable for use in an
+   Error exception. */
+bool isStoreName(const string & name, string & problemDescription);
+bool isStoreName(const string & name);
 void checkStoreName(const string & name);
 
+/* Return true iff `name' is a valid basename for a store item's path, e.g. of
+   the form hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh-NAME, where 'h' represents a
+   nix-base32 character and NAME is a valid store name per isStoreName. */
+bool isStoreBasenameStrict(const string & name);
+/* Return true iff `path' is a valid path for a store item. */
+bool isStorePathStrict(const Path & path);
+
 
 /* Chop off the parts after the top-level store name, e.g.,
    /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
diff --git a/nix/libstore/worker-protocol.hh b/nix/libstore/worker-protocol.hh
index ef259db2a0..8e3fb36919 100644
--- a/nix/libstore/worker-protocol.hh
+++ b/nix/libstore/worker-protocol.hh
@@ -59,5 +59,8 @@ typedef enum {
 Path readStorePath(Source & from);
 template<class T> T readStorePaths(Source & from);
 
+Path readStorePathStrict(Source & from);
+template<class T> T readStorePathsStrict(Source & from);
+
 
 }

Reply via email to