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;