4a665da79e
- /ws-pty carries the dashboard token like /ws (the terminal never attached with DASHBOARD_TOKEN set), and so does everything that cannot send the header: images, downloads, exports, drops (withToken/tokenHeader). A refused upgrade answers 401/403 and is logged and counted by reason. - The browser asks for a missing or wrong token (401 EUNAUTHORIZED) and takes ?token= from the link agenthub start prints, dropping it from the URL. - The CLI sends the token (DASHBOARD_TOKEN, else the one the running server publishes 0600 in <data dir>/runtime/dashboard-token); MCP and statusline read the same file. - DASHBOARD_HOST takes a list, and 127.0.0.1 is always listened on too, so a LAN-only bind keeps hooks and the CLI working. - agenthub start --host/--allowed-hosts/--token are saved in <data dir>/network.json for every later start; a wide bind gets a token; every reachable URL is printed. - tmux < 3.7 is an amber, once-per-version notice that says only the pane scrollbar is missing, with a ~/.local build instead of sudo make install. - INSTALL: PATH belongs in ~/.profile for sudo -iu/ssh; WSL mirrored notes.
83 lines
3.1 KiB
JavaScript
83 lines
3.1 KiB
JavaScript
/**
|
|
* @file websocket-pty-token.test.js
|
|
* @description With DASHBOARD_TOKEN set, a `/ws-pty/:runId` upgrade is let
|
|
* through only with the token (`?token=`, appended after the size query the
|
|
* terminal sends), and a refused one gets a real HTTP status and is counted
|
|
* by reason — it used to be a silent `socket.destroy()`, so a terminal stuck
|
|
* "reconnecting" behind a token gave no clue why. Real http.Server, raw
|
|
* upgrade requests; the run itself does not exist, so a let-through upgrade
|
|
* ends in the attach failing, never in a 401.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
const { describe, it, before, after } = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const http = require("node:http");
|
|
const net = require("node:net");
|
|
|
|
process.env.DASHBOARD_TOKEN = "s3cret-token";
|
|
const ws = require("../websocket");
|
|
|
|
function rawUpgrade(port, path) {
|
|
return new Promise((resolve) => {
|
|
const socket = net.connect(port, "127.0.0.1", () => {
|
|
socket.write(
|
|
`GET ${path} HTTP/1.1\r\nHost: 127.0.0.1:${port}\r\n` +
|
|
`Upgrade: websocket\r\nConnection: Upgrade\r\n` +
|
|
`Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n`
|
|
);
|
|
});
|
|
let body = "";
|
|
socket.on("data", (d) => {
|
|
body += d.toString("utf8");
|
|
// A 101 means the upgrade was accepted: that is all this test needs.
|
|
if (body.startsWith("HTTP/1.1 101")) socket.destroy();
|
|
});
|
|
socket.on("close", () => resolve(body));
|
|
socket.on("error", () => resolve(body));
|
|
});
|
|
}
|
|
|
|
describe("/ws-pty behind DASHBOARD_TOKEN", () => {
|
|
let server;
|
|
let port;
|
|
before(async () => {
|
|
server = http.createServer((req, res) => res.end("ok"));
|
|
ws.initWebSocket(server);
|
|
ws.initPtyWebSocket(server);
|
|
await new Promise((r) => server.listen(0, "127.0.0.1", r));
|
|
port = server.address().port;
|
|
});
|
|
after(async () => {
|
|
ws.closeWebSocket();
|
|
await new Promise((r) => server.close(r));
|
|
delete process.env.DASHBOARD_TOKEN;
|
|
});
|
|
|
|
it("refuses an attach without the token with a 401, and counts why", async () => {
|
|
const before = ws.getUpgradeRefusals().token;
|
|
const reply = await rawUpgrade(port, "/ws-pty/agenthub-lane-1?cols=120&rows=30");
|
|
assert.match(reply, /^HTTP\/1\.1 401/);
|
|
assert.equal(ws.getUpgradeRefusals().token, before + 1);
|
|
});
|
|
|
|
it("refuses a wrong token", async () => {
|
|
const reply = await rawUpgrade(port, "/ws-pty/agenthub-lane-1?cols=120&rows=30&token=nope");
|
|
assert.match(reply, /^HTTP\/1\.1 401/);
|
|
});
|
|
|
|
it("lets the attach through with the token after the size query", async () => {
|
|
const before = ws.getUpgradeRefusals().token;
|
|
const reply = await rawUpgrade(
|
|
port,
|
|
"/ws-pty/agenthub-lane-1?cols=120&rows=30&token=s3cret-token"
|
|
);
|
|
assert.match(reply, /^HTTP\/1\.1 101/);
|
|
assert.equal(ws.getUpgradeRefusals().token, before);
|
|
});
|
|
|
|
it("lets a sizeless attach through with ?token=", async () => {
|
|
const reply = await rawUpgrade(port, "/ws-pty/agenthub-lane-1?token=s3cret-token");
|
|
assert.match(reply, /^HTTP\/1\.1 101/);
|
|
});
|
|
});
|