Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/spector"
---

Bind the mock server to the loopback interface (`127.0.0.1`) so the unauthenticated `/.admin/stop` endpoint can no longer be reached by other hosts on the network. The server is only reachable from the local host.
3 changes: 3 additions & 0 deletions packages/spector/docs/using-spector.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ tsp-spector serve ./path/to/scenarios --port 1234
tsp-spector serve ./path/to/scenarios --coverageFile ./path/to/spector-coverage.json
```

> [!NOTE]
> The server always binds to the loopback interface (`127.0.0.1`), so it is only reachable from the local host and is not exposed to other machines on the network.
Alternative to start in background

```bash
Expand Down
11 changes: 9 additions & 2 deletions packages/spector/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export interface MockApiServerConfig {
port: number;
}

/**
* The mock server always binds to the loopback interface so it is only reachable
* from the local host. This keeps the unauthenticated admin endpoints (e.g. the
* server stop signal) from being exposed to other hosts on the network.
*/
const LOOPBACK_HOST = "127.0.0.1";

const errorHandler: ErrorRequestHandler = (err, _req, res, _next) => {
logger.error("Error", err);

Expand Down Expand Up @@ -85,14 +92,14 @@ export class MockApiServer {
this.app.use(errorHandler);

return new Promise((resolve, reject) => {
const server = this.app.listen(this.config.port, () => {
const server = this.app.listen(this.config.port, LOOPBACK_HOST, () => {
const resolvedPort = getPort(server);
if (!resolvedPort) {
logger.error("Failed to resolve port");
reject(new Error("Failed to resolve port"));
return;
}
logger.info(`Started server on ${resolvedPort}`);
logger.info(`Started server on ${LOOPBACK_HOST}:${resolvedPort}`);
resolve(resolvedPort);
});

Expand Down
Loading