mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +00:00
fix(cli): bind studio preview server to loopback by default (#1210)
## Summary - Binds the Studio preview server (`packages/cli`) to `127.0.0.1` instead of `0.0.0.0` so it is only reachable from localhost. - Adds a `--host` flag for callers that genuinely need to expose the server on a wider interface (e.g. Docker, remote dev boxes). ## Security **F-001 HIGH** — Studio preview server was binding on all interfaces, making it reachable from any network the developer's machine was on (including shared Wi-Fi, corp LAN). Because the server serves the project filesystem under no auth, any peer on the same network could read arbitrary project files. Restricting to loopback closes this exposure for the default case. ## Test plan - [x] `hyperframes preview` starts — server reachable on `localhost:<port>`, not on LAN IP - [x] `hyperframes preview --host 0.0.0.0` still binds on all interfaces for Docker / remote-dev use cases - [x] Existing unit tests pass
This commit is contained in:
@@ -188,7 +188,7 @@ export function detectHyperframesServer(
|
||||
* Get the PID of the process listening on a port (macOS/Linux only).
|
||||
* Returns null on Windows or if detection fails.
|
||||
*/
|
||||
export async function getProcessOnPort(port: number): Promise<string | null> {
|
||||
async function getProcessOnPort(port: number): Promise<string | null> {
|
||||
if (process.platform === "win32") return null;
|
||||
try {
|
||||
const { stdout } = await execFileAsync("lsof", [`-ti:${port}`, "-sTCP:LISTEN"], {
|
||||
@@ -336,8 +336,16 @@ export async function findPortAndServe(
|
||||
projectDir: string,
|
||||
forceNew: boolean,
|
||||
expectedServerBuildSignature: string | null = null,
|
||||
bindHost?: string,
|
||||
): Promise<FindPortResult> {
|
||||
const { createAdaptorServer } = await import("@hono/node-server");
|
||||
// SECURITY (F-001): bind to loopback by default. The studio API exposes
|
||||
// unauthenticated project file read/write/delete + render-spawn endpoints;
|
||||
// a bare `listen(port)` binds the unspecified address (`::`/`0.0.0.0`),
|
||||
// handing those endpoints to anyone on the LAN. Operators who genuinely
|
||||
// need LAN exposure opt in explicitly via the HYPERFRAMES_PREVIEW_HOST
|
||||
// env var (e.g. HYPERFRAMES_PREVIEW_HOST=0.0.0.0).
|
||||
const host = bindHost ?? (process.env.HYPERFRAMES_PREVIEW_HOST?.trim() || "127.0.0.1");
|
||||
const normalizedDir = resolve(projectDir).replace(/\\/g, "/").toLowerCase();
|
||||
const endPort = startPort + MAX_PORT_SCAN - 1;
|
||||
|
||||
@@ -362,7 +370,7 @@ export async function findPortAndServe(
|
||||
};
|
||||
server!.once("error", onError);
|
||||
server!.once("listening", onListening);
|
||||
server!.listen(port);
|
||||
server!.listen(port, host);
|
||||
});
|
||||
return { type: "started", server, port };
|
||||
} catch (err: unknown) {
|
||||
|
||||
Reference in New Issue
Block a user