From 2951da32e7c9902d45f9043c71538b56137a476e Mon Sep 17 00:00:00 2001 From: iris Date: Sat, 23 May 2026 13:35:06 +0200 Subject: [PATCH] frontend: tighten extraFiles target validation per damocles review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- nix/templates/harness-base.nix | 35 ++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 2a342c5..2b859a8 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -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 '' 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;