fix: resolve per-user paths from root's own home when running as root

Path resolution trusted HOME (and XDG_*), which sudo and su can preserve
from the invoking user (GitHub runners, sudo -E, su without -). Any pmg
run as root then created root-owned ~/.config/safedep inside that user's
home, and event-log init fail-closed every later non-root pmg/npm/pip
run for them. System install made sudo pmg the documented flow, turning
this latent bug into the happy path.

When euid is 0, configDir and cacheDir now resolve from root's passwd
home instead of the environment, so root state lands under /root and
user homes are never touched. PMG_CONFIG_DIR/PMG_CACHE_DIR still win,
non-root resolution is unchanged, and Windows is unaffected (no euid).
Event-log init stays fatal on failure; sudo-run package events are
attributed via SUDO_USER and synced by the exit auto-sync as usual.

E2E: GitHub runners preserve HOME under sudo, so assert that no sudo
pmg run leaks state into the runner's home, and that the managed-config
refusal fails for the documented reason rather than a permission brick.
This commit is contained in:
Sahilb315
2026-07-14 03:24:01 +05:30
parent 479f546ebc
commit de0fa41852
3 changed files with 164 additions and 2 deletions
+21 -2
View File
@@ -947,6 +947,21 @@ jobs:
test -x "/usr/local/lib/pmg/bin/$shim" || { echo "Missing shim: $shim"; exit 1; }
done
- name: Root runs keep per-user state out of the invoking user's home
run: |
# GitHub runner sudo preserves HOME. Every sudo pmg run above used to
# create root-owned ~/.config/safedep for the runner user, which
# fail-closes all their later pmg/npm runs. Must run before any
# non-root pmg invocation legitimately creates that directory.
sudo sh -c 'echo "sudo sees HOME=$HOME"'
if [ -e "$HOME/.config/safedep" ]; then
echo "ERROR: root-created state leaked into $HOME/.config/safedep"
ls -laR "$HOME/.config/safedep"
exit 1
fi
sudo test -d /root/.config/safedep/pmg/logs
echo "SUCCESS: root state stayed under /root"
- name: PATH and profile.d activate shims
run: |
# Docker-style: non-login shells need PATH (or source profile.d)
@@ -957,14 +972,18 @@ jobs:
- name: Managed config refuses CLI mutation
run: |
if pmg config set dependency_cooldown.days 7; then
# Assert the refusal reason: a permission-denied brick (poisoned home)
# would also make config set fail and mask a regression.
if out=$(pmg config set dependency_cooldown.days 7 2>&1); then
echo "ERROR: config set should fail under system config"
exit 1
fi
if sudo pmg config set dependency_cooldown.days 7; then
echo "$out" | grep -qi 'globally managed' || { echo "ERROR: failed for the wrong reason:"; echo "$out"; exit 1; }
if out=$(sudo pmg config set dependency_cooldown.days 7 2>&1); then
echo "ERROR: config set should fail under system config even as root"
exit 1
fi
echo "$out" | grep -qi 'globally managed' || { echo "ERROR: root run failed for the wrong reason:"; echo "$out"; exit 1; }
echo "SUCCESS: managed config is locked"
- name: Doctor reports system install state