@kortix/agent-tunnel 0.13.50 → 0.13.52
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
|
@@ -169,6 +169,12 @@ The local config is the maximum boundary. The server cannot widen configured
|
|
|
169
169
|
filesystem paths, blocked paths, shell commands, timeouts, file sizes, or desktop
|
|
170
170
|
features.
|
|
171
171
|
|
|
172
|
+
A `shell` grant is local code execution. The allow list matches the executable
|
|
173
|
+
name exactly and the block list also matches every path ending in that name
|
|
174
|
+
(`/bin/rm` is `rm`). Arguments are not restricted: an allowed `git`, `find`, or
|
|
175
|
+
`python` can run any code through its own flags. Grant `shell` only to a session
|
|
176
|
+
you trust with this computer.
|
|
177
|
+
|
|
172
178
|
## Computer Use driver
|
|
173
179
|
|
|
174
180
|
Agent Tunnel never downloads or executes a desktop driver. Install `cua-driver`
|
package/dist/agent-cli.js
CHANGED
|
@@ -19,15 +19,13 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
19
19
|
return cached;
|
|
20
20
|
}
|
|
21
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
|
-
const to = isNodeMode || !mod || !mod.__esModule
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
}
|
|
22
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
31
29
|
if (canCache)
|
|
32
30
|
cache.set(mod, to);
|
|
33
31
|
return to;
|
|
@@ -46,21 +44,11 @@ var __export = (target, all) => {
|
|
|
46
44
|
set: __exportSetter.bind(all, name)
|
|
47
45
|
});
|
|
48
46
|
};
|
|
49
|
-
var __esm = (fn, res
|
|
50
|
-
if (fn)
|
|
51
|
-
try {
|
|
52
|
-
res = fn(fn = 0);
|
|
53
|
-
} catch (e) {
|
|
54
|
-
err = [e];
|
|
55
|
-
}
|
|
56
|
-
if (err)
|
|
57
|
-
throw err[0];
|
|
58
|
-
return res;
|
|
59
|
-
};
|
|
47
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
60
48
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
61
49
|
|
|
62
50
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/constants.js
|
|
63
|
-
var require_constants = __commonJS(
|
|
51
|
+
var require_constants = __commonJS((exports, module) => {
|
|
64
52
|
var BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"];
|
|
65
53
|
var hasBlob = typeof Blob !== "undefined";
|
|
66
54
|
if (hasBlob)
|
|
@@ -80,7 +68,7 @@ var require_constants = __commonJS(function(exports, module) {
|
|
|
80
68
|
});
|
|
81
69
|
|
|
82
70
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/buffer-util.js
|
|
83
|
-
var require_buffer_util = __commonJS(
|
|
71
|
+
var require_buffer_util = __commonJS((exports, module) => {
|
|
84
72
|
var { EMPTY_BUFFER } = require_constants();
|
|
85
73
|
var FastBuffer = Buffer[Symbol.species];
|
|
86
74
|
function concat(list, totalLength) {
|
|
@@ -141,11 +129,11 @@ var require_buffer_util = __commonJS(function(exports, module) {
|
|
|
141
129
|
if (!process.env.WS_NO_BUFFER_UTIL) {
|
|
142
130
|
try {
|
|
143
131
|
const bufferUtil = (()=>{throw new Error("Cannot require module "+"bufferutil");})();
|
|
144
|
-
module.exports.mask = function(source,
|
|
132
|
+
module.exports.mask = function(source, mask, output, offset, length) {
|
|
145
133
|
if (length < 48)
|
|
146
|
-
_mask(source,
|
|
134
|
+
_mask(source, mask, output, offset, length);
|
|
147
135
|
else
|
|
148
|
-
bufferUtil.mask(source,
|
|
136
|
+
bufferUtil.mask(source, mask, output, offset, length);
|
|
149
137
|
};
|
|
150
138
|
module.exports.unmask = function(buffer, mask) {
|
|
151
139
|
if (buffer.length < 32)
|
|
@@ -158,7 +146,7 @@ var require_buffer_util = __commonJS(function(exports, module) {
|
|
|
158
146
|
});
|
|
159
147
|
|
|
160
148
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/limiter.js
|
|
161
|
-
var require_limiter = __commonJS(
|
|
149
|
+
var require_limiter = __commonJS((exports, module) => {
|
|
162
150
|
var kDone = Symbol("kDone");
|
|
163
151
|
var kRun = Symbol("kRun");
|
|
164
152
|
|
|
@@ -190,7 +178,7 @@ var require_limiter = __commonJS(function(exports, module) {
|
|
|
190
178
|
});
|
|
191
179
|
|
|
192
180
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/permessage-deflate.js
|
|
193
|
-
var require_permessage_deflate = __commonJS(
|
|
181
|
+
var require_permessage_deflate = __commonJS((exports, module) => {
|
|
194
182
|
var zlib = __require("zlib");
|
|
195
183
|
var bufferUtil = require_buffer_util();
|
|
196
184
|
var Limiter = require_limiter();
|
|
@@ -378,7 +366,7 @@ var require_permessage_deflate = __commonJS(function(exports, module) {
|
|
|
378
366
|
callback(err);
|
|
379
367
|
return;
|
|
380
368
|
}
|
|
381
|
-
const
|
|
369
|
+
const data2 = bufferUtil.concat(this._inflate[kBuffers], this._inflate[kTotalLength]);
|
|
382
370
|
if (this._inflate._readableState.endEmitted) {
|
|
383
371
|
this._inflate.close();
|
|
384
372
|
this._inflate = null;
|
|
@@ -389,7 +377,7 @@ var require_permessage_deflate = __commonJS(function(exports, module) {
|
|
|
389
377
|
this._inflate.reset();
|
|
390
378
|
}
|
|
391
379
|
}
|
|
392
|
-
callback(null,
|
|
380
|
+
callback(null, data2);
|
|
393
381
|
});
|
|
394
382
|
}
|
|
395
383
|
_compress(data, fin, callback) {
|
|
@@ -411,9 +399,9 @@ var require_permessage_deflate = __commonJS(function(exports, module) {
|
|
|
411
399
|
if (!this._deflate) {
|
|
412
400
|
return;
|
|
413
401
|
}
|
|
414
|
-
let
|
|
402
|
+
let data2 = bufferUtil.concat(this._deflate[kBuffers], this._deflate[kTotalLength]);
|
|
415
403
|
if (fin) {
|
|
416
|
-
|
|
404
|
+
data2 = new FastBuffer(data2.buffer, data2.byteOffset, data2.length - 4);
|
|
417
405
|
}
|
|
418
406
|
this._deflate[kCallback] = null;
|
|
419
407
|
this._deflate[kTotalLength] = 0;
|
|
@@ -421,7 +409,7 @@ var require_permessage_deflate = __commonJS(function(exports, module) {
|
|
|
421
409
|
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
|
422
410
|
this._deflate.reset();
|
|
423
411
|
}
|
|
424
|
-
callback(null,
|
|
412
|
+
callback(null, data2);
|
|
425
413
|
});
|
|
426
414
|
}
|
|
427
415
|
}
|
|
@@ -454,7 +442,7 @@ var require_permessage_deflate = __commonJS(function(exports, module) {
|
|
|
454
442
|
});
|
|
455
443
|
|
|
456
444
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/validation.js
|
|
457
|
-
var require_validation = __commonJS(
|
|
445
|
+
var require_validation = __commonJS((exports, module) => {
|
|
458
446
|
var { isUtf8 } = __require("buffer");
|
|
459
447
|
var { hasBlob } = require_constants();
|
|
460
448
|
var tokenChars = [
|
|
@@ -641,7 +629,7 @@ var require_validation = __commonJS(function(exports, module) {
|
|
|
641
629
|
});
|
|
642
630
|
|
|
643
631
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/receiver.js
|
|
644
|
-
var require_receiver = __commonJS(
|
|
632
|
+
var require_receiver = __commonJS((exports, module) => {
|
|
645
633
|
var { Writable } = __require("stream");
|
|
646
634
|
var PerMessageDeflate = require_permessage_deflate();
|
|
647
635
|
var {
|
|
@@ -1035,7 +1023,7 @@ var require_receiver = __commonJS(function(exports, module) {
|
|
|
1035
1023
|
});
|
|
1036
1024
|
|
|
1037
1025
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/sender.js
|
|
1038
|
-
var require_sender = __commonJS(
|
|
1026
|
+
var require_sender = __commonJS((exports, module) => {
|
|
1039
1027
|
var { Duplex } = __require("stream");
|
|
1040
1028
|
var { randomFillSync } = __require("crypto");
|
|
1041
1029
|
var {
|
|
@@ -1394,7 +1382,7 @@ var require_sender = __commonJS(function(exports, module) {
|
|
|
1394
1382
|
});
|
|
1395
1383
|
|
|
1396
1384
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/event-target.js
|
|
1397
|
-
var require_event_target = __commonJS(
|
|
1385
|
+
var require_event_target = __commonJS((exports, module) => {
|
|
1398
1386
|
var { kForOnEventAttribute, kListener } = require_constants();
|
|
1399
1387
|
var kCode = Symbol("kCode");
|
|
1400
1388
|
var kData = Symbol("kData");
|
|
@@ -1545,7 +1533,7 @@ var require_event_target = __commonJS(function(exports, module) {
|
|
|
1545
1533
|
});
|
|
1546
1534
|
|
|
1547
1535
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/extension.js
|
|
1548
|
-
var require_extension = __commonJS(
|
|
1536
|
+
var require_extension = __commonJS((exports, module) => {
|
|
1549
1537
|
var { tokenChars } = require_validation();
|
|
1550
1538
|
function push(dest, name, elem) {
|
|
1551
1539
|
if (dest[name] === undefined)
|
|
@@ -1710,7 +1698,7 @@ var require_extension = __commonJS(function(exports, module) {
|
|
|
1710
1698
|
});
|
|
1711
1699
|
|
|
1712
1700
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/websocket.js
|
|
1713
|
-
var require_websocket = __commonJS(
|
|
1701
|
+
var require_websocket = __commonJS((exports, module) => {
|
|
1714
1702
|
var EventEmitter = __require("events");
|
|
1715
1703
|
var https = __require("https");
|
|
1716
1704
|
var http = __require("http");
|
|
@@ -2133,7 +2121,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
2133
2121
|
}
|
|
2134
2122
|
const defaultPort = isSecure ? 443 : 80;
|
|
2135
2123
|
const key = randomBytes(16).toString("base64");
|
|
2136
|
-
const
|
|
2124
|
+
const request = isSecure ? https.request : http.request;
|
|
2137
2125
|
const protocolSet = new Set;
|
|
2138
2126
|
let perMessageDeflate;
|
|
2139
2127
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -2192,8 +2180,8 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
2192
2180
|
const headers = options && options.headers;
|
|
2193
2181
|
options = { ...options, headers: {} };
|
|
2194
2182
|
if (headers) {
|
|
2195
|
-
for (const [
|
|
2196
|
-
options.headers[
|
|
2183
|
+
for (const [key2, value] of Object.entries(headers)) {
|
|
2184
|
+
options.headers[key2.toLowerCase()] = value;
|
|
2197
2185
|
}
|
|
2198
2186
|
}
|
|
2199
2187
|
} else if (websocket.listenerCount("redirect") === 0) {
|
|
@@ -2209,12 +2197,12 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
2209
2197
|
if (opts.auth && !options.headers.authorization) {
|
|
2210
2198
|
options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
|
|
2211
2199
|
}
|
|
2212
|
-
req = websocket._req =
|
|
2200
|
+
req = websocket._req = request(opts);
|
|
2213
2201
|
if (websocket._redirects) {
|
|
2214
2202
|
websocket.emit("redirect", websocket.url, req);
|
|
2215
2203
|
}
|
|
2216
2204
|
} else {
|
|
2217
|
-
req = websocket._req =
|
|
2205
|
+
req = websocket._req = request(opts);
|
|
2218
2206
|
}
|
|
2219
2207
|
if (opts.timeout) {
|
|
2220
2208
|
req.on("timeout", () => {
|
|
@@ -2482,7 +2470,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
2482
2470
|
});
|
|
2483
2471
|
|
|
2484
2472
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/stream.js
|
|
2485
|
-
var require_stream = __commonJS(
|
|
2473
|
+
var require_stream = __commonJS((exports, module) => {
|
|
2486
2474
|
var WebSocket2 = require_websocket();
|
|
2487
2475
|
var { Duplex } = __require("stream");
|
|
2488
2476
|
function emitClose(stream) {
|
|
@@ -2532,9 +2520,9 @@ var require_stream = __commonJS(function(exports, module) {
|
|
|
2532
2520
|
return;
|
|
2533
2521
|
}
|
|
2534
2522
|
let called = false;
|
|
2535
|
-
ws.once("error", function error(
|
|
2523
|
+
ws.once("error", function error(err2) {
|
|
2536
2524
|
called = true;
|
|
2537
|
-
callback(
|
|
2525
|
+
callback(err2);
|
|
2538
2526
|
});
|
|
2539
2527
|
ws.once("close", function close() {
|
|
2540
2528
|
if (!called)
|
|
@@ -2585,7 +2573,7 @@ var require_stream = __commonJS(function(exports, module) {
|
|
|
2585
2573
|
});
|
|
2586
2574
|
|
|
2587
2575
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/subprotocol.js
|
|
2588
|
-
var require_subprotocol = __commonJS(
|
|
2576
|
+
var require_subprotocol = __commonJS((exports, module) => {
|
|
2589
2577
|
var { tokenChars } = require_validation();
|
|
2590
2578
|
function parse(header) {
|
|
2591
2579
|
const protocols = new Set;
|
|
@@ -2606,11 +2594,11 @@ var require_subprotocol = __commonJS(function(exports, module) {
|
|
|
2606
2594
|
}
|
|
2607
2595
|
if (end === -1)
|
|
2608
2596
|
end = i;
|
|
2609
|
-
const
|
|
2610
|
-
if (protocols.has(
|
|
2611
|
-
throw new SyntaxError(`The "${
|
|
2597
|
+
const protocol2 = header.slice(start, end);
|
|
2598
|
+
if (protocols.has(protocol2)) {
|
|
2599
|
+
throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`);
|
|
2612
2600
|
}
|
|
2613
|
-
protocols.add(
|
|
2601
|
+
protocols.add(protocol2);
|
|
2614
2602
|
start = end = -1;
|
|
2615
2603
|
} else {
|
|
2616
2604
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
@@ -2630,7 +2618,7 @@ var require_subprotocol = __commonJS(function(exports, module) {
|
|
|
2630
2618
|
});
|
|
2631
2619
|
|
|
2632
2620
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/websocket-server.js
|
|
2633
|
-
var require_websocket_server = __commonJS(
|
|
2621
|
+
var require_websocket_server = __commonJS((exports, module) => {
|
|
2634
2622
|
var EventEmitter = __require("events");
|
|
2635
2623
|
var http = __require("http");
|
|
2636
2624
|
var { Duplex } = __require("stream");
|
|
@@ -2942,8 +2930,15 @@ var require_websocket_server = __commonJS(function(exports, module) {
|
|
|
2942
2930
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/wrapper.mjs
|
|
2943
2931
|
var exports_wrapper = {};
|
|
2944
2932
|
__export(exports_wrapper, {
|
|
2933
|
+
subprotocol: () => import_subprotocol.default,
|
|
2934
|
+
extension: () => import_extension.default,
|
|
2935
|
+
default: () => wrapper_default,
|
|
2936
|
+
createWebSocketStream: () => import_stream.default,
|
|
2937
|
+
WebSocketServer: () => import_websocket_server.default,
|
|
2945
2938
|
WebSocket: () => import_websocket.default,
|
|
2946
|
-
|
|
2939
|
+
Sender: () => import_sender.default,
|
|
2940
|
+
Receiver: () => import_receiver.default,
|
|
2941
|
+
PerMessageDeflate: () => import_permessage_deflate.default
|
|
2947
2942
|
});
|
|
2948
2943
|
var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket, import_websocket_server, wrapper_default;
|
|
2949
2944
|
var init_wrapper = __esm(() => {
|
|
@@ -2962,7 +2957,7 @@ var init_wrapper = __esm(() => {
|
|
|
2962
2957
|
if (typeof globalThis.WebSocket === "undefined") {
|
|
2963
2958
|
try {
|
|
2964
2959
|
const ws = await Promise.resolve().then(() => (init_wrapper(), exports_wrapper));
|
|
2965
|
-
const WebSocketImpl = ws.WebSocket ??
|
|
2960
|
+
const WebSocketImpl = ws.WebSocket ?? ws.default;
|
|
2966
2961
|
globalThis.WebSocket = WebSocketImpl;
|
|
2967
2962
|
} catch {
|
|
2968
2963
|
console.error('[agent-tunnel] WebSocket is not available. Install the "ws" package or use Node.js 22+.');
|
|
@@ -3928,7 +3923,7 @@ function visibleLength(value) {
|
|
|
3928
3923
|
function clearScreen() {
|
|
3929
3924
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
3930
3925
|
}
|
|
3931
|
-
var sleep = (ms) => new Promise((
|
|
3926
|
+
var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3932
3927
|
var glyph = {
|
|
3933
3928
|
on: `${c.green}●${c.reset}`,
|
|
3934
3929
|
off: `${c.gray}○${c.reset}`,
|
|
@@ -4449,7 +4444,7 @@ class TunnelAgent {
|
|
|
4449
4444
|
} catch {}
|
|
4450
4445
|
const deadline = Date.now() + hold;
|
|
4451
4446
|
while (Date.now() < deadline) {
|
|
4452
|
-
await new Promise((
|
|
4447
|
+
await new Promise((resolve2) => setTimeout(resolve2, Math.min(100, hold / 4)));
|
|
4453
4448
|
const decision = decideAccess(readAccess(this.home));
|
|
4454
4449
|
if (decision === "ask")
|
|
4455
4450
|
continue;
|
|
@@ -4747,7 +4742,7 @@ function hostBundleId() {
|
|
|
4747
4742
|
}
|
|
4748
4743
|
}
|
|
4749
4744
|
function execFile(cmd, args, timeoutMs = 30000, env = driverEnvironment()) {
|
|
4750
|
-
return new Promise((
|
|
4745
|
+
return new Promise((resolve2, reject) => {
|
|
4751
4746
|
const proc = spawn4(cmd, args, {
|
|
4752
4747
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4753
4748
|
env
|
|
@@ -4792,7 +4787,7 @@ function execFile(cmd, args, timeoutMs = 30000, env = driverEnvironment()) {
|
|
|
4792
4787
|
const detail = stderr.trim() || stdout.trim();
|
|
4793
4788
|
reject(new Error(`${cmd} failed (${code})${detail ? `: ${detail}` : ""}`));
|
|
4794
4789
|
} else {
|
|
4795
|
-
|
|
4790
|
+
resolve2({ stdout, stderr });
|
|
4796
4791
|
}
|
|
4797
4792
|
});
|
|
4798
4793
|
proc.on("error", (err) => {
|
|
@@ -4814,7 +4809,7 @@ function isDaemonProxyFallback(message) {
|
|
|
4814
4809
|
return message.includes("daemon proxy") && message.includes("Resource temporarily unavailable");
|
|
4815
4810
|
}
|
|
4816
4811
|
async function sleep2(ms) {
|
|
4817
|
-
await new Promise((
|
|
4812
|
+
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
4818
4813
|
}
|
|
4819
4814
|
function sanitizeArgs(args) {
|
|
4820
4815
|
const sanitized = {};
|
|
@@ -5395,7 +5390,8 @@ function validateCommand(command, allowedCommands, blockedCommands) {
|
|
|
5395
5390
|
throw new Error(`Command contains disallowed characters: "${trimmed}"`);
|
|
5396
5391
|
}
|
|
5397
5392
|
const executable = trimmed;
|
|
5398
|
-
|
|
5393
|
+
const name = executable.slice(executable.lastIndexOf("/") + 1);
|
|
5394
|
+
if (blockedCommands.length > 0 && (blockedCommands.includes(executable) || blockedCommands.includes(name))) {
|
|
5399
5395
|
throw new Error(`Command "${executable}" is blocked`);
|
|
5400
5396
|
}
|
|
5401
5397
|
if (allowedCommands.length > 0) {
|
|
@@ -5446,7 +5442,7 @@ function createShellCapability(config) {
|
|
|
5446
5442
|
safeEnv[key] = process.env[key];
|
|
5447
5443
|
}
|
|
5448
5444
|
}
|
|
5449
|
-
return new Promise((
|
|
5445
|
+
return new Promise((resolve3, reject) => {
|
|
5450
5446
|
const proc = spawn5(executable, args, {
|
|
5451
5447
|
cwd,
|
|
5452
5448
|
shell: false,
|
|
@@ -5489,7 +5485,7 @@ function createShellCapability(config) {
|
|
|
5489
5485
|
reject(new Error(`Command failed to start: ${err.message}`));
|
|
5490
5486
|
});
|
|
5491
5487
|
proc.on("close", (code, signal) => {
|
|
5492
|
-
|
|
5488
|
+
resolve3({
|
|
5493
5489
|
exitCode: code,
|
|
5494
5490
|
signal,
|
|
5495
5491
|
stdout,
|
|
@@ -5578,12 +5574,12 @@ function clearSavedCredentials() {
|
|
|
5578
5574
|
// src/agent/credential-probe.ts
|
|
5579
5575
|
var DEFAULT_PROBE_TIMEOUT_MS = 1e4;
|
|
5580
5576
|
function probeCredentials(config, options = {}) {
|
|
5581
|
-
return new Promise((
|
|
5577
|
+
return new Promise((resolve3) => {
|
|
5582
5578
|
let socket;
|
|
5583
5579
|
try {
|
|
5584
5580
|
socket = new WebSocket(new URL(buildTunnelWsUrl(config)));
|
|
5585
5581
|
} catch {
|
|
5586
|
-
|
|
5582
|
+
resolve3("unreachable");
|
|
5587
5583
|
return;
|
|
5588
5584
|
}
|
|
5589
5585
|
const settle = (result) => {
|
|
@@ -5591,7 +5587,7 @@ function probeCredentials(config, options = {}) {
|
|
|
5591
5587
|
try {
|
|
5592
5588
|
socket.close(1000, "probe complete");
|
|
5593
5589
|
} catch {}
|
|
5594
|
-
|
|
5590
|
+
resolve3(result);
|
|
5595
5591
|
};
|
|
5596
5592
|
const timer = setTimeout(() => settle("unreachable"), options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS);
|
|
5597
5593
|
socket.addEventListener("open", () => {
|
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { validateCommand } from './command-validator';
|
|
3
|
+
|
|
4
|
+
describe('validateCommand blocked commands', () => {
|
|
5
|
+
test('a blocked name blocks its absolute and relative path spellings', () => {
|
|
6
|
+
for (const spelling of ['rm', '/bin/rm', '/usr/bin/rm', './rm']) {
|
|
7
|
+
expect(() => validateCommand(spelling, [], ['rm'])).toThrow(/is blocked/);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
test('an unrelated command with the blocked name inside a directory still runs', () => {
|
|
11
|
+
expect(validateCommand('/opt/rm-tools/ls', [], ['rm'])).toBe('/opt/rm-tools/ls');
|
|
12
|
+
});
|
|
13
|
+
test('an allowlist stays an exact match', () => {
|
|
14
|
+
expect(() => validateCommand('/usr/bin/git', ['git'], [])).toThrow(/not in the allowed/);
|
|
15
|
+
expect(validateCommand('git', ['git'], [])).toBe('git');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -17,7 +17,11 @@ export function validateCommand(
|
|
|
17
17
|
|
|
18
18
|
const executable = trimmed;
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
// A blocked name also blocks every path that ends in it: `/bin/rm` is `rm`.
|
|
21
|
+
// Arguments stay unrestricted: an allowed `git` or `python` runs code through
|
|
22
|
+
// its own flags, so a `shell` grant is local code execution, not a sandbox.
|
|
23
|
+
const name = executable.slice(executable.lastIndexOf('/') + 1);
|
|
24
|
+
if (blockedCommands.length > 0 && (blockedCommands.includes(executable) || blockedCommands.includes(name))) {
|
|
21
25
|
throw new Error(`Command "${executable}" is blocked`);
|
|
22
26
|
}
|
|
23
27
|
|