Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/host-modules/lib/name-guards.nix
atlas 7ee6e776a3 swarm: refuse swarm.hives keys that are not valid hive identifiers
A hive key becomes a certificate subject and a directory name, but only the
reserved-name and fragment guards looked at it, so `a/b` or `../x` evaluated
and misbehaved later. Assert at eval time that every key is 1-63 characters
of [a-z0-9-], the rule `Ident` applies in hive-types, and name the offending
keys in the message.
2026-10-12 21:45:50 +02:00

89 lines
2.4 KiB
Nix

# Assertions about names an operator chooses, so a module states WHAT is
# forbidden and not how to phrase the refusal.
{ lib }:
let
# One shape for both guards. Reports EVERY offender, not the first: fixing
# one per rebuild is the slowest possible way to learn a rule.
#
# Offenders come after their label rather than before it, so one name and
# five read the same — no English verb agrees with both.
guard =
{
option,
names,
forbidden,
hit,
problem,
forbiddenLabel,
why,
}:
let
offenders = lib.filter (n: lib.any (f: hit f n) forbidden) names;
quote = xs: lib.concatMapStringsSep ", " (x: "'${x}'") xs;
in
{
assertion = offenders == [ ];
message = ''
${option} ${problem}: ${quote offenders}
${forbiddenLabel}: ${quote forbidden}
${why}
'';
};
in
{
# No name may BE one of `reserved`.
mustNotEqual =
{
option,
names,
reserved,
why,
}:
guard {
inherit option names why;
forbidden = reserved;
hit = f: n: f == n;
problem = "has reserved name(s)";
forbiddenLabel = "Reserved";
};
# No name may CONTAIN one of `fragments`.
mustNotContain =
{
option,
names,
fragments,
why,
}:
guard {
inherit option names why;
forbidden = fragments;
hit = lib.hasInfix;
problem = "has name(s) containing a reserved word";
forbiddenLabel = "Forbidden as substrings";
};
# Every name must be a valid hive `Ident` (hive-types/src/lib.rs): 1-63
# characters of `[a-z0-9-]`, with no leading-character rule. A hive name is
# spliced into certificate subjects and filesystem paths, where `/` and `..`
# change meaning.
mustBeIdent =
{ option, names }:
let
offenders = lib.filter (n: builtins.match "[a-z0-9-]+" n == null || lib.stringLength n > 63) names;
in
{
assertion = offenders == [ ];
message = ''
${option} has name(s) that are not valid identifiers: ${
lib.concatMapStringsSep ", " (x: "'${x}'") offenders
}
Required: 1-63 characters, each one of [a-z0-9-]
A hive name becomes a certificate subject and a directory name, so a
`/` or `..` in it names something else. `Ident` in
hive-types/src/lib.rs applies the same rule. Rename the hive.
'';
};
}