fix(brainstorm-server): verify PID ownership before stopping

stop-server.sh read server.pid and SIGKILL'd that PID with no checks. After a
reboot or PID wraparound the pid file can point at an unrelated, live process —
which we would then kill.

Verify the PID is actually our server (a running 'node ... server.cjs') before
signalling it. If ownership can't be proven, fail closed: remove the stale pid
file and report {status: stale_pid} without killing anything. Real servers still
stop ({status: stopped}); a missing pid file still reports not_running.

Adds stop-server.test.sh covering: an unrelated reused PID is left alone, a real
server is stopped, and a missing pid file.

Refs #1703
This commit is contained in:
Jesse Vincent
2026-06-09 14:57:44 -07:00
parent e0442fba00
commit ddcb56c16e
2 changed files with 82 additions and 0 deletions

View File

@@ -16,9 +16,27 @@ fi
STATE_DIR="${SESSION_DIR}/state"
PID_FILE="${STATE_DIR}/server.pid"
# Confirm a PID is actually our brainstorm server (node running server.cjs),
# not a reused/unrelated process whose PID was recycled into a stale pid file.
is_brainstorm_server() {
kill -0 "$1" 2>/dev/null || return 1
case "$(ps -p "$1" -o command= 2>/dev/null)" in
*node*server.cjs*) return 0 ;;
*) return 1 ;;
esac
}
if [[ -f "$PID_FILE" ]]; then
pid=$(cat "$PID_FILE")
# Refuse to signal a PID we can't prove is our server. A stale pid file may
# point at an unrelated process after a reboot/PID wraparound.
if ! is_brainstorm_server "$pid"; then
rm -f "$PID_FILE"
echo '{"status": "stale_pid"}'
exit 0
fi
# Try to stop gracefully, fallback to force if still alive
kill "$pid" 2>/dev/null || true