GitHub Security Advisory: GHSA-3298-56p6-rpw2
Package: openclaw (NPM)
Severity: MODERATE (CVSS 6.1)
CWE: CWE-404: Improper Resource Shutdown or Release
Vulnerable versions: < 2026.3.24
Patched in: 2026.3.24

Summary: OpenClaw has incomplete Fix for CVE-2026-27486: Unvalidated SIGKILL in `!stop` Chat Command via `shell-utils.ts`

Details:
> Fixed in OpenClaw 2026.3.24, the current shipping release.

### Advisory Details
**Title**: Incomplete Fix for CVE-2026-27486: Unvalidated SIGKILL in `!stop` Chat Command via `shell-utils.ts`

**Description**:
### Summary
The `!stop` (and `/bash stop`) chat command kills background bash processes using `SIGKILL` directly, without first sending `SIGTERM` to allow graceful shutdown. This is because `bash-command.ts` imports `killProcessTree()` from `src/agents/shell-utils.ts`, which still contains the pre-CVE-2026-27486 aggressive kill logic, rather than from the patched `src/process/kill-tree.ts`.

### Details
CVE-2026-27486 fixed unsafe process termination by introducing a graceful shutdown sequence in `src/process/kill-tree.ts` — sending `SIGTERM` first, waiting a configurable grace period (default 3 seconds), then escalating to `SIGKILL` only if the process is still alive.

However, an identical copy of the **unpatched** `killProcessTree` function remains in `src/agents/shell-utils.ts` (lines 170–192). This function sends `SIGKILL` immediately with no `SIGTERM`:

```typescript
// src/agents/shell-utils.ts:170-192
export function killProcessTree(pid: number): void {
  // ... Windows handling ...
  try {
    process.kill(-pid, "SIGKILL"); // Immediate hard kill, no SIGTERM
  } catch {
    try {
      process.kill(pid, "SIGKILL");
    } catch {
      // process already dead
    }
  }
}
```

The `!stop` chat command handler in `src/auto-reply/reply/bash-command.ts` imports and calls this vulnerable version at line 302:

```typescript
// src/auto-reply/reply/bash-command.ts:5
import { killProcessTree } from "../../agents/shell-utils.js";

// src/auto-reply/reply/bash-command.ts:300-304
const pid = running.pid ?? running.child?.pid;
if (pid) {
  killProcessTree(pid);  // Calls the UNPATCHED version
}
markExited(running, null, "SIGKILL", "failed");
```

Compare this to the patched version in `src/process/kill-tree.ts`:

```typescript
// src/process/kill-tree.ts:46-78
function killProcessTreeUnix(pid: number, graceMs: number): void {
  // Step 1: Try graceful SIGTERM to process group
  try {
    process.kill(-pid, "SIGTERM");
  } catch { /* ... */ }

  // Step 2: Wait grace period, then SIGKILL if still alive
  setTimeout(() => {
    if (isProcessAlive(-pid)) {
      try { process.kill(-pid, "SIGKILL"); } catch { /* ... */ }
    }
  }, graceMs).unref();
}
```

### PoC

This PoC demonstrates the difference between the vulnerable and patched code paths inside a running OpenClaw Gateway container.

**Setup:**
```bash
# Build and start the gateway container
cd CVE-2026-27486-variant-exp/
docker compose up -d
sleep 5
```

**Exploit (vulnerable `killProcessTree` from `shell-utils.ts`):**

The following script is injected into the container and executed. It starts a bash process that traps `SIGTERM` for graceful shutdown, then kills it using the same code path as `!stop`:

```javascript
// exploit_sigkill.cjs — replicates src/agents/shell-utils.ts:183-190
c