bdy 1.25.20 → 1.25.21-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.25.20",
4
+ "version": "1.25.21-dev",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "homepage": "https://buddy.works/docs/cli",
@@ -305,6 +305,17 @@ class AgentManagerClass {
305
305
  res.statusCode = 404;
306
306
  res.end('Not found');
307
307
  }
308
+ // The server is bound to loopback, but a web page open on this machine can still
309
+ // reach it: a no-cors POST or an <img> fires without a preflight, a WebSocket is
310
+ // never subject to CORS, and DNS rebinding lets a page read the responses. bdy
311
+ // sends no Origin and always targets 127.0.0.1, browsers always send Origin on
312
+ // those requests and keep the rebound name in Host - so refuse either.
313
+ isLocalRequest(req) {
314
+ if (req.headers.origin !== undefined)
315
+ return false;
316
+ const host = req.headers.host;
317
+ return (host === `127.0.0.1:${this.port}` || host === `localhost:${this.port}`);
318
+ }
308
319
  serverError(res, message) {
309
320
  res.statusCode = 400;
310
321
  res.end(JSON.stringify({
@@ -709,6 +720,11 @@ class AgentManagerClass {
709
720
  }
710
721
  async processRequest(req, res) {
711
722
  logger_1.default.debug(`MANAGER REQUEST: ${req.url}`);
723
+ if (!this.isLocalRequest(req)) {
724
+ res.statusCode = 403;
725
+ res.end('Forbidden');
726
+ return;
727
+ }
712
728
  if (req.url === '/status')
713
729
  return this.processStatus(res);
714
730
  if (req.url === '/target')
@@ -751,6 +767,10 @@ class AgentManagerClass {
751
767
  }
752
768
  processUpgrade(req, socket, head) {
753
769
  logger_1.default.debug(`MANAGER REQUEST: ${req.url}`);
770
+ if (!this.isLocalRequest(req)) {
771
+ socket.destroy();
772
+ return;
773
+ }
754
774
  const url = new URL(req.url || '', 'http://localhost');
755
775
  const id = url.searchParams.get('id');
756
776
  if (url.pathname === '/tunnel' && id && this.ws) {
@@ -801,7 +821,8 @@ class AgentManagerClass {
801
821
  logger_1.default.error(err);
802
822
  }
803
823
  });
804
- this.server.listen(this.port, () => {
824
+ // Loopback only: the API has no credentials, every caller is a local bdy.
825
+ this.server.listen(this.port ?? 0, '127.0.0.1', () => {
805
826
  logger_1.default.info(texts_1.LOG_AGENT_SERVER_STARTED);
806
827
  });
807
828
  }
@@ -10,7 +10,7 @@ const undici_1 = __importDefault(require("undici"));
10
10
  const TIMEOUT = 120000;
11
11
  const openSocket = (port, path) => {
12
12
  return new Promise((resolve, reject) => {
13
- const ws = new ws_1.WebSocket(`ws://localhost:${port}${path}`);
13
+ const ws = new ws_1.WebSocket(`ws://127.0.0.1:${port}${path}`);
14
14
  ws.on('error', () => {
15
15
  reject(new Error(texts_1.ERR_FAILED_TO_CONNECT_TO_AGENT));
16
16
  });
@@ -26,7 +26,7 @@ const makeRequest = async (port, path, body) => {
26
26
  }, TIMEOUT);
27
27
  logger_1.default.debug(`TUNNEL API AGENT REQUEST: :${port}${path}`);
28
28
  logger_1.default.debug(body);
29
- const response = await undici_1.default.fetch(`http://localhost:${port}${path}`, {
29
+ const response = await undici_1.default.fetch(`http://127.0.0.1:${port}${path}`, {
30
30
  body: body ? JSON.stringify(body) : null,
31
31
  headers: { 'Content-Type': 'application/json' },
32
32
  method: 'POST',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.25.20",
4
+ "version": "1.25.21-dev",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "homepage": "https://buddy.works/docs/cli",