@matter/nodejs-shell 0.17.5-alpha.0-20260702-a2cd6f764 → 0.17.5-alpha.0-20260706-fd4c49e2e

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.
package/README.md CHANGED
@@ -4,6 +4,8 @@ This project provides a light-weight node.js implementation of a Matter shell ap
4
4
 
5
5
  > This package supports all Node.js LTS versions starting with 20.x
6
6
 
7
+ > **⚠️ Development and testing tool only.** The shell (and its optional WebSocket/web interface) is intended for development, testing and debugging. It is **not** hardened for production use: it has no authentication, stores credentials unencrypted on disk, and the WebSocket/web interface serves the shell without access control. Do not expose it on untrusted networks or run it as a production service.
8
+
7
9
  ## Install
8
10
 
9
11
  If you want to install just the shell app then you can do so by running:
@@ -448,6 +450,8 @@ $ more .matter-shell-1/Node.ip
448
450
 
449
451
  If the matter shell is started with the parameter --webSocketInterface all interaction with the shell will be done over a websocket instead of the local terminal. The parameter --webSocketPort NNNN can be used to change from the default port of 3000 to a user-specified port. If the parameter --webServer is added, the matter shell will also start an http server that will serve files from the same directory as the application itself utilizying the same port as the websocket. The functionality of the shell will be identical to the above description with the exception that the "exit" command will only close the websocket and not exit the matter shell application.
450
452
 
453
+ > **⚠️ Security:** The WebSocket/web interface has **no authentication** — anyone who can reach the port gets full shell control. By default it binds to loopback (`127.0.0.1`) only; a warning is logged to remind you of this. To expose it on other interfaces pass `--webAddress <address>` (e.g. `--webAddress 0.0.0.0`), but only do so on a trusted network. This interface is meant for local development and testing, not for production or internet-facing deployments.
454
+
451
455
  An example application that shows interaction from a web browser is included. The example shows how commands can be sent from html and javascript in the browser to the shell and how the results of the commands can be parsed to create a user interface.
452
456
 
453
457
  ![Web Interface](src/shell/webassets/WebShell.png "Example Application")
package/dist/esm/app.js CHANGED
@@ -91,6 +91,11 @@ async function main() {
91
91
  description: "Enable Web server when using WebSocket interface",
92
92
  type: "boolean",
93
93
  default: false
94
+ },
95
+ webAddress: {
96
+ description: "Address the WebSocket/web server binds to. Defaults to loopback (127.0.0.1); pass e.g. 0.0.0.0 to expose on all interfaces.",
97
+ type: "string",
98
+ default: void 0
94
99
  }
95
100
  });
96
101
  },
@@ -108,7 +113,8 @@ async function main() {
108
113
  logfile,
109
114
  webSocketInterface,
110
115
  webSocketPort,
111
- webServer
116
+ webServer,
117
+ webAddress
112
118
  } = argv;
113
119
  theNode = new MatterNode(nodeNum, netInterface);
114
120
  await theNode.initialize(factoryReset);
@@ -128,7 +134,7 @@ async function main() {
128
134
  setLogLevel("default", await theNode.Store.get("LogLevel", "info"));
129
135
  if (webSocketInterface) {
130
136
  Logger.format = LogFormat.PLAIN;
131
- initializeWebPlumbing(theNode, nodeNum, webSocketPort, webServer);
137
+ initializeWebPlumbing(theNode, nodeNum, webSocketPort, webServer, webAddress);
132
138
  } else {
133
139
  theShell = new Shell(theNode, nodeNum, PROMPT, process.stdin, process.stdout);
134
140
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/app.ts"],
4
- "mappings": "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",
4
+ "mappings": "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",
5
5
  "names": ["yargs"]
6
6
  }
@@ -14,26 +14,56 @@ let client;
14
14
  let server;
15
15
  let wss;
16
16
  const socketLogger = "websocket";
17
- function initializeWebPlumbing(theNode, nodeNum, webSocketPort, webServer) {
17
+ const logger = Logger.get("WebPlumbing");
18
+ const DEFAULT_LISTEN_ADDRESS = "127.0.0.1";
19
+ function initializeWebPlumbing(theNode, nodeNum, webSocketPort, webServer, listenAddress) {
20
+ const configuredAddress = listenAddress?.trim() || void 0;
21
+ const listenHost = configuredAddress ?? DEFAULT_LISTEN_ADDRESS;
22
+ if (configuredAddress === void 0) {
23
+ logger.warn(
24
+ `No listen address configured; binding the WebSocket/web server to loopback ${DEFAULT_LISTEN_ADDRESS} only. Pass --webAddress <address> (e.g. 0.0.0.0) to expose it on other interfaces.`
25
+ );
26
+ }
18
27
  if (webServer) {
19
- const root = path.resolve(__dirname) ?? "./";
28
+ const resolvedRoot = path.resolve(__dirname);
29
+ let root;
30
+ try {
31
+ root = fs.realpathSync(resolvedRoot);
32
+ } catch (error) {
33
+ logger.warn(`Could not resolve web root real path (${resolvedRoot}): ${error}. Using unresolved path.`);
34
+ root = resolvedRoot;
35
+ }
36
+ const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;
37
+ const isContained = (candidate) => candidate === root || candidate.startsWith(rootWithSep);
20
38
  server = http.createServer((req, res) => {
21
39
  const url = req.url ?? "/";
22
- const safePath = path.normalize(
23
- path.join(root, decodeURIComponent(url === "/" ? "/index.html" : url))
24
- );
25
- if (!safePath.startsWith(root)) {
40
+ let decodedPath;
41
+ try {
42
+ decodedPath = decodeURIComponent(url === "/" ? "/index.html" : url);
43
+ } catch {
44
+ res.writeHead(400).end("Bad Request");
45
+ return;
46
+ }
47
+ const safePath = path.normalize(path.join(root, decodedPath));
48
+ if (!isContained(safePath)) {
26
49
  res.writeHead(403).end("Forbidden");
27
50
  return;
28
51
  }
29
- fs.readFile(safePath, (err, data) => {
30
- if (err) return res.writeHead(404).end("Not Found");
31
- res.writeHead(200).end(data);
52
+ fs.realpath(safePath, (realpathErr, resolvedPath) => {
53
+ if (realpathErr) return res.writeHead(404).end("Not Found");
54
+ if (!isContained(resolvedPath)) {
55
+ res.writeHead(403).end("Forbidden");
56
+ return;
57
+ }
58
+ fs.readFile(resolvedPath, (err, data) => {
59
+ if (err) return res.writeHead(404).end("Not Found");
60
+ res.writeHead(200).end(data);
61
+ });
32
62
  });
33
- }).listen(webSocketPort);
63
+ }).listen(webSocketPort, listenHost);
34
64
  wss = new WebSocketServer({ server });
35
- } else wss = new WebSocketServer({ port: webSocketPort });
36
- console.info(`WebSocket server running on ws://localhost:${webSocketPort}`);
65
+ } else wss = new WebSocketServer({ port: webSocketPort, host: listenHost });
66
+ console.info(`WebSocket server listening on ${listenHost} port ${webSocketPort}`);
37
67
  console.log = // console.debug = // too much traffic - kills the websocket
38
68
  console.info = console.warn = console.error = (...args) => {
39
69
  if (client && client.readyState === WebSocket.OPEN) {
@@ -50,11 +80,11 @@ function initializeWebPlumbing(theNode, nodeNum, webSocketPort, webServer) {
50
80
  return;
51
81
  }
52
82
  client = ws;
53
- createWebSocketLogger(ws).then((logger) => {
83
+ createWebSocketLogger(ws).then((logger2) => {
54
84
  delete Logger.destinations["Shell"];
55
85
  Logger.destinations[socketLogger] = LogDestination({
56
86
  name: socketLogger,
57
- write: (text, message) => logger(message.level, text)
87
+ write: (text, message) => logger2(message.level, text)
58
88
  });
59
89
  }).catch((err) => {
60
90
  console.error("Failed to add WebSocket logger: " + err);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/web_plumbing.ts"],
4
- "mappings": "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",
5
- "names": []
4
+ "mappings": "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",
5
+ "names": ["logger"]
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matter/nodejs-shell",
3
- "version": "0.17.5-alpha.0-20260702-a2cd6f764",
3
+ "version": "0.17.5-alpha.0-20260706-fd4c49e2e",
4
4
  "description": "Shell app for Matter controller",
5
5
  "keywords": [
6
6
  "iot",
@@ -34,14 +34,14 @@
34
34
  "matter-shell": "dist/cjs/app.js"
35
35
  },
36
36
  "dependencies": {
37
- "@matter/general": "0.17.5-alpha.0-20260702-a2cd6f764",
38
- "@matter/model": "0.17.5-alpha.0-20260702-a2cd6f764",
39
- "@matter/node": "0.17.5-alpha.0-20260702-a2cd6f764",
40
- "@matter/nodejs": "0.17.5-alpha.0-20260702-a2cd6f764",
41
- "@matter/nodejs-ble": "0.17.5-alpha.0-20260702-a2cd6f764",
42
- "@matter/protocol": "0.17.5-alpha.0-20260702-a2cd6f764",
43
- "@matter/types": "0.17.5-alpha.0-20260702-a2cd6f764",
44
- "@project-chip/matter.js": "0.17.5-alpha.0-20260702-a2cd6f764",
37
+ "@matter/general": "0.17.5-alpha.0-20260706-fd4c49e2e",
38
+ "@matter/model": "0.17.5-alpha.0-20260706-fd4c49e2e",
39
+ "@matter/node": "0.17.5-alpha.0-20260706-fd4c49e2e",
40
+ "@matter/nodejs": "0.17.5-alpha.0-20260706-fd4c49e2e",
41
+ "@matter/nodejs-ble": "0.17.5-alpha.0-20260706-fd4c49e2e",
42
+ "@matter/protocol": "0.17.5-alpha.0-20260706-fd4c49e2e",
43
+ "@matter/types": "0.17.5-alpha.0-20260706-fd4c49e2e",
44
+ "@project-chip/matter.js": "0.17.5-alpha.0-20260706-fd4c49e2e",
45
45
  "ws": "^8.21.0",
46
46
  "yargs": "^18.0.0"
47
47
  },
package/src/app.ts CHANGED
@@ -107,6 +107,12 @@ async function main() {
107
107
  type: "boolean",
108
108
  default: false,
109
109
  },
110
+ webAddress: {
111
+ description:
112
+ "Address the WebSocket/web server binds to. Defaults to loopback (127.0.0.1); pass e.g. 0.0.0.0 to expose on all interfaces.",
113
+ type: "string",
114
+ default: undefined,
115
+ },
110
116
  });
111
117
  },
112
118
  async argv => {
@@ -125,6 +131,7 @@ async function main() {
125
131
  webSocketInterface,
126
132
  webSocketPort,
127
133
  webServer,
134
+ webAddress,
128
135
  } = argv;
129
136
 
130
137
  theNode = new MatterNode(nodeNum, netInterface);
@@ -147,7 +154,7 @@ async function main() {
147
154
 
148
155
  if (webSocketInterface) {
149
156
  Logger.format = LogFormat.PLAIN;
150
- initializeWebPlumbing(theNode, nodeNum, webSocketPort, webServer); // set up but wait for connect to create Shell
157
+ initializeWebPlumbing(theNode, nodeNum, webSocketPort, webServer, webAddress); // set up but wait for connect to create Shell
151
158
  } else {
152
159
  theShell = new Shell(theNode, nodeNum, PROMPT, process.stdin, process.stdout);
153
160
  }
@@ -19,39 +19,76 @@ let client: WebSocket;
19
19
  let server: Server;
20
20
  let wss: WebSocketServer;
21
21
  const socketLogger = "websocket";
22
+ const logger = Logger.get("WebPlumbing");
23
+
24
+ const DEFAULT_LISTEN_ADDRESS = "127.0.0.1";
22
25
 
23
26
  export function initializeWebPlumbing(
24
27
  theNode: MatterNode,
25
28
  nodeNum: number,
26
29
  webSocketPort: number,
27
30
  webServer: boolean,
31
+ listenAddress?: string,
28
32
  ): void {
33
+ const configuredAddress = listenAddress?.trim() || undefined;
34
+ const listenHost = configuredAddress ?? DEFAULT_LISTEN_ADDRESS;
35
+ if (configuredAddress === undefined) {
36
+ logger.warn(
37
+ `No listen address configured; binding the WebSocket/web server to loopback ${DEFAULT_LISTEN_ADDRESS} only. ` +
38
+ `Pass --webAddress <address> (e.g. 0.0.0.0) to expose it on other interfaces.`,
39
+ );
40
+ }
41
+
29
42
  if (webServer) {
30
- const root: string = path.resolve(__dirname) ?? "./";
43
+ const resolvedRoot = path.resolve(__dirname);
44
+ let root: string;
45
+ try {
46
+ root = fs.realpathSync(resolvedRoot);
47
+ } catch (error) {
48
+ logger.warn(`Could not resolve web root real path (${resolvedRoot}): ${error}. Using unresolved path.`);
49
+ root = resolvedRoot;
50
+ }
51
+ const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;
52
+ // The trailing separator prevents sibling directories that merely share the root string as a
53
+ // prefix from passing the containment check.
54
+ const isContained = (candidate: string) => candidate === root || candidate.startsWith(rootWithSep);
31
55
 
32
56
  server = http
33
57
  .createServer((req, res) => {
34
58
  const url = req.url ?? "/";
35
- const safePath: string = path.normalize(
36
- path.join(root, decodeURIComponent(url === "/" ? "/index.html" : url)),
37
- );
59
+ let decodedPath: string;
60
+ try {
61
+ decodedPath = decodeURIComponent(url === "/" ? "/index.html" : url);
62
+ } catch {
63
+ res.writeHead(400).end("Bad Request");
64
+ return;
65
+ }
66
+ const safePath: string = path.normalize(path.join(root, decodedPath));
38
67
 
39
- // Check that the resolved path is within the root directory
40
- if (!safePath.startsWith(root)) {
68
+ if (!isContained(safePath)) {
41
69
  res.writeHead(403).end("Forbidden");
42
70
  return;
43
71
  }
44
72
 
45
- fs.readFile(safePath, (err, data) => {
46
- if (err) return res.writeHead(404).end("Not Found");
47
- res.writeHead(200).end(data);
73
+ // Resolve symlinks so a link inside the root cannot redirect reads outside of it.
74
+ fs.realpath(safePath, (realpathErr, resolvedPath) => {
75
+ if (realpathErr) return res.writeHead(404).end("Not Found");
76
+ if (!isContained(resolvedPath)) {
77
+ res.writeHead(403).end("Forbidden");
78
+ return;
79
+ }
80
+
81
+ fs.readFile(resolvedPath, (err, data) => {
82
+ if (err) return res.writeHead(404).end("Not Found");
83
+ res.writeHead(200).end(data);
84
+ });
48
85
  });
49
86
  })
50
- .listen(webSocketPort);
87
+ .listen(webSocketPort, listenHost);
51
88
  wss = new WebSocketServer({ server });
52
- } else wss = new WebSocketServer({ port: webSocketPort });
89
+ } else wss = new WebSocketServer({ port: webSocketPort, host: listenHost });
53
90
 
54
- console.info(`WebSocket server running on ws://localhost:${webSocketPort}`);
91
+ console.info(`WebSocket server listening on ${listenHost} port ${webSocketPort}`);
55
92
 
56
93
  console.log =
57
94
  // console.debug = // too much traffic - kills the websocket