mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
fix: gate SUDO_USER trust and root path diversion; add doctor binary check
Address review findings on the system-install PR: - cloud_sink: honor SUDO_USER for audit attribution only when euid==0. Without the gate any user could set SUDO_USER and spoof cloud-audit attribution to another account. Matches the guard in cmd/setup/cert.go. - config: divert per-user paths to root's passwd home only on an actual sudo elevation (euid==0 && SUDO_USER set), not for every root euid. The blanket root diversion ignored HOME/XDG_CONFIG_HOME and silently stopped reading genuine root users' config (golden Docker images), regressing two tests that only fail when the suite runs as root. Genuine root honors the environment as before; su without - leaves no marker and stays a documented, loud-failing residual. - doctor: add a system-only check re-validating that the binary the installed shims exec is still root-owned and non-writable, catching permission/ownership drift after install. - shim: fold the duplicated shim-scan loop into firstShimContent.
This commit is contained in:
@@ -33,6 +33,7 @@ const (
|
||||
checkProtectionNpm = "protection-npm"
|
||||
checkProtectionPip = "protection-pip"
|
||||
checkCA = "ca-cert"
|
||||
checkSystemBinary = "system-binary"
|
||||
|
||||
aliasesInstalledMessage = "Shell aliases installed"
|
||||
)
|
||||
@@ -254,9 +255,42 @@ func runCoreChecks(cfg *config.RuntimeConfig) []doctor.CheckResult {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// System-only: the binary every user's shim execs must stay root-owned and
|
||||
// non-writable. Validation runs at install; re-check it here to catch later
|
||||
// permission/ownership drift (redeploy, chmod, image rebuild).
|
||||
if shim.SystemShimsInstalled() {
|
||||
checks = append(checks, doctor.Check{
|
||||
Name: checkSystemBinary,
|
||||
Category: "Security",
|
||||
Run: checkSystemBinaryResult,
|
||||
})
|
||||
}
|
||||
|
||||
return doctor.RunChecks(checks)
|
||||
}
|
||||
|
||||
func checkSystemBinaryResult() doctor.CheckResult {
|
||||
path, ok := shim.SystemShimBinary()
|
||||
if !ok {
|
||||
return doctor.CheckResult{
|
||||
Status: doctor.StatusWarn,
|
||||
Message: "Could not determine system shim binary",
|
||||
}
|
||||
}
|
||||
if err := shim.ValidateSystemBinary(path); err != nil {
|
||||
return doctor.CheckResult{
|
||||
Status: doctor.StatusFail,
|
||||
Message: fmt.Sprintf("System binary unsafe: %v", err),
|
||||
Fix: "Reinstall with pmg setup install --system, or restore root ownership/permissions",
|
||||
}
|
||||
}
|
||||
return doctor.CheckResult{
|
||||
Status: doctor.StatusPass,
|
||||
Message: fmt.Sprintf("System binary is root-owned and safe (%s)", path),
|
||||
}
|
||||
}
|
||||
|
||||
// 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 remedy is triaged: chown
|
||||
@@ -371,6 +405,9 @@ func shimDirs() []string {
|
||||
return dirs
|
||||
}
|
||||
|
||||
// checkShimDirResolution classifies interception against all shim dirs via
|
||||
// shimDirs(); shimDir/pathLabel only select the PATH-membership fallback and
|
||||
// the display label, not which directories count as intercepting.
|
||||
func checkShimDirResolution(shimDir, pathLabel string, pathEntries []string) doctor.CheckResult {
|
||||
underShim, shadowed := classifyPackageManagerResolutions(
|
||||
alias.DefaultConfig().PackageManagers,
|
||||
@@ -481,6 +518,7 @@ var checkDisplayNames = map[string]string{
|
||||
checkProtectionNpm: "npm protection",
|
||||
checkProtectionPip: "pip protection",
|
||||
checkCA: "MITM CA",
|
||||
checkSystemBinary: "System binary",
|
||||
}
|
||||
|
||||
var checkFixes = map[string]string{
|
||||
|
||||
@@ -107,6 +107,17 @@ func TestClassifyPackageManagerResolutionsAcceptsEitherShimDir(t *testing.T) {
|
||||
assert.Equal(t, []string{"yarn"}, shadowed)
|
||||
}
|
||||
|
||||
func TestCheckSystemBinaryResult(t *testing.T) {
|
||||
// No system shims installed -> could not determine binary (Warn).
|
||||
result := checkSystemBinaryResult()
|
||||
// On a dev machine with no /usr/local/lib/pmg/bin shims, SystemShimBinary
|
||||
// returns !ok, so we get a Warn rather than a spurious Fail.
|
||||
assert.Contains(t, []doctor.CheckStatus{doctor.StatusWarn, doctor.StatusPass, doctor.StatusFail}, result.Status)
|
||||
if result.Status == doctor.StatusWarn {
|
||||
assert.Equal(t, "Could not determine system shim binary", result.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckEventLogDirResult(t *testing.T) {
|
||||
configDir := "/home/dev/.config/safedep/pmg"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user