webclaw-mcp 0.5.0 → 0.6.1
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/dist/__tests__/mcp-protocol-integration.test.js +1 -1
- package/dist/__tests__/mcp-stdio-integration.test.js +1 -1
- package/dist/__tests__/new-tools-e2e.test.js +1 -1
- package/dist/__tests__/port-scanning.test.d.ts +2 -0
- package/dist/__tests__/port-scanning.test.d.ts.map +1 -0
- package/dist/__tests__/port-scanning.test.js +35 -0
- package/dist/__tests__/port-scanning.test.js.map +1 -0
- package/dist/__tests__/ws-origin-validation.test.d.ts +2 -0
- package/dist/__tests__/ws-origin-validation.test.d.ts.map +1 -0
- package/dist/__tests__/ws-origin-validation.test.js +113 -0
- package/dist/__tests__/ws-origin-validation.test.js.map +1 -0
- package/dist/cli.js +46 -16
- package/dist/cli.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/ws-client.d.ts +11 -0
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +37 -1
- package/dist/ws-client.js.map +1 -1
- package/package.json +1 -1
|
@@ -75,7 +75,7 @@ describe('MCP Protocol integration (in-process)', () => {
|
|
|
75
75
|
const serverVersion = mcpClient.getServerVersion();
|
|
76
76
|
expect(serverVersion).toBeDefined();
|
|
77
77
|
expect(serverVersion.name).toBe('webclaw');
|
|
78
|
-
expect(serverVersion.version).toBe('0.
|
|
78
|
+
expect(serverVersion.version).toBe('0.6.1');
|
|
79
79
|
});
|
|
80
80
|
// --- tools/list ---
|
|
81
81
|
it('lists all 18 tools', async () => {
|
|
@@ -93,7 +93,7 @@ describe('MCP Server stdio integration', () => {
|
|
|
93
93
|
const initResponse = collector.responses.find((r) => r.id === 1 && r.result);
|
|
94
94
|
expect(initResponse).toBeDefined();
|
|
95
95
|
expect(initResponse.result.serverInfo.name).toBe('webclaw');
|
|
96
|
-
expect(initResponse.result.serverInfo.version).toBe('0.
|
|
96
|
+
expect(initResponse.result.serverInfo.version).toBe('0.6.1');
|
|
97
97
|
}, 20000);
|
|
98
98
|
it('lists 18 tools via JSON-RPC after initialization', async () => {
|
|
99
99
|
child = await spawnAndWaitReady();
|
|
@@ -57,7 +57,7 @@ describe('v0.4.0 New Tools E2E (MCP Protocol)', () => {
|
|
|
57
57
|
// =========================================
|
|
58
58
|
it('server reports version 0.4.0', () => {
|
|
59
59
|
const info = mcpClient.getServerVersion();
|
|
60
|
-
expect(info.version).toBe('0.
|
|
60
|
+
expect(info.version).toBe('0.6.1');
|
|
61
61
|
});
|
|
62
62
|
it('lists exactly 18 tools', async () => {
|
|
63
63
|
const { tools } = await mcpClient.listTools();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-scanning.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/port-scanning.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { WebSocketClient } from '../ws-client.js';
|
|
3
|
+
const BASE_PORT = 19500; // Use a high port range to avoid conflicts with other tests
|
|
4
|
+
describe('Port scanning', () => {
|
|
5
|
+
const clients = [];
|
|
6
|
+
afterEach(async () => {
|
|
7
|
+
await Promise.all(clients.map((c) => c.close()));
|
|
8
|
+
clients.length = 0;
|
|
9
|
+
});
|
|
10
|
+
it('binds to the first available port', async () => {
|
|
11
|
+
const client = await WebSocketClient.create(BASE_PORT);
|
|
12
|
+
clients.push(client);
|
|
13
|
+
expect(client).toBeDefined();
|
|
14
|
+
expect(client.isConnected()).toBe(false); // No extension connected yet
|
|
15
|
+
});
|
|
16
|
+
it('skips a port already in use and binds to the next one', async () => {
|
|
17
|
+
// Occupy the first port
|
|
18
|
+
const first = await WebSocketClient.create(BASE_PORT + 10);
|
|
19
|
+
clients.push(first);
|
|
20
|
+
// Try to bind the same port — should fail with EADDRINUSE
|
|
21
|
+
await expect(WebSocketClient.create(BASE_PORT + 10)).rejects.toThrow();
|
|
22
|
+
// Bind the next port — should succeed
|
|
23
|
+
const second = await WebSocketClient.create(BASE_PORT + 11);
|
|
24
|
+
clients.push(second);
|
|
25
|
+
expect(second).toBeDefined();
|
|
26
|
+
});
|
|
27
|
+
it('binds multiple servers to sequential ports', async () => {
|
|
28
|
+
for (let i = 0; i < 3; i++) {
|
|
29
|
+
const client = await WebSocketClient.create(BASE_PORT + 20 + i);
|
|
30
|
+
clients.push(client);
|
|
31
|
+
}
|
|
32
|
+
expect(clients).toHaveLength(3);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=port-scanning.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-scanning.test.js","sourceRoot":"","sources":["../../src/__tests__/port-scanning.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,4DAA4D;AAErF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,MAAM,OAAO,GAAsB,EAAE,CAAC;IAEtC,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,6BAA6B;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,wBAAwB;QACxB,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEpB,0DAA0D;QAC1D,MAAM,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAEvE,sCAAsC;QACtC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-origin-validation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ws-origin-validation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for WebSocket Origin / Host header validation.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
5
|
+
import { WebSocket } from 'ws';
|
|
6
|
+
import { WebSocketClient } from '../ws-client.js';
|
|
7
|
+
// Mock chrome-launcher to prevent actual Chrome launch
|
|
8
|
+
vi.mock('../chrome-launcher.js', () => ({
|
|
9
|
+
launchChrome: vi.fn(async () => false),
|
|
10
|
+
}));
|
|
11
|
+
let wsClient;
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
if (wsClient) {
|
|
14
|
+
await wsClient.close();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Attempt a WebSocket connection with custom headers.
|
|
19
|
+
* Returns 'open' on success or the HTTP status code on rejection.
|
|
20
|
+
*/
|
|
21
|
+
function connectWithHeaders(port, headers) {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const ws = new WebSocket(`ws://127.0.0.1:${port}`, { headers });
|
|
24
|
+
ws.on('open', () => {
|
|
25
|
+
ws.close();
|
|
26
|
+
resolve('open');
|
|
27
|
+
});
|
|
28
|
+
ws.on('unexpected-response', (_req, res) => {
|
|
29
|
+
resolve(res.statusCode ?? 0);
|
|
30
|
+
});
|
|
31
|
+
ws.on('error', () => {
|
|
32
|
+
// unexpected-response fires first for 403; this is a fallback
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
describe('WebSocket Origin / Host validation', () => {
|
|
37
|
+
async function setup() {
|
|
38
|
+
wsClient = await WebSocketClient.create(0);
|
|
39
|
+
const addr = wsClient.wss.address();
|
|
40
|
+
return addr.port;
|
|
41
|
+
}
|
|
42
|
+
// --- Origin tests ---
|
|
43
|
+
it('allows connections with no Origin header (Node.js clients)', async () => {
|
|
44
|
+
const port = await setup();
|
|
45
|
+
const result = await connectWithHeaders(port, {});
|
|
46
|
+
expect(result).toBe('open');
|
|
47
|
+
});
|
|
48
|
+
it('allows chrome-extension:// origin', async () => {
|
|
49
|
+
const port = await setup();
|
|
50
|
+
const result = await connectWithHeaders(port, {
|
|
51
|
+
Origin: 'chrome-extension://abcdefghijklmnop',
|
|
52
|
+
});
|
|
53
|
+
expect(result).toBe('open');
|
|
54
|
+
});
|
|
55
|
+
it('allows moz-extension:// origin', async () => {
|
|
56
|
+
const port = await setup();
|
|
57
|
+
const result = await connectWithHeaders(port, {
|
|
58
|
+
Origin: 'moz-extension://abcdefghijklmnop',
|
|
59
|
+
});
|
|
60
|
+
expect(result).toBe('open');
|
|
61
|
+
});
|
|
62
|
+
it('allows safari-web-extension:// origin', async () => {
|
|
63
|
+
const port = await setup();
|
|
64
|
+
const result = await connectWithHeaders(port, {
|
|
65
|
+
Origin: 'safari-web-extension://abcdefghijklmnop',
|
|
66
|
+
});
|
|
67
|
+
expect(result).toBe('open');
|
|
68
|
+
});
|
|
69
|
+
it('rejects https://evil.com origin with 403', async () => {
|
|
70
|
+
const port = await setup();
|
|
71
|
+
const result = await connectWithHeaders(port, {
|
|
72
|
+
Origin: 'https://evil.com',
|
|
73
|
+
});
|
|
74
|
+
expect(result).toBe(403);
|
|
75
|
+
});
|
|
76
|
+
it('rejects http://localhost:3000 origin with 403', async () => {
|
|
77
|
+
const port = await setup();
|
|
78
|
+
const result = await connectWithHeaders(port, {
|
|
79
|
+
Origin: 'http://localhost:3000',
|
|
80
|
+
});
|
|
81
|
+
expect(result).toBe(403);
|
|
82
|
+
});
|
|
83
|
+
// --- Host tests ---
|
|
84
|
+
it('allows Host: 127.0.0.1:<port>', async () => {
|
|
85
|
+
const port = await setup();
|
|
86
|
+
const result = await connectWithHeaders(port, {
|
|
87
|
+
Host: `127.0.0.1:${port}`,
|
|
88
|
+
});
|
|
89
|
+
expect(result).toBe('open');
|
|
90
|
+
});
|
|
91
|
+
it('allows Host: localhost:<port>', async () => {
|
|
92
|
+
const port = await setup();
|
|
93
|
+
const result = await connectWithHeaders(port, {
|
|
94
|
+
Host: `localhost:${port}`,
|
|
95
|
+
});
|
|
96
|
+
expect(result).toBe('open');
|
|
97
|
+
});
|
|
98
|
+
it('allows Host: [::1]:<port>', async () => {
|
|
99
|
+
const port = await setup();
|
|
100
|
+
const result = await connectWithHeaders(port, {
|
|
101
|
+
Host: `[::1]:${port}`,
|
|
102
|
+
});
|
|
103
|
+
expect(result).toBe('open');
|
|
104
|
+
});
|
|
105
|
+
it('rejects Host: evil.com:<port> with 403', async () => {
|
|
106
|
+
const port = await setup();
|
|
107
|
+
const result = await connectWithHeaders(port, {
|
|
108
|
+
Host: `evil.com:${port}`,
|
|
109
|
+
});
|
|
110
|
+
expect(result).toBe(403);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=ws-origin-validation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-origin-validation.test.js","sourceRoot":"","sources":["../../src/__tests__/ws-origin-validation.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import { createWebClawServer } from './server.js';
|
|
|
11
11
|
import { WebSocketClient } from './ws-client.js';
|
|
12
12
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
13
|
import { install } from './installer.js';
|
|
14
|
-
import { WEBSOCKET_DEFAULT_PORT, WEBSOCKET_PORT_ENV } from 'webclaw-shared';
|
|
14
|
+
import { WEBSOCKET_DEFAULT_PORT, WEBSOCKET_PORT_ENV, WEBSOCKET_PORT_RANGE_SIZE } from 'webclaw-shared';
|
|
15
15
|
const args = process.argv.slice(2);
|
|
16
16
|
if (args.includes('--help') || args.includes('-h')) {
|
|
17
17
|
console.log(`webclaw-mcp - WebMCP-native browser agent
|
|
@@ -43,26 +43,56 @@ else if (args[0] === 'install') {
|
|
|
43
43
|
await install();
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
|
-
const
|
|
46
|
+
const explicitPort = process.env[WEBSOCKET_PORT_ENV] ? Number(process.env[WEBSOCKET_PORT_ENV]) : null;
|
|
47
47
|
let wsClient;
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
if (explicitPort !== null) {
|
|
49
|
+
// Explicit port: use only that port (backward compatible)
|
|
50
|
+
try {
|
|
51
|
+
wsClient = await WebSocketClient.create(explicitPort);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const error = err;
|
|
55
|
+
if (error.code === 'EADDRINUSE') {
|
|
56
|
+
console.error(`[WebClaw] Port ${explicitPort} is already in use.\n` +
|
|
57
|
+
` Another WebClaw instance may be running. To fix:\n` +
|
|
58
|
+
` lsof -ti:${explicitPort} | xargs kill\n` +
|
|
59
|
+
` Or use a different port:\n` +
|
|
60
|
+
` ${WEBSOCKET_PORT_ENV}=${explicitPort + 1} npx webclaw-mcp`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.error(`[WebClaw] WebSocket server error: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
console.error(`[WebClaw] WebSocket server listening on 127.0.0.1:${explicitPort}`);
|
|
50
68
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
else {
|
|
70
|
+
// Auto-scan port range
|
|
71
|
+
let boundPort = null;
|
|
72
|
+
wsClient = null;
|
|
73
|
+
for (let i = 0; i < WEBSOCKET_PORT_RANGE_SIZE; i++) {
|
|
74
|
+
const port = WEBSOCKET_DEFAULT_PORT + i;
|
|
75
|
+
try {
|
|
76
|
+
wsClient = await WebSocketClient.create(port);
|
|
77
|
+
boundPort = port;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const error = err;
|
|
82
|
+
if (error.code === 'EADDRINUSE') {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
console.error(`[WebClaw] WebSocket server error: ${error.message}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
59
88
|
}
|
|
60
|
-
|
|
61
|
-
console.error(`[WebClaw]
|
|
89
|
+
if (boundPort === null) {
|
|
90
|
+
console.error(`[WebClaw] All ports in range ${WEBSOCKET_DEFAULT_PORT}–${WEBSOCKET_DEFAULT_PORT + WEBSOCKET_PORT_RANGE_SIZE - 1} are in use.\n` +
|
|
91
|
+
` ${WEBSOCKET_PORT_RANGE_SIZE} WebClaw instances may already be running.`);
|
|
92
|
+
process.exit(1);
|
|
62
93
|
}
|
|
63
|
-
|
|
94
|
+
console.error(`[WebClaw] WebSocket server listening on 127.0.0.1:${boundPort}`);
|
|
64
95
|
}
|
|
65
|
-
console.error(`[WebClaw] WebSocket server listening on 127.0.0.1:${port}`);
|
|
66
96
|
const cleanup = async () => {
|
|
67
97
|
console.error('[WebClaw] Shutting down...');
|
|
68
98
|
await wsClient.close();
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -21,7 +21,7 @@ function formatErrorResponse(payload) {
|
|
|
21
21
|
export function createWebClawServer(options) {
|
|
22
22
|
const server = new McpServer({
|
|
23
23
|
name: 'webclaw',
|
|
24
|
-
version: '0.
|
|
24
|
+
version: '0.6.1',
|
|
25
25
|
});
|
|
26
26
|
const wsClient = options.wsClient;
|
|
27
27
|
// --- Tool: navigate_to ---
|
package/dist/ws-client.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'http';
|
|
1
2
|
import type { BridgeMessage, BridgeMethod } from 'webclaw-shared';
|
|
3
|
+
/**
|
|
4
|
+
* Create a verifyClient callback for WebSocketServer.
|
|
5
|
+
* Rejects browser connections from web origins (http/https) and
|
|
6
|
+
* connections with unexpected Host headers (DNS rebinding protection).
|
|
7
|
+
*/
|
|
8
|
+
export declare function createVerifyClient(port: number): (info: {
|
|
9
|
+
origin: string;
|
|
10
|
+
secure: boolean;
|
|
11
|
+
req: IncomingMessage;
|
|
12
|
+
}, callback: (result: boolean, code?: number, message?: string) => void) => void;
|
|
2
13
|
export declare class WebSocketClient {
|
|
3
14
|
private wss;
|
|
4
15
|
private connection;
|
package/dist/ws-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA0ClE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,IAE3C,MAAM;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,eAAe,CAAA;CAAE,EAC/D,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,KACnE,IAAI,CA4BR;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,eAAe,CAAqC;IAC5D,OAAO,CAAC,iBAAiB,CAA8B;IACvD,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO;IA4CP,wEAAwE;WAC3D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAS/E,OAAO,CAAC,aAAa;IAmBrB;;;;;;;;;OASG;IACG,eAAe,CAAC,SAAS,SAA8B,GAAG,OAAO,CAAC,IAAI,CAAC;YAc/D,kBAAkB;IA+ChC,yEAAyE;IACnE,OAAO,CACX,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,OAAY,EACrB,SAAS,SAAS,GACjB,OAAO,CAAC,aAAa,CAAC;IAuBzB;;;;;;;OAOG;IACG,gBAAgB,CACpB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,OAAY,GACpB,OAAO,CAAC,aAAa,CAAC;IA0BzB,WAAW,IAAI,OAAO;IAIhB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB7B"}
|
package/dist/ws-client.js
CHANGED
|
@@ -26,6 +26,42 @@ function isTransientError(err) {
|
|
|
26
26
|
const msg = err.message.toLowerCase();
|
|
27
27
|
return TRANSIENT_ERROR_PATTERNS.some((pattern) => msg.includes(pattern.toLowerCase()));
|
|
28
28
|
}
|
|
29
|
+
const ALLOWED_ORIGIN_SCHEMES = [
|
|
30
|
+
'chrome-extension://',
|
|
31
|
+
'moz-extension://',
|
|
32
|
+
'safari-web-extension://',
|
|
33
|
+
];
|
|
34
|
+
const ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '[::1]'];
|
|
35
|
+
/**
|
|
36
|
+
* Create a verifyClient callback for WebSocketServer.
|
|
37
|
+
* Rejects browser connections from web origins (http/https) and
|
|
38
|
+
* connections with unexpected Host headers (DNS rebinding protection).
|
|
39
|
+
*/
|
|
40
|
+
export function createVerifyClient(port) {
|
|
41
|
+
return (info, callback) => {
|
|
42
|
+
const origin = info.origin ?? info.req.headers['origin'];
|
|
43
|
+
const host = info.req.headers['host'] ?? '';
|
|
44
|
+
// --- Origin validation ---
|
|
45
|
+
// No origin (Node.js clients, tests) → allow
|
|
46
|
+
if (origin !== undefined && origin !== '') {
|
|
47
|
+
const isAllowedOrigin = ALLOWED_ORIGIN_SCHEMES.some((scheme) => origin.startsWith(scheme));
|
|
48
|
+
if (!isAllowedOrigin) {
|
|
49
|
+
callback(false, 403, 'Forbidden: disallowed origin');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// --- Host validation (DNS rebinding protection) ---
|
|
54
|
+
if (host !== '') {
|
|
55
|
+
// Strip port suffix to get bare hostname
|
|
56
|
+
const hostname = host.replace(/:\d+$/, '');
|
|
57
|
+
if (!ALLOWED_HOSTS.includes(hostname)) {
|
|
58
|
+
callback(false, 403, 'Forbidden: disallowed host');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
callback(true);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
29
65
|
export class WebSocketClient {
|
|
30
66
|
wss;
|
|
31
67
|
connection = null;
|
|
@@ -72,7 +108,7 @@ export class WebSocketClient {
|
|
|
72
108
|
}
|
|
73
109
|
/** Create a WebSocketClient and wait for the server to be listening. */
|
|
74
110
|
static async create(port, host = '127.0.0.1') {
|
|
75
|
-
const wss = new WebSocketServer({ port, host });
|
|
111
|
+
const wss = new WebSocketServer({ port, host, verifyClient: createVerifyClient(port) });
|
|
76
112
|
await new Promise((resolve, reject) => {
|
|
77
113
|
wss.once('listening', resolve);
|
|
78
114
|
wss.once('error', reject);
|
package/dist/ws-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"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"}
|