summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReepca Russelstein <reepca@russelstein.xyz>2026-06-21 07:19:56 -0500
committerLudovic Courtès <ludo@gnu.org>2026-06-24 15:20:16 +0200
commit69b37a8e5d166328fa3d9a7c61b7ca1dfa772552 (patch)
tree933a8cbf82af5ecb1e58650c23d957567f90ad47
parent694785dc13840f36e9dda2fd413f0d6ac176f153 (diff)
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 <ludo@gnu.org>
-rw-r--r--nix/libstore/store-api.cc111
-rw-r--r--nix/libstore/store-api.hh18
-rw-r--r--nix/libstore/worker-protocol.hh3
3 files changed, 116 insertions, 16 deletions
diff --git a/nix/libstore/store-api.cc b/nix/libstore/store-api.cc
index 26d44f78b19..6445209652e 100644
--- a/nix/libstore/store-api.cc
+++ b/nix/libstore/store-api.cc
@@ -29,6 +29,71 @@ bool isStorePath(const Path & path)
29} 29}
30 30
31 31
32bool isStoreName(const string & name, string & problemDescription)
33{
34 const string validChars = "+-._?=";
35 if (name.empty()) {
36 problemDescription = "empty string is not a valid name";
37 return false;
38 }
39 /* Disallow names starting with a dot for possible security
40 reasons (e.g., "." and ".."). */
41 if (name.starts_with(".")) {
42 problemDescription = std::format("invalid name: `{}' (can't begin with dot)",
43 name);
44 return false;
45 }
46 for (const auto& i : name)
47 if (!((i >= 'A' && i <= 'Z') ||
48 (i >= 'a' && i <= 'z') ||
49 (i >= '0' && i <= '9') ||
50 validChars.find(i) != string::npos))
51 {
52 problemDescription = std::format("invalid character `{}' in name `{}'",
53 i,
54 name);
55 return false;
56 }
57 return true;
58}
59
60
61bool isStoreName(const string & name)
62{
63 string problemDescription; /* Placeholder */
64 return isStoreName(name, problemDescription);
65}
66
67
68void checkStoreName(const string & name)
69{
70 string problemDescription;
71 if (!isStoreName(name, problemDescription))
72 throw Error(problemDescription);
73}
74
75
76bool isStoreBasenameStrict(const string & name)
77{
78 /* 1. At least 34 characters long
79 2. First 32 characters are all nix-base32 characters
80 3. 33rd character (index 32) is a dash
81 4. 34th character (index 33) and those following it form a valid store
82 name. */
83 return name.size() >= 34
84 && isHash32(string(name, 0, 32))
85 && name[32] == '-'
86 && isStoreName(string(name, 33));
87}
88
89
90bool isStorePathStrict(const Path & path)
91{
92 return isStorePath(path)
93 && isStoreBasenameStrict(string(path, settings.nixStore.size() + 1));
94}
95
96
32void assertStorePath(const Path & path) 97void assertStorePath(const Path & path)
33{ 98{
34 if (!isStorePath(path)) 99 if (!isStorePath(path))
@@ -36,6 +101,13 @@ void assertStorePath(const Path & path)
36} 101}
37 102
38 103
104void assertStorePathStrict(const Path & path)
105{
106 if (!isStorePathStrict(path))
107 throw Error(std::format("path `{}' is not a valid store path", path));
108}
109
110
39Path toStorePath(const Path & path) 111Path toStorePath(const Path & path)
40{ 112{
41 if (!isInStore(path)) 113 if (!isInStore(path))
@@ -55,22 +127,7 @@ string storePathToName(const Path & path)
55} 127}
56 128
57 129
58void checkStoreName(const string & name) 130
59{
60 string validChars = "+-._?=";
61 /* Disallow names starting with a dot for possible security
62 reasons (e.g., "." and ".."). */
63 if (name.starts_with("."))
64 throw Error(std::format("invalid name: `{}' (can't begin with dot)", name));
65 for (const auto& i : name)
66 if (!((i >= 'A' && i <= 'Z') ||
67 (i >= 'a' && i <= 'z') ||
68 (i >= '0' && i <= '9') ||
69 validChars.find(i) != string::npos))
70 {
71 throw Error(std::format("invalid character `{}' in name `{}'", i, name));
72 }
73}
74 131
75 132
76/* Store paths have the following form: 133/* Store paths have the following form:
@@ -250,6 +307,28 @@ template<class T> T readStorePaths(Source & from)
250 return paths; 307 return paths;
251} 308}
252 309
310
253template PathSet readStorePaths(Source & from); 311template PathSet readStorePaths(Source & from);
254 312
313
314Path readStorePathStrict(Source & from)
315{
316 Path path = readString(from);
317 assertStorePathStrict(path);
318 return path;
319}
320
321
322template<class T> T readStorePathsStrict(Source & from)
323{
324 T paths = readStrings<T>(from);
325 for (auto& i : paths) assertStorePathStrict(i);
326 return paths;
327}
328
329
330template PathSet readStorePathsStrict(Source & from);
331
255} 332}
333
334
diff --git a/nix/libstore/store-api.hh b/nix/libstore/store-api.hh
index b57f1e10842..bc90d145a29 100644
--- a/nix/libstore/store-api.hh
+++ b/nix/libstore/store-api.hh
@@ -301,6 +301,12 @@ public:
301 301
302/* Throw an exception if `path' is not directly in the Nix store. */ 302/* Throw an exception if `path' is not directly in the Nix store. */
303void assertStorePath(const Path & path); 303void assertStorePath(const Path & path);
304/* Throw an exception if `path' is not a valid path for a store item. This
305 requires not only that it is directly in the store (like with
306 assertStorePath), but also that it has a valid hash part (32 nix-base32
307 characters) followed by a dash ('-') followed by a valid store name as per
308 isStoreName and checkStoreName. */
309void assertStorePathStrict(const Path & path);
304 310
305bool isInStore(const Path & path); 311bool isInStore(const Path & path);
306bool isStorePath(const Path & path); 312bool isStorePath(const Path & path);
@@ -308,8 +314,20 @@ bool isStorePath(const Path & path);
308/* Extract the name part of the given store path. */ 314/* Extract the name part of the given store path. */
309string storePathToName(const Path & path); 315string storePathToName(const Path & path);
310 316
317/* Return true iff `name' is a valid name for a store item. If false is
318 returned, `problemDescription' is set to a string suitable for use in an
319 Error exception. */
320bool isStoreName(const string & name, string & problemDescription);
321bool isStoreName(const string & name);
311void checkStoreName(const string & name); 322void checkStoreName(const string & name);
312 323
324/* Return true iff `name' is a valid basename for a store item's path, e.g. of
325 the form hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh-NAME, where 'h' represents a
326 nix-base32 character and NAME is a valid store name per isStoreName. */
327bool isStoreBasenameStrict(const string & name);
328/* Return true iff `path' is a valid path for a store item. */
329bool isStorePathStrict(const Path & path);
330
313 331
314/* Chop off the parts after the top-level store name, e.g., 332/* Chop off the parts after the top-level store name, e.g.,
315 /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */ 333 /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
diff --git a/nix/libstore/worker-protocol.hh b/nix/libstore/worker-protocol.hh
index ef259db2a01..8e3fb369193 100644
--- a/nix/libstore/worker-protocol.hh
+++ b/nix/libstore/worker-protocol.hh
@@ -59,5 +59,8 @@ typedef enum {
59Path readStorePath(Source & from); 59Path readStorePath(Source & from);
60template<class T> T readStorePaths(Source & from); 60template<class T> T readStorePaths(Source & from);
61 61
62Path readStorePathStrict(Source & from);
63template<class T> T readStorePathsStrict(Source & from);
64
62 65
63} 66}