frontend: tighten extraFiles target validation per damocles review

Follow-up to PR #350 review:

1. New assertion: hyperhive.frontend.extraFiles[*].target must be a
   relative path inside the static dir — leading '/' and '..'
   segments rejected at config eval time. Belt-and-braces against
   string-concat-into-paths escapes (the boundary doc flags this
   pattern even though agent.nix goes through operator review).

2. Documented overwrite semantics in the option doc: collision with
   a default-dist path or with a prior entry's target is a hard-fail
   (`refusing to overwrite existing path …`). To override a default
   file, fork `hyperhive.frontend.dist` instead — extraFiles is
   pure additions.

The collision-hard-fail behaviour was already implemented in
`mergedDist` (in commit a19e156); this commit just makes the
contract explicit in the docstring.

Refs #273, addresses damocles' notes on PR #350.
This commit is contained in:
iris 2026-05-23 13:35:06 +02:00 committed by Mara
parent 229c4292e9
commit 2951da32e7

View file

@ -224,10 +224,19 @@
the bitburner agent's game page at `/bitburner/`).
The default agent UI remains served at `/`; entries here only
add new routes and never replace the default. Path collisions
with default-dist filenames are a configuration error and
will surface as build failures (the merge derivation aborts
on overwrite).
add new routes and never replace the default. Overwrite
semantics are **hard-fail**: if `target` collides with an
existing file or directory in the default dist (or with a
prior entry's target), the `mergedDist` build aborts with
`refusing to overwrite existing path '<target>' in the
default dist`. To override a default file, fork the dist via
`hyperhive.frontend.dist` instead `extraFiles` is for
pure additions.
`target` must be a relative path inside the static dir. An
assertion rejects leading `/` and `..` segments at config
eval time (string-concat-into-paths safety, even though
agent.nix goes through operator review before deploy).
'';
};
@ -442,6 +451,24 @@
|| lib.hasSuffix ".svg" (toString config.hyperhive.icon);
message = "hyperhive.icon must point to an .svg file";
}
# hyperhive.frontend.extraFiles[*].target is concatenated into
# $out during the mergedDist build. Reject path traversal
# (`..` segments) and absolute paths (leading `/`) — both would
# let an entry escape the static dir. agent.nix is operator-
# reviewed, so this is belt-and-braces, but it's the kind of
# mistake that's easy to make and hard to spot during review.
{
assertion = lib.all (
entry:
!(lib.hasPrefix "/" entry.target)
&& !(builtins.any (seg: seg == "..") (lib.splitString "/" entry.target))
) (lib.attrValues config.hyperhive.frontend.extraFiles);
message = ''
hyperhive.frontend.extraFiles: `target` must be a relative
path inside the static dir no leading `/`, no `..`
segments.
'';
}
];
environment.etc."hyperhive/extra-mcp.json".text = builtins.toJSON config.hyperhive.extraMcpServers;