GitHub
11/02/2026, 10:50 AMisOsquerydRunning counted any process named `osqueryd`/`osqueryd.exe` as the managed agent. A lower-privileged local user could run a process with that name while the real service was down. Flags and upgrade checks would then accept a failed change, and the endpoint would report osquery as healthy.
• (Medium): verify reports success for an unhealthy endpoint. verifyNode logged mismatches, missing install files, a bad version and a stopped osqueryd, then returned nil. Anything relying on the exit code saw success. Its process check (a "/osqueryd " substring in the command line) also never matched on Windows.
Change
osqueryd identity
• Linux/macOS: osqueryd counts only when its process's real uid is root. An unprivileged user cannot start such a process, including by running a setuid-root binary under the name osqueryd, because the real uid stays theirs.
• Windows: isOsquerydRunning asks the service manager whether the osquery service is Running. A missing service counts as not running; other service-manager errors are returned.
• openOsqueryService now wraps its errors with %w, so a missing service can be told apart from other errors.
• All callers (flags guard, health watcher, service loop, upgrade) get the fix through the existing osquerydRunning seam.
• I chose not to match on the executable path. Any user can run the real binary, and a strict path match would mark osquery as down on non-standard installs, which would roll back good flags across the fleet.
verify
• Every check is still reported. The command now returns verification failed - <list> when any check fails: secret, flags or cert mismatch, osquery not installed, version too low or unknown, osqueryd not running.
• It uses the same osqueryd check as the daemon, and osqueryVersionReader for the version.
Docs: ARCHITECTURE.md and README.md describe the new osqueryd check and the verify exit code.
Security / operational impact
• verify now exits non-zero on drift or a stopped agent. Scripts that ignored its exit code are unaffected; anything checking it will now see failures it used to miss.
• osqueryd running as a non-root user on POSIX no longer counts as running. Standard systemd and launchd installs run it as root.
• The process ID is no longer included in `verify`'s "osqueryd is running" log line.
Validation
• go test ./cmd/osctrld/ -race passes
• golangci-lint run ./... and `GOOS=windows golangci-lint run ./...`: 0 issues
• New tests:
• `TestVerifyNode`: a healthy case and one case per failure
• `TestRootOwned`: covers a setuid binary run by a normal user
• `TestListRootProcessNamesSkipsOwnProcess`: the non-root test process is filtered out (skipped when run as root)
• Not run on a real Windows endpoint: windowsServiceRunning is only compiled and linted, because CI runs tests on Linux only.
jmpsec/osctrldGitHub
11/02/2026, 10:56 AM