mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
/run/hermes-service is owned by hermes, so a hermes process can rename anything root creates there and leave a symlink in its place. Root registration built `.gateway-<p>.tmp` inside it and then chmodded and chowned every entry, so a swap mid-build handed any root-owned directory to hermes. Root's s6-svc/s6-svwait/s6-svscanctl calls had the same shape: they open control FIFOs and fifodirs in that tree and followed a hermes-planted symlink (s6-svc wrote its command byte into a root 0600 file). - Root registration builds each slot in /run/.hermes-service.staging (root, 0700, same tmpfs; every ancestor must be root-controlled) and publishes it with one rename(2), which never follows a symlink at the destination. Chowns use follow_symlinks=False. - S6ServiceManager runs its s6 tools through `s6-setuidgid hermes` when called as root, so lifecycle control acts with hermes's own rights. - Non-root registration is unchanged. Tests: unit cases for the staging swap, the ancestor/staging trust check and the s6 privilege drop; tests/docker/test_root_registration_ symlink_races.py plays the hermes process in a real container. Both Docker cases fail with the previous service_manager.py copied in (swap succeeds; victim file becomes "tRIGINAL") and pass now. Reported-by: Adolanium