summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2026-06-08 11:02:59 +0200
committerLudovic Courtès <ludo@gnu.org>2026-07-02 19:42:47 +0200
commit652e0c9110cae669767318dac7c09bcd4f56ce75 (patch)
tree581edf096126ae95aff4a3d97f20bcb080a3b095
parent26d7eb8a4adafc648ef035e91b6bbc4945d4c962 (diff)
store: Add ‘valid-path-syntax?’ and ‘valid-store-name?’.
* guix/store.scm (store-path?): Update docstring. (%store-item-charset): New variable. (valid-store-name?, valid-path-basename-syntax?, valid-path-syntax?): New procedures. * tests/store.scm ("valid-path-syntax?") ("valid-path-syntax? truncated hash") ("valid-path-syntax? slash") ("valid-path-syntax? leading dot") ("valid-path-syntax? prefix") ("valid-path-syntax? truncated"): New tests. Change-Id: Ic808dd5a8270fbea20e9c99c2861f30133d13b41 Signed-off-by: Ludovic Courtès <ludo@gnu.org>
-rw-r--r--guix/store.scm46
-rw-r--r--tests/store.scm24
2 files changed, 68 insertions, 2 deletions
diff --git a/guix/store.scm b/guix/store.scm
index ec72786f347..ad2df17701d 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012-2025 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2012-2026 Ludovic Courtès <ludo@gnu.org>
3;;; Copyright © 2018 Jan Nieuwenhuizen <janneke@gnu.org> 3;;; Copyright © 2018 Jan Nieuwenhuizen <janneke@gnu.org>
4;;; Copyright © 2019, 2020 Mathieu Othacehe <m.othacehe@gmail.com> 4;;; Copyright © 2019, 2020 Mathieu Othacehe <m.othacehe@gmail.com>
5;;; Copyright © 2020 Florian Pelz <pelzflorian@pelzflorian.de> 5;;; Copyright © 2020 Florian Pelz <pelzflorian@pelzflorian.de>
@@ -204,6 +204,9 @@
204 output-path 204 output-path
205 fixed-output-path 205 fixed-output-path
206 store-path? 206 store-path?
207 valid-path-syntax?
208 valid-path-basename-syntax?
209 valid-store-name?
207 direct-store-path? 210 direct-store-path?
208 derivation-path? 211 derivation-path?
209 store-path-base 212 store-path-base
@@ -1962,7 +1965,10 @@ HASH-ALGO, of the derivation NAME. RECURSIVE? has the same meaning as for
1962 name)))) 1965 name))))
1963 1966
1964(define (store-path? path) 1967(define (store-path? path)
1965 "Return #t if PATH is a store path." 1968 "Return #t if PATH is a store path.
1969
1970This is a lightweight check. Use 'valid-path-syntax?' to validate untrusted
1971input."
1966 ;; This is a lightweight check, compared to using a regexp, but this has to 1972 ;; This is a lightweight check, compared to using a regexp, but this has to
1967 ;; be fast as it's called often in `derivation', for instance. 1973 ;; be fast as it's called often in `derivation', for instance.
1968 ;; `isStorePath' in Nix does something similar. 1974 ;; `isStorePath' in Nix does something similar.
@@ -1998,6 +2004,42 @@ valid inputs."
1998 (not (string-index base #\/)) 2004 (not (string-index base #\/))
1999 base)))) 2005 base))))
2000 2006
2007(define %store-item-charset
2008 ;; Valid characters for the name of a store item.
2009 (string->char-set (string-append "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2010 "abcdefghijklmnopqrstuvwxyz"
2011 "0123456789" "+-._?=")))
2012
2013(define (valid-store-name? name)
2014 "Return true if NAME is syntactically a valid store file name--i.e., a name
2015that would be accepted by 'add-to-store' & co."
2016 ;; Like 'checkStoreName'.
2017 (and (not (string-null? name))
2018 (not (string-prefix? "." name))
2019 (string-every %store-item-charset name)))
2020
2021(define (valid-path-basename-syntax? item)
2022 "Return true if ITEM has a valid syntax as the basename of a store item."
2023 (define hash-len 32)
2024
2025 (and (> (string-length item) (+ hash-len 1))
2026 (string-every %nix-base32-charset item 0 hash-len)
2027 (eqv? (string-ref item hash-len) #\-)
2028 (valid-store-name? (string-drop item (+ hash-len 1)))))
2029
2030(define (valid-path-syntax? path)
2031 "Return true if PATH is syntactically a valid store path. Unlike
2032'store-path?', this can be used to validate untrusted input.
2033
2034This must not be confused with 'valid-path?'."
2035 (define prefix-len
2036 (string-length (%store-prefix)))
2037
2038 (and (> (string-length path) (+ prefix-len 1))
2039 (string-prefix? (%store-prefix) path)
2040 (eq? (string-ref path prefix-len) #\/)
2041 (valid-path-basename-syntax? (string-drop path (+ prefix-len 1)))))
2042
2001(define (store-path-package-name path) 2043(define (store-path-package-name path)
2002 "Return the package name part of PATH, a file name in the store." 2044 "Return the package name part of PATH, a file name in the store."
2003 (let ((base (store-path-base path))) 2045 (let ((base (store-path-base path)))
diff --git a/tests/store.scm b/tests/store.scm
index 87773bf0c36..e7ba7469047 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -78,6 +78,30 @@
78 (open-connection #f #:port port) 78 (open-connection #f #:port port)
79 'broken))) 79 'broken)))
80 80
81(test-assert "valid-path-syntax?"
82 (valid-path-syntax? (string-append (%store-prefix)
83 "/yp8mg1rwaw3y1p09bc8dx4l6rx4qx961-coreutils-9.1")))
84
85(test-assert "valid-path-syntax? truncated hash"
86 (not (valid-path-syntax?
87 (string-append (%store-prefix)
88 "/p8mg1rwaw3y1p09bc8dx4l6rx4qx961-coreutils-9.1"))))
89
90(test-assert "valid-path-syntax? slash"
91 (not (valid-path-syntax?
92 (string-append (%store-prefix)
93 "/yp8mg1rwaw3y1p09bc8dx4l6rx4qx961-coreutils-9.1/../../../etc/passwd"))))
94
95(test-assert "valid-path-syntax? leading dot"
96 (not (valid-path-syntax? (string-append (%store-prefix)
97 "/yp8mg1rwaw3y1p09bc8dx4l6rx4qx961-.leading-dot"))))
98
99(test-assert "valid-path-syntax? prefix"
100 (not (valid-path-syntax? (%store-prefix))))
101
102(test-assert "valid-path-syntax? truncated"
103 (not (valid-path-syntax? (string-append (%store-prefix) "/aaaaaaaaaaa"))))
104
81(test-equal "store-path-hash-part" 105(test-equal "store-path-hash-part"
82 "283gqy39v3g9dxjy26rynl0zls82fmcg" 106 "283gqy39v3g9dxjy26rynl0zls82fmcg"
83 (store-path-hash-part 107 (store-path-hash-part