mirror of
https://github.com/obra/superpowers.git
synced 2026-06-15 15:19:04 +08:00
Harden companion stop ownership proof
This commit is contained in:
@@ -15,32 +15,52 @@ fi
|
||||
|
||||
STATE_DIR="${SESSION_DIR}/state"
|
||||
PID_FILE="${STATE_DIR}/server.pid"
|
||||
SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
|
||||
|
||||
# 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
|
||||
local command_line
|
||||
command_line="$(ps -p "$1" -o command= 2>/dev/null || true)"
|
||||
if [[ -z "$command_line" ]]; then
|
||||
command_line="$(ps -f -p "$1" 2>/dev/null | sed '1d' || true)"
|
||||
read_expected_server_id() {
|
||||
[[ -f "$SERVER_ID_FILE" ]] || return 1
|
||||
local id
|
||||
id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)"
|
||||
[[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1
|
||||
printf '%s\n' "$id"
|
||||
}
|
||||
|
||||
command_line_for_pid() {
|
||||
local pid="$1"
|
||||
if [[ -r "/proc/$pid/cmdline" ]]; then
|
||||
tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true
|
||||
return 0
|
||||
fi
|
||||
case "$command_line" in
|
||||
*node*server.cjs*) ;;
|
||||
ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true
|
||||
}
|
||||
|
||||
command_has_server_id() {
|
||||
local pid="$1"
|
||||
local expected="$2"
|
||||
local expected_arg="--brainstorm-server-id=$expected"
|
||||
if [[ -r "/proc/$pid/cmdline" ]]; then
|
||||
local arg
|
||||
while IFS= read -r -d '' arg; do
|
||||
[[ "$arg" == "$expected_arg" ]] && return 0
|
||||
done < "/proc/$pid/cmdline"
|
||||
return 1
|
||||
fi
|
||||
local command_line
|
||||
command_line="$(command_line_for_pid "$pid")"
|
||||
[[ -n "$command_line" ]] || return 1
|
||||
case " $command_line " in
|
||||
*" $expected_arg "*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
# Stronger check: if we recorded the bound port and lsof is available, require
|
||||
# the PID to be the process actually LISTENING on this session's port. This
|
||||
# rules out an unrelated `node ... server.cjs` (another project, an editor task
|
||||
# runner, a different session) that happened to recycle the stale PID.
|
||||
local info="${STATE_DIR}/server-info"
|
||||
if [[ -f "$info" ]] && command -v lsof >/dev/null 2>&1; then
|
||||
local port
|
||||
port=$(sed -n 's/.*"port":\([0-9][0-9]*\).*/\1/p' "$info" | head -1)
|
||||
if [[ -n "$port" ]]; then
|
||||
[[ "$(lsof -nP -iTCP:"$port" -sTCP:LISTEN -t 2>/dev/null | head -1)" == "$1" ]] || return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Confirm a PID has this session's per-start instance id, not just a familiar
|
||||
# process name. Ambiguous or legacy metadata fails closed as stale_pid.
|
||||
is_brainstorm_server() {
|
||||
kill -0 "$1" 2>/dev/null || return 1
|
||||
local expected_id
|
||||
expected_id="$(read_expected_server_id)" || return 1
|
||||
command_has_server_id "$1" "$expected_id" || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -50,7 +70,7 @@ if [[ -f "$PID_FILE" ]]; then
|
||||
# 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"
|
||||
rm -f "$PID_FILE" "$SERVER_ID_FILE"
|
||||
echo '{"status": "stale_pid"}'
|
||||
exit 0
|
||||
fi
|
||||
@@ -79,7 +99,7 @@ if [[ -f "$PID_FILE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$PID_FILE" "${STATE_DIR}/server.log"
|
||||
rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log"
|
||||
|
||||
# Only delete ephemeral /tmp directories
|
||||
if [[ "$SESSION_DIR" == /tmp/* ]]; then
|
||||
|
||||
Reference in New Issue
Block a user