Files
wehub-resource-sync 36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:06 +08:00

301 lines
8.0 KiB
Go

package source
import (
"runtime"
"strings"
"testing"
"time"
"github.com/pranshuparmar/witr/pkg/model"
)
// baseProc returns a benign process record that produces no warnings by
// itself. Tests mutate one field at a time to exercise individual rules.
func baseProc() model.Process {
return model.Process{
PID: 1234,
Command: "nginx",
User: "www-data",
StartedAt: time.Now().Add(-1 * time.Hour),
WorkingDir: "/var/www",
Health: "healthy",
}
}
// wrap forces Warnings to use a known source type so we don't depend on
// the platform-specific Detect() and its real-system probing.
func wrap(p model.Process) []string {
parent := baseProc()
parent.PID = 1
parent.Command = "systemd"
return Warnings([]model.Process{parent, p}, 0, model.SourceSystemd)
}
func contains(haystack []string, needle string) bool {
for _, h := range haystack {
if strings.Contains(h, needle) {
return true
}
}
return false
}
func TestWarningsHealthStates(t *testing.T) {
t.Parallel()
tests := []struct {
health string
want string
}{
{"zombie", "zombie"},
{"stopped", "stopped"},
{"high-cpu", "high CPU"},
{"high-mem", "high memory"},
}
for _, tt := range tests {
tt := tt
t.Run(tt.health, func(t *testing.T) {
t.Parallel()
p := baseProc()
p.Health = tt.health
if !contains(wrap(p), tt.want) {
t.Errorf("expected warning containing %q for health=%q, got: %v",
tt.want, tt.health, wrap(p))
}
})
}
}
func TestWarningsPublicBind(t *testing.T) {
t.Parallel()
p := baseProc()
p.Sockets = []model.Socket{{Address: "0.0.0.0", Port: 443, State: "LISTEN"}}
if !contains(wrap(p), "public interface") {
t.Errorf("expected public-interface warning, got: %v", wrap(p))
}
}
func TestWarningsRootUser(t *testing.T) {
t.Parallel()
p := baseProc()
p.User = "root"
if !contains(wrap(p), "running as root") {
t.Errorf("expected root warning, got: %v", wrap(p))
}
}
func TestWarningsDangerousCapabilities(t *testing.T) {
t.Parallel()
p := baseProc()
p.Capabilities = []string{"CAP_NET_BIND_SERVICE", "CAP_SYS_PTRACE", "CAP_SYS_ADMIN"}
w := wrap(p)
if !contains(w, "dangerous capabilities") {
t.Fatalf("expected dangerous-capabilities warning, got: %v", w)
}
// Only the dangerous ones should appear in the message.
if !contains(w, "CAP_SYS_PTRACE") || !contains(w, "CAP_SYS_ADMIN") {
t.Errorf("expected dangerous caps listed in warning, got: %v", w)
}
if contains(w, "CAP_NET_BIND_SERVICE") {
t.Errorf("benign cap should not appear in warning, got: %v", w)
}
}
func TestWarningsBenignCapabilitiesSuppressed(t *testing.T) {
t.Parallel()
p := baseProc()
p.Capabilities = []string{"CAP_NET_BIND_SERVICE"}
if contains(wrap(p), "dangerous capabilities") {
t.Errorf("benign capability triggered warning, got: %v", wrap(p))
}
}
func TestWarningsRootSuppressesCapabilitiesCheck(t *testing.T) {
t.Parallel()
// Per the implementation, root already triggers its own warning and the
// capability check is skipped (root has them all anyway).
p := baseProc()
p.User = "root"
p.Capabilities = []string{"CAP_SYS_ADMIN"}
w := wrap(p)
if contains(w, "dangerous capabilities") {
t.Errorf("dangerous-capabilities warning should be suppressed for root, got: %v", w)
}
if !contains(w, "running as root") {
t.Errorf("root warning still expected, got: %v", w)
}
}
func TestWarningsUnknownSupervisor(t *testing.T) {
t.Parallel()
p := baseProc()
got := Warnings([]model.Process{p}, 0, model.SourceUnknown)
hasWarning := contains(got, "No known supervisor")
if runtime.GOOS == "windows" {
// Suppressed on Windows: ancestry truncates at orphaned processes, so
// "unknown source" is normal rather than a sign of no supervision.
if hasWarning {
t.Errorf("unknown-supervisor warning should be suppressed on Windows, got: %v", got)
}
return
}
if !hasWarning {
t.Errorf("expected unknown-supervisor warning, got: %v", got)
}
}
func TestWarningsLongRunning(t *testing.T) {
t.Parallel()
p := baseProc()
p.StartedAt = time.Now().Add(-100 * 24 * time.Hour)
if !contains(wrap(p), "over 90 days") {
t.Errorf("expected long-running warning, got: %v", wrap(p))
}
}
// A zero start time means we couldn't read it (e.g. a protected Windows
// process), not that the process is ancient — it must not trigger the
// long-running warning. Regression guard for issue #205.
func TestWarningsZeroStartTimeNotLongRunning(t *testing.T) {
t.Parallel()
p := baseProc()
p.StartedAt = time.Time{}
if contains(wrap(p), "over 90 days") {
t.Errorf("zero start time should not trigger long-running warning, got: %v", wrap(p))
}
}
func TestWarningsSuspiciousWorkingDirs(t *testing.T) {
t.Parallel()
for _, dir := range []string{"/", "/tmp", "/var/tmp"} {
dir := dir
t.Run(dir, func(t *testing.T) {
t.Parallel()
p := baseProc()
p.WorkingDir = dir
if !contains(wrap(p), "suspicious working directory") {
t.Errorf("expected suspicious-dir warning for %q, got: %v", dir, wrap(p))
}
})
}
}
func TestWarningsContainerHealthcheck(t *testing.T) {
t.Parallel()
tests := []struct {
name string
healthcheck string
wantWarning bool
}{
{"absent warns", "absent", true},
{"present does not warn", "present", false},
{"unknown does not warn", "", false},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := baseProc()
p.Container = "docker (abc123)"
p.ContainerHealthcheck = tc.healthcheck
if got := contains(wrap(p), "healthcheck"); got != tc.wantWarning {
t.Errorf("ContainerHealthcheck=%q: warning=%v, want %v (%v)", tc.healthcheck, got, tc.wantWarning, wrap(p))
}
})
}
}
func TestWarningsSnapAndFlatpakSkipHealthcheck(t *testing.T) {
t.Parallel()
// Snap and Flatpak don't use healthchecks — they shouldn't trigger this
// warning even though the Container field is set.
for _, container := range []string{"snap: discord", "flatpak: org.signal.Signal"} {
container := container
t.Run(container, func(t *testing.T) {
t.Parallel()
p := baseProc()
p.Container = container
if contains(wrap(p), "healthcheck") {
t.Errorf("healthcheck warning should not fire for %q, got: %v", container, wrap(p))
}
})
}
}
func TestWarningsServiceNameMismatch(t *testing.T) {
t.Parallel()
tests := []struct {
name string
command string
service string
wantWarning bool
}{
{"match", "nginx", "nginx.service", false},
{"systemd suffix tolerated", "postgres", "postgresql.service", false}, // svcCore contains cmd substring
{"systemd template instance tolerated", "agetty", "getty@tty1.service", false},
{"mismatch", "nginx", "redis.service", true},
{"empty service skipped", "nginx", "", false},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
p := baseProc()
p.Command = tt.command
p.Service = tt.service
got := contains(wrap(p), "Service name and process name")
if got != tt.wantWarning {
t.Errorf("service mismatch warning for cmd=%q svc=%q: got=%v want=%v\n%v",
tt.command, tt.service, got, tt.wantWarning, wrap(p))
}
})
}
}
func TestWarningsRestartCount(t *testing.T) {
t.Parallel()
chain := []model.Process{{PID: 1, Command: "systemd"}, baseProc()}
// The warning is driven by the real restart count (e.g. systemd NRestarts),
// not by the shape of the ancestry.
if got := Warnings(chain, 7, model.SourceSystemd); !contains(got, "restarted 7 times") {
t.Errorf("expected restart warning for 7 restarts, got: %v", got)
}
if got := Warnings(chain, 5, model.SourceSystemd); contains(got, "restarted") {
t.Errorf("no restart warning expected for 5 restarts, got: %v", got)
}
}
func TestWarningsBenignProcProducesNoSpurious(t *testing.T) {
t.Parallel()
got := wrap(baseProc())
for _, w := range got {
// The base fixture is intentionally benign; any warning here is a
// false positive that should be investigated.
t.Errorf("benign fixture produced warning: %q", w)
}
}
func TestWarningsEmptyInputReturnsNil(t *testing.T) {
t.Parallel()
if got := Warnings(nil, 0); got != nil {
t.Errorf("Warnings(nil) = %v, want nil", got)
}
}