mirror of
https://github.com/penpot/penpot.git
synced 2026-08-28 15:48:52 +00:00
🐛 Bind MCP ReplServer to localhost to prevent unauthenticated RCE
The ReplServer Express app was calling `app.listen(port)` with no host argument, causing Node/Express to default to binding on all interfaces (0.0.0.0). Combined with the unauthenticated /execute endpoint, any network peer could POST arbitrary JS and get it run inside the MCP process. Fix: add a `host` parameter (default "localhost") to the ReplServer constructor and pass it to `app.listen`. The call site in PenpotMcpServer now forwards `this.host` (sourced from PENPOT_MCP_SERVER_HOST env var, default "localhost"), so environment- variable overrides continue to work. Signed-off-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
parent
697a825d76
commit
798ee46b4a
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
- Fix MCP ReplServer binding to all interfaces (0.0.0.0) instead of localhost, allowing unauthenticated RCE
|
||||||
- Fix incorrect invitation token handling on register process [Github #9380](https://github.com/penpot/penpot/pull/9380)
|
- Fix incorrect invitation token handling on register process [Github #9380](https://github.com/penpot/penpot/pull/9380)
|
||||||
- Fix incorrect handling of version restore operation [Github #9041](https://github.com/penpot/penpot/pull/9041)
|
- Fix incorrect handling of version restore operation [Github #9041](https://github.com/penpot/penpot/pull/9041)
|
||||||
- Fix false “text editing” warning when applying tokens [Github #6346](https://github.com/penpot/penpot/issues/9346)
|
- Fix false “text editing” warning when applying tokens [Github #6346](https://github.com/penpot/penpot/issues/9346)
|
||||||
|
|||||||
@ -92,7 +92,7 @@ export class PenpotMcpServer {
|
|||||||
this.tools = this.initTools();
|
this.tools = this.initTools();
|
||||||
|
|
||||||
this.pluginBridge = new PluginBridge(this, this.webSocketPort);
|
this.pluginBridge = new PluginBridge(this, this.webSocketPort);
|
||||||
this.replServer = new ReplServer(this.pluginBridge, this.replPort);
|
this.replServer = new ReplServer(this.pluginBridge, this.replPort, this.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -17,13 +17,16 @@ export class ReplServer {
|
|||||||
private readonly logger = createLogger("ReplServer");
|
private readonly logger = createLogger("ReplServer");
|
||||||
private readonly app: express.Application;
|
private readonly app: express.Application;
|
||||||
private readonly port: number;
|
private readonly port: number;
|
||||||
|
private readonly host: string;
|
||||||
private server: any;
|
private server: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly pluginBridge: PluginBridge,
|
private readonly pluginBridge: PluginBridge,
|
||||||
port: number = 4403
|
port: number = 4403,
|
||||||
|
host: string = "localhost"
|
||||||
) {
|
) {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
|
this.host = host;
|
||||||
this.app = express();
|
this.app = express();
|
||||||
this.setupMiddleware();
|
this.setupMiddleware();
|
||||||
this.setupRoutes();
|
this.setupRoutes();
|
||||||
@ -86,9 +89,9 @@ export class ReplServer {
|
|||||||
*/
|
*/
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.server = this.app.listen(this.port, () => {
|
this.server = this.app.listen(this.port, this.host, () => {
|
||||||
this.logger.info(`REPL server started on port ${this.port}`);
|
this.logger.info(`REPL server started on port ${this.port}`);
|
||||||
this.logger.info(`REPL interface URL: http://${this.pluginBridge.mcpServer.host}:${this.port}`);
|
this.logger.info(`REPL interface URL: http://${this.host}:${this.port}`);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user