diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2026-06-08 11:02:59 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2026-07-02 19:42:47 +0200 |
| commit | 652e0c9110cae669767318dac7c09bcd4f56ce75 (patch) | |
| tree | 581edf096126ae95aff4a3d97f20bcb080a3b095 | |
| parent | 26d7eb8a4adafc648ef035e91b6bbc4945d4c962 (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.scm | 46 | ||||
| -rw-r--r-- | tests/store.scm | 24 |
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 | |||
| 1970 | This is a lightweight check. Use 'valid-path-syntax?' to validate untrusted | ||
| 1971 | input." | ||
| 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 | ||
| 2015 | that 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 | |||
| 2034 | This 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 |
