Prechádzať zdrojové kódy

fix(brainstorm-server): tie stop-server PID check to the session's port

The node+server.cjs command match (from the adversarial review) still matched any
unrelated node process running a file named server.cjs. When we recorded the
bound port (state/server-info) and lsof is available, additionally require the
PID to be the process actually LISTENING on this session's port — which rules out
a different project's server.cjs / editor task runner that recycled the stale
PID. Falls back to the command match when the port or lsof isn't available.

Test: a 'node server.cjs' process not listening on the recorded port is spared.

Refs #1703
Jesse Vincent 3 mesiacov pred
rodič
commit
eee4f87471

+ 14 - 1
skills/brainstorming/scripts/stop-server.sh

@@ -21,9 +21,22 @@ PID_FILE="${STATE_DIR}/server.pid"
 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 ;;
+    *node*server.cjs*) ;;
     *) 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
+  return 0
 }
 
 if [[ -f "$PID_FILE" ]]; then

+ 22 - 0
tests/brainstorm-server/stop-server.test.sh

@@ -60,5 +60,27 @@ case "$OUT" in
 esac
 rm -rf "$SESS"
 
+# --- Test 4: a `node server.cjs` impostor NOT listening on our port is spared ---
+if command -v lsof > /dev/null 2>&1; then
+  SESS="$(mktemp -d)"; mkdir -p "$SESS/state"
+  echo '{"type":"server-started","port":3499}' > "$SESS/state/server-info" # nothing listens on 3499
+  ( exec -a "node server.cjs" sleep 600 ) &
+  IMPOSTOR=$!
+  echo "$IMPOSTOR" > "$SESS/state/server.pid"
+  OUT="$("$STOP" "$SESS")"
+  if kill -0 "$IMPOSTOR" 2>/dev/null; then
+    case "$OUT" in
+      *stale_pid*) ok "a node server.cjs not listening on our port is left alone" ;;
+      *) bad "impostor survived but status was not stale_pid" "$OUT" ;;
+    esac
+  else
+    bad "killed a node server.cjs that was NOT on our recorded port" "$OUT"
+  fi
+  kill -9 "$IMPOSTOR" 2>/dev/null
+  rm -rf "$SESS"
+else
+  echo "  SKIP: lsof unavailable — port cross-check test"
+fi
+
 echo "--- Results: $PASS passed, $FAIL failed ---"
 [ "$FAIL" -eq 0 ] || exit 1