security: FORGE_TOKEN leaked into argv by service_proc start (service_proc.nu:225) #106

Closed
opened 2026-04-21 15:25:05 +00:00 by sameh-farouk · 0 comments
Member

Summary

service_proc start embeds FORGE_TOKEN (and WEBROOT) into the bash -c command string passed to screen, which puts the secret on the process argv of the bash wrapper. /proc/<pid>/cmdline is world-readable on Linux, so every other user on a shared host can read the token with plain ps aux — no root, no -e flag required.

Confirmed on a live multi-user host: ps aux | grep hero_proc_server returned a line containing FORGE_TOKEN="<actual-token>" visible to all users. Rotating the token does not help while the leaky process is alive; the new value re-leaks on the next service_proc start.

Location

tools/modules/services/service_proc.nu:215–227, specifically line 225:

let cmd = $"FORGE_TOKEN=\"($forge_token)\" WEBROOT=\"($webroot)\" \"($bin)\" --socket \"($sock)\" --db-path \"($db_path)\" --log-level info 2>&1 | tee \"($log_file)\""

then passed to sp_screen_start $cmd $root which wraps in bash -c.

Root cause

The launcher bakes env vars into the command string to survive sudo stripping env on the root-managed path. That's only needed for the sudo branch (line 71). The non-sudo branch (^screen -dmS ... bash -c $cmd, line 73) inherits env from the calling nushell and doesn't need the inline VAR="..." prefix.

Proposed fix

Non-sudo path (covers the multi-user case):

let cmd = $"\"($bin)\" --socket \"($sock)\" --db-path \"($db_path)\" --log-level info 2>&1 | tee \"($log_file)\""
with-env { FORGE_TOKEN: $forge_token, WEBROOT: $webroot } {
    sp_screen_start $cmd $root
}

Sudo path: use sudo --preserve-env=FORGE_TOKEN,WEBROOT inside sp_screen_start, or pass via a 0600 env file that hero_proc_server reads at startup. Either way, do not interpolate the token into the bash -c string.

Verification

After patch, ps aux | grep FORGE_TOKEN on the host should return no matches in hero_proc's argv chain. cat /proc/<pid>/cmdline | tr '\0' ' ' should show the binary + flags only.

Impact

High. Every service_proc start on a multi-user host exposes the invoking user's forge token cross-user. All tokens ever started via this launcher on a shared host should be rotated.

### Summary `service_proc start` embeds `FORGE_TOKEN` (and `WEBROOT`) into the `bash -c` command string passed to `screen`, which puts the secret on the process argv of the bash wrapper. `/proc/<pid>/cmdline` is world-readable on Linux, so **every other user on a shared host can read the token** with plain `ps aux` — no root, no `-e` flag required. Confirmed on a live multi-user host: `ps aux | grep hero_proc_server` returned a line containing `FORGE_TOKEN="<actual-token>"` visible to all users. Rotating the token does not help while the leaky process is alive; the new value re-leaks on the next `service_proc start`. ### Location `tools/modules/services/service_proc.nu:215–227`, specifically line 225: ```nu let cmd = $"FORGE_TOKEN=\"($forge_token)\" WEBROOT=\"($webroot)\" \"($bin)\" --socket \"($sock)\" --db-path \"($db_path)\" --log-level info 2>&1 | tee \"($log_file)\"" ``` then passed to `sp_screen_start $cmd $root` which wraps in `bash -c`. ### Root cause The launcher bakes env vars into the command string to survive `sudo` stripping env on the root-managed path. That's only needed for the sudo branch (line 71). The non-sudo branch (`^screen -dmS ... bash -c $cmd`, line 73) inherits env from the calling nushell and doesn't need the inline `VAR="..."` prefix. ### Proposed fix Non-sudo path (covers the multi-user case): ```nu let cmd = $"\"($bin)\" --socket \"($sock)\" --db-path \"($db_path)\" --log-level info 2>&1 | tee \"($log_file)\"" with-env { FORGE_TOKEN: $forge_token, WEBROOT: $webroot } { sp_screen_start $cmd $root } ``` Sudo path: use `sudo --preserve-env=FORGE_TOKEN,WEBROOT` inside `sp_screen_start`, or pass via a 0600 env file that `hero_proc_server` reads at startup. Either way, do not interpolate the token into the `bash -c` string. ### Verification After patch, `ps aux | grep FORGE_TOKEN` on the host should return no matches in hero_proc's argv chain. `cat /proc/<pid>/cmdline | tr '\0' ' '` should show the binary + flags only. ### Impact **High.** Every `service_proc start` on a multi-user host exposes the invoking user's forge token cross-user. All tokens ever started via this launcher on a shared host should be rotated.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_skills#106
No description provided.