@botbuddy/cli 1.62.3 → 1.62.4
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/daemon/bin/darwin-arm64/botbuddy-daemon +0 -0
- package/daemon/bin/linux-x64/botbuddy-daemon +0 -0
- package/daemon-release/darwin-arm64.json +4 -4
- package/daemon-release/linux-x64.json +4 -4
- package/daemon-release/manifest.json +5 -5
- package/package.json +1 -1
- package/src/stack-tunnel.mjs +35 -6
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.62.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.62.4",
|
|
3
|
+
"source_commit": "1cc371424a95b39915a3e882a428688bced7386a",
|
|
4
|
+
"source_sha256": "8d9dd0daf489c418b04d34b57696a6f97a9346d141f9266dda3a74e7ca87d4cf",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "4ce65712c580403552a1d15f9dce56149dfdf48d121c634a6f5c748cb48e85fd"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.62.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.62.4",
|
|
3
|
+
"source_commit": "1cc371424a95b39915a3e882a428688bced7386a",
|
|
4
|
+
"source_sha256": "8d9dd0daf489c418b04d34b57696a6f97a9346d141f9266dda3a74e7ca87d4cf",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "4993e29705d75254afd21fadcbef568faf8650471df1bbcd9a96a6c5d3d2a254"
|
|
7
7
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
|
-
"release_version": "1.62.
|
|
4
|
-
"source_commit": "
|
|
5
|
-
"source_sha256": "
|
|
3
|
+
"release_version": "1.62.4",
|
|
4
|
+
"source_commit": "1cc371424a95b39915a3e882a428688bced7386a",
|
|
5
|
+
"source_sha256": "8d9dd0daf489c418b04d34b57696a6f97a9346d141f9266dda3a74e7ca87d4cf",
|
|
6
6
|
"targets": {
|
|
7
7
|
"darwin-arm64": {
|
|
8
8
|
"goos": "darwin",
|
|
9
9
|
"goarch": "arm64",
|
|
10
10
|
"binary": "daemon/bin/darwin-arm64/botbuddy-daemon",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "4ce65712c580403552a1d15f9dce56149dfdf48d121c634a6f5c748cb48e85fd",
|
|
12
12
|
"mode": "0755"
|
|
13
13
|
},
|
|
14
14
|
"linux-x64": {
|
|
15
15
|
"goos": "linux",
|
|
16
16
|
"goarch": "amd64",
|
|
17
17
|
"binary": "daemon/bin/linux-x64/botbuddy-daemon",
|
|
18
|
-
"sha256": "
|
|
18
|
+
"sha256": "4993e29705d75254afd21fadcbef568faf8650471df1bbcd9a96a6c5d3d2a254",
|
|
19
19
|
"mode": "0755"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
package/src/stack-tunnel.mjs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
// Dependency-free (Node stdlib only).
|
|
23
23
|
|
|
24
24
|
import { spawn, spawnSync } from "node:child_process";
|
|
25
|
-
import { connect, createServer } from "node:net";
|
|
25
|
+
import { connect, createServer, isIP } from "node:net";
|
|
26
26
|
|
|
27
27
|
export const SSH_FORWARD_OPTIONS = Object.freeze([
|
|
28
28
|
"-N", "-T",
|
|
@@ -299,7 +299,7 @@ export async function openSshForwards({
|
|
|
299
299
|
* `GatewayPorts yes` silently binds it on every interface, exposing this machine's
|
|
300
300
|
* callback service to the host's network. The CLI cannot read sshd_config, so after the
|
|
301
301
|
* forward is up it asks the host which address the listener really holds and refuses
|
|
302
|
-
* anything but loopback (fail closed: no
|
|
302
|
+
* anything but loopback (fail closed: no inventory, no listener, unreadable → refused).
|
|
303
303
|
*/
|
|
304
304
|
export function verifyCallbackLoopback({ sshBin, sshTarget, port, env = process.env, run = spawnSync, label = null }) {
|
|
305
305
|
const what = label ?? `the reverse forward for --callback-port ${port}`;
|
|
@@ -307,18 +307,47 @@ export function verifyCallbackLoopback({ sshBin, sshTarget, port, env = process.
|
|
|
307
307
|
`${what} on ${sshTarget} could not be proven loopback-only (${why}). ` +
|
|
308
308
|
"An sshd with GatewayPorts yes binds -R forwards on every interface; set GatewayPorts no (or clientspecified) on the host, " +
|
|
309
309
|
"or run without --callback-port.", extra);
|
|
310
|
+
const checkedPort = validPort(port);
|
|
311
|
+
if (checkedPort == null) throw fail("invalid listener port");
|
|
312
|
+
// Linux lsof cannot see root-owned sshd sockets from an unprivileged login (BOT-2743).
|
|
313
|
+
// ss reads the socket table without process visibility; -p is deliberately omitted.
|
|
314
|
+
// Dispatch on the remote OS in the SAME SSH invocation, with no guessed fallback.
|
|
315
|
+
const command = `case "$(uname -s)" in Linux) printf 'bb-ss-v1\\n'; ss -H -ltn '( sport = :${checkedPort} )';; ` +
|
|
316
|
+
`Darwin) lsof -nP -iTCP:${checkedPort} -sTCP:LISTEN -Fn;; ` +
|
|
317
|
+
"*) printf 'unsupported listener inventory platform\\n' >&2; exit 69;; esac";
|
|
310
318
|
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process -- fixed argv; target validated; integer port.
|
|
311
319
|
const r = run(sshBin, [
|
|
312
320
|
"-T", "-o", "BatchMode=yes", "-o", "ControlMaster=no", "-o", "ControlPath=none", "--", sshTarget,
|
|
313
|
-
|
|
321
|
+
command,
|
|
314
322
|
], { encoding: "utf8", env, timeout: 15_000 });
|
|
315
323
|
if (r.error || r.status !== 0) {
|
|
316
324
|
const tail = String(r.stderr || r.error?.message || "").trim().split("\n").slice(-1)[0];
|
|
317
325
|
// lsof exits 1 with no output when nothing listens.
|
|
318
|
-
if (r.status === 1 && !String(r.stdout || "").trim()
|
|
319
|
-
|
|
326
|
+
if (!r.error && r.status === 1 && !String(r.stdout || "").trim() && !String(r.stderr || "").trim()) {
|
|
327
|
+
throw fail(`no listener on the host's port ${port}`, { noListener: true });
|
|
328
|
+
}
|
|
329
|
+
throw fail(`could not inspect host sockets with ss/lsof (exit ${r.status ?? "?"}${tail ? `: ${tail}` : ""})`);
|
|
330
|
+
}
|
|
331
|
+
const output = String(r.stdout || "");
|
|
332
|
+
let addresses;
|
|
333
|
+
if (output.startsWith("bb-ss-v1\n")) {
|
|
334
|
+
addresses = output.slice("bb-ss-v1\n".length).split("\n").filter((line) => line.trim()).map((line) => {
|
|
335
|
+
// ss -H -ltn: State Recv-Q Send-Q Local-Address:Port Peer-Address:Port.
|
|
336
|
+
// Reject the whole answer on any bad row: silently skipping a wildcard is unsafe.
|
|
337
|
+
const fields = line.trim().split(/\s+/);
|
|
338
|
+
const local = /^(.+):(\d+)$/.exec(fields[3] ?? "");
|
|
339
|
+
let address = local?.[1];
|
|
340
|
+
if (address?.startsWith("[") && address.endsWith("]")) address = address.slice(1, -1);
|
|
341
|
+
const family = isIP(address ?? "");
|
|
342
|
+
if (fields.length !== 5 || fields[0] !== "LISTEN" || !/^\d+$/.test(fields[1]) || !/^\d+$/.test(fields[2]) ||
|
|
343
|
+
!local || Number(local[2]) !== checkedPort || (!family && address !== "*")) {
|
|
344
|
+
throw fail("malformed ss listener inventory");
|
|
345
|
+
}
|
|
346
|
+
return `${family === 6 ? `[${address}]` : address}:${checkedPort}`;
|
|
347
|
+
});
|
|
348
|
+
} else {
|
|
349
|
+
addresses = output.split("\n").filter((l) => l.startsWith("n")).map((l) => l.slice(1).trim());
|
|
320
350
|
}
|
|
321
|
-
const addresses = String(r.stdout || "").split("\n").filter((l) => l.startsWith("n")).map((l) => l.slice(1).trim());
|
|
322
351
|
if (!addresses.length) throw fail(`no listener on the host's port ${port}`, { noListener: true });
|
|
323
352
|
const exposed = addresses.filter((a) => !(a.startsWith("127.") || a.startsWith("[::1]:") || a.startsWith("localhost:")));
|
|
324
353
|
if (exposed.length) throw fail(`the host listens on ${exposed.join(", ")}`, { exposed, listeners: addresses });
|