projects/guix·commit 69b37a8e

daemon: libstore: add assertStorePathStrict and helpers.

69b37a8e · Reepca Russelstein <[email protected]> · 2026-06-21 07:19:56 -05:00

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]>
Commit
69b37a8e5d166328fa3d9a7c61b7ca1dfa772552
Parents
694785dc

Changes

3 files changed · +116 −16

FileStatusAddDelContent
nix/libstore/store-api.ccmodified+95−16View file
nix/libstore/store-api.hhmodified+18−0View file
nix/libstore/worker-protocol.hhmodified+3−0View file

nix/libstore/store-api.cc

View file
diff --git a/nix/libstore/store-api.cc b/nix/libstore/store-api.cc
index 26d44f7..6445209 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);
+
}
+
+

nix/libstore/store-api.hh

View file
diff --git a/nix/libstore/store-api.hh b/nix/libstore/store-api.hh
index b57f1e1..bc90d14 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. */

nix/libstore/worker-protocol.hh

View file
diff --git a/nix/libstore/worker-protocol.hh b/nix/libstore/worker-protocol.hh
index ef259db..8e3fb36 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);
+
}