fix: triage the unwritable config dir remedy by cause

The chown hint is only correct when another account created files
inside the current user's own home. When a leaked HOME or
XDG_CONFIG_HOME points at another user's home (e.g. sudo -u on GitHub
runners), following it would chown that user's directory and brick
their pmg instead. Classify the failure against the passwd home,
which the leaked environment cannot influence, and prescribe:

- dir inside own home: restore ownership with chown
- dir outside own home: fix the leaked environment, never chown
- explicit PMG_CONFIG_DIR: make it writable

Used by both the fatal event-log error and the doctor check, and the
docs troubleshooting now carries the same two-case triage.
This commit is contained in:
Sahilb315
2026-07-14 03:56:07 +05:30
parent de0fa41852
commit cd9b45b3bc
6 changed files with 100 additions and 12 deletions
+4 -3
View File
@@ -259,8 +259,9 @@ func runCoreChecks(cfg *config.RuntimeConfig) []doctor.CheckResult {
// checkEventLogDirResult is the testable core of the event-log dir check.
// Event logging is mandatory (init failure is fatal), so an unwritable dir
// fail-closes every pmg command for this user. The common cause is a root or
// sudo run having created the per-user directory as root, hence the chown fix.
// fail-closes every pmg command for this user. The remedy is triaged: chown
// when another account created files in this user's home, an environment fix
// when a leaked HOME/XDG_CONFIG_HOME points at another user's home.
func checkEventLogDirResult(skipEventLogging bool, logDir, configDir string) doctor.CheckResult {
if skipEventLogging {
return doctor.CheckResult{
@@ -287,7 +288,7 @@ func checkEventLogDirResult(skipEventLogging bool, logDir, configDir string) doc
return doctor.CheckResult{
Status: doctor.StatusFail,
Message: "Event log directory not writable",
Fix: fmt.Sprintf("sudo chown -R $(id -un) %s", configDir),
Fix: config.UnwritableConfigDirRemedy(configDir),
}
}
if err := probe.Close(); err != nil {
+3 -3
View File
@@ -6,6 +6,7 @@ import (
"path/filepath"
"testing"
"github.com/safedep/pmg/config"
"github.com/safedep/pmg/internal/doctor"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -133,7 +134,7 @@ func TestCheckEventLogDirResult(t *testing.T) {
assert.Equal(t, doctor.StatusPass, result.Status)
})
t.Run("unwritable directory fails with chown fix", func(t *testing.T) {
t.Run("unwritable directory fails with triaged remedy", func(t *testing.T) {
if os.Geteuid() == 0 {
t.Skip("running as root: directory permissions are not enforced")
}
@@ -146,7 +147,6 @@ func TestCheckEventLogDirResult(t *testing.T) {
result := checkEventLogDirResult(false, dir, configDir)
assert.Equal(t, doctor.StatusFail, result.Status)
assert.Equal(t, "Event log directory not writable", result.Message)
assert.Contains(t, result.Fix, "sudo chown -R")
assert.Contains(t, result.Fix, configDir)
assert.Equal(t, config.UnwritableConfigDirRemedy(configDir), result.Fix)
})
}