fix: support dev server previews over relay
This commit is contained in:
@@ -41,7 +41,7 @@ Relay is not a separate link format: it is one transport candidate inside the un
|
||||
Everything a client normally sends to the single OpenChamber origin:
|
||||
- **HTTP** — REST endpoints and proxied OpenCode SDK calls under `/api/*`, plus `/auth/*` and `/health`.
|
||||
- **SSE** — long-lived streamed responses (the event stream and notifications). These are just HTTP responses whose body streams; the tunnel needs no special SSE handling.
|
||||
- **WebSocket** — the endpoints that use a real socket (the global event stream on platforms that support WS, terminal I/O, dictation).
|
||||
- **WebSocket** — the endpoints that use a real socket (the global event stream on platforms that support WS, terminal I/O, dictation, and desktop dev-server previews).
|
||||
|
||||
The host dispatcher restricts tunneled traffic to explicit path allowlists (one for HTTP, one for WS).
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ const ALLOWED_WS_PATHS = new Set([
|
||||
'/api/event/ws',
|
||||
'/api/terminal/ws',
|
||||
'/api/dictation/ws',
|
||||
'/api/dev-tunnel',
|
||||
]);
|
||||
export const isAllowedRelayWebSocketPath = (pathname) => ALLOWED_WS_PATHS.has(pathname);
|
||||
|
||||
// Hop-by-hop headers stripped from tunneled requests; `host` is set by fetch
|
||||
// to the loopback origin. content-length is dropped too because the body is
|
||||
@@ -425,7 +427,7 @@ export const createTunnelHost = ({ connectionId, getLocalPort, sendFrame, getBuf
|
||||
void sendAbort(streamId, error?.message ?? 'malformed ws open');
|
||||
return;
|
||||
}
|
||||
if (!ALLOWED_WS_PATHS.has(open.path)) {
|
||||
if (!isAllowedRelayWebSocketPath(open.path)) {
|
||||
void sendAbort(streamId, 'Path is not allowed through the relay');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import http from 'node:http';
|
||||
|
||||
import { createTunnelHost } from './tunnel-host.js';
|
||||
import { createTunnelHost, isAllowedRelayWebSocketPath } from './tunnel-host.js';
|
||||
import { decodeTunnelFrame, encodeTunnelFrame, encodeJsonPayload, TunnelFrameType } from './tunnel-codec.js';
|
||||
|
||||
const startLoopback = () =>
|
||||
@@ -145,3 +145,11 @@ describe('tunnel-host HTTP body forwarding', () => {
|
||||
await loopback.stop();
|
||||
});
|
||||
});
|
||||
|
||||
describe('relay host WebSocket allowlist', () => {
|
||||
test('allows only the exact dev-server tunnel path', () => {
|
||||
expect(isAllowedRelayWebSocketPath('/api/dev-tunnel')).toBe(true);
|
||||
expect(isAllowedRelayWebSocketPath('/api/dev-tunnel/')).toBe(false);
|
||||
expect(isAllowedRelayWebSocketPath('/api/database/ws')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user