[PATCH v2 8/8] lib/config.nix: Validate configuration parameters

Demi Marie Obenour
2025-11-12T22:18:33Z

Wrong values for the version or update URL will cause very confusing
build-time or runtime errors. Provide a better user experience by
validating them up-front.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
---
lib/config.nix | 41 +++++++++++++++++++++++++++++++++++------
1 file changed, 35 insertions(+), 6 deletions(-)

diff --git a/lib/config.nix b/lib/config.nix
index 5b6b95013734202b7e2e01d5ffce313080658006..660a2427447fd9851e60e955da6bd1a5d71cfdac 100644
--- a/lib/config.nix
+++ b/lib/config.nix
@@ -19,10 +19,39 @@ let
inherit default;
} else config;
finalConfig = default // callConfig config;
+
+ # Only allow unreserved characters, : (for port numbers), /, and %-encoding.
+ # The rest of the code is allowed to assume that these are the only characters
+ # in the update URL.
+ # Do not use [:alnum:] or [:hexdigit:] as they depend on the locale in POSIX.
+ # Query strings and fragment identifiers break appending
+ # /SHA256SUMS and /SHA256SUMS.gpg to a URL.
+ # [, ], {, and } would cause globbing in curl.
+ url-regex = "^https?://([ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:./~-]|%[ABCDEFabcdef0123456789]{2})+$";
+ update-url = finalConfig.update-url;
+
+ # Only allow a numeric version for now.
+ number_re = "(0|[1-9][0-9]{0,2})";
+ version_re = "^(${number_re}\\.){2}${number_re}$";
in
- finalConfig // {
- update-signing-key = builtins.path {
- name = "signing-key";
- path = finalConfig.update-signing-key;
- };
- }
+ if !builtins.isString update-url then
+ builtins.abort "Update URL must be a string, not ${builtins.typeOf update-url}"
+ else if builtins.match "^https?://.*" update-url == null then
+ builtins.abort "Update URL ${builtins.toJSON update-url} has unsupported scheme (not https:// or http://) or is invalid"
+ else if builtins.match url-regex update-url == null then
+ builtins.abort "Update URL ${builtins.toJSON update-url} has forbidden characters"
+ else if builtins.substring (builtins.stringLength update-url - 1) 1 update-url == "/" then
+ builtins.abort "Update URL ${builtins.toJSON update-url} must not end with /"
+ else if !builtins.isString finalConfig.version then
+ builtins.abort "Version must be a string, not ${builtins.typeOf finalConfig.version}"
+ else if builtins.match version_re finalConfig.version == null then
+ builtins.abort "Version ${builtins.toJSON finalConfig.version} is invalid"
+ else if !builtins.isPath finalConfig.update-signing-key then
+ builtins.abort "Update verification key file is of type ${builtins.typeOf finalConfig.update-signing-key}, not path"
+ else
+ finalConfig // {
+ update-signing-key = builtins.path {
+ name = "signing-key";
+ path = finalConfig.update-signing-key;
+ };
+ }

--
2.51.2