fix: remove secure-context websocket id dependency
This commit is contained in:
@@ -52,8 +52,10 @@ backend への proxy はこの現象の直接原因ではない。
|
|||||||
|
|
||||||
## 改善案
|
## 改善案
|
||||||
|
|
||||||
- UI 全体で使う ID 生成 helper を用意し、secure context に依存しない実装にする。
|
- `WorkspaceMultiplexer` の client/request correlation ID は、module-local の単調増加
|
||||||
`crypto.getRandomValues()` から UUID v4 相当を生成する方法で十分である。
|
sequence から生成する。これらは1ページ・1接続内で重複しなければよく、暗号学的乱数や
|
||||||
|
永続的なglobal uniquenessは不要なので、`crypto`、`Math.random()`、時刻依存のfallbackを
|
||||||
|
使わない。
|
||||||
- `WorkspaceMultiplexer.subscribe()` の同期初期化失敗を Console の diagnostic/state に
|
- `WorkspaceMultiplexer.subscribe()` の同期初期化失敗を Console の diagnostic/state に
|
||||||
反映し、初期値の `connecting` に留まらないようにする。
|
反映し、初期値の `connecting` に留まらないようにする。
|
||||||
- LAN 上の平文 HTTP を開発時の対応経路とするなら、insecure context から subscription
|
- LAN 上の平文 HTTP を開発時の対応経路とするなら、insecure context から subscription
|
||||||
@@ -67,3 +69,9 @@ backend への proxy はこの現象の直接原因ではない。
|
|||||||
`--host` は Vite の listener を LAN に bind するだけであり、配信 origin を secure
|
`--host` は Vite の listener を LAN に bind するだけであり、配信 origin を secure
|
||||||
context に変えるものではない。localhost で正常に動くことだけでは、LAN IP の HTTP
|
context に変えるものではない。localhost で正常に動くことだけでは、LAN IP の HTTP
|
||||||
アクセスでも同じブラウザ API が利用できることを証明できない。
|
アクセスでも同じブラウザ API が利用できることを証明できない。
|
||||||
|
|
||||||
|
## Resolution (2026-08-06)
|
||||||
|
|
||||||
|
`WorkspaceMultiplexer` の client/request ID をmodule-localの単調増加sequenceへ変更した。
|
||||||
|
これによりWebSocket接続開始経路からsecure-context限定の `crypto.randomUUID()` 依存を除去した。
|
||||||
|
IDはlocal correlationにのみ使われ、認証・認可・capability authorityには使われない。
|
||||||
|
|||||||
@@ -735,6 +735,11 @@ Deno.test("Workspace Worker list and Console share the multiplexed connection",
|
|||||||
multiplexer.includes("frame: 'worker_protocol'"),
|
multiplexer.includes("frame: 'worker_protocol'"),
|
||||||
"Sidebar and Console should share one Workspace multiplexer and route Worker methods through a subscription lane",
|
"Sidebar and Console should share one Workspace multiplexer and route Worker methods through a subscription lane",
|
||||||
);
|
);
|
||||||
|
assert(
|
||||||
|
multiplexer.includes('nextMultiplexerId') &&
|
||||||
|
!multiplexer.includes('crypto.randomUUID'),
|
||||||
|
"Workspace subscription correlation IDs should not require secure-context crypto APIs",
|
||||||
|
);
|
||||||
assert(
|
assert(
|
||||||
multiplexer.includes("this.#socket?.readyState === WebSocket.OPEN") &&
|
multiplexer.includes("this.#socket?.readyState === WebSocket.OPEN") &&
|
||||||
multiplexer.includes("this.#sendSubscribe(subscription)") &&
|
multiplexer.includes("this.#sendSubscribe(subscription)") &&
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ export type WorkspaceMultiplexerSubscription = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const multiplexers = new Map<string, WorkspaceMultiplexer>();
|
const multiplexers = new Map<string, WorkspaceMultiplexer>();
|
||||||
|
let nextMultiplexerSequence = 0;
|
||||||
|
|
||||||
|
function nextMultiplexerId(kind: 'client' | 'request'): string {
|
||||||
|
nextMultiplexerSequence += 1;
|
||||||
|
return `${kind}-${nextMultiplexerSequence}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function workspaceMultiplexer(workspaceId: string): WorkspaceMultiplexer {
|
export function workspaceMultiplexer(workspaceId: string): WorkspaceMultiplexer {
|
||||||
let multiplexer = multiplexers.get(workspaceId);
|
let multiplexer = multiplexers.get(workspaceId);
|
||||||
@@ -53,7 +59,7 @@ export class WorkspaceMultiplexer {
|
|||||||
selector: EventSubscriptionSelector,
|
selector: EventSubscriptionSelector,
|
||||||
listener: Listener,
|
listener: Listener,
|
||||||
): WorkspaceMultiplexerSubscription {
|
): WorkspaceMultiplexerSubscription {
|
||||||
const clientId = crypto.randomUUID();
|
const clientId = nextMultiplexerId('client');
|
||||||
const subscription: ActiveSubscription = {
|
const subscription: ActiveSubscription = {
|
||||||
clientId,
|
clientId,
|
||||||
selector,
|
selector,
|
||||||
@@ -112,7 +118,7 @@ export class WorkspaceMultiplexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#sendSubscribe(subscription: ActiveSubscription): void {
|
#sendSubscribe(subscription: ActiveSubscription): void {
|
||||||
const requestId = crypto.randomUUID();
|
const requestId = nextMultiplexerId('request');
|
||||||
subscription.requestId = requestId;
|
subscription.requestId = requestId;
|
||||||
this.#requests.set(requestId, subscription.clientId);
|
this.#requests.set(requestId, subscription.clientId);
|
||||||
this.#send({
|
this.#send({
|
||||||
@@ -198,7 +204,7 @@ export class WorkspaceMultiplexer {
|
|||||||
message: {
|
message: {
|
||||||
method: 'unsubscribe_events',
|
method: 'unsubscribe_events',
|
||||||
params: {
|
params: {
|
||||||
request_id: crypto.randomUUID(),
|
request_id: nextMultiplexerId('request'),
|
||||||
subscription_id: subscription.subscriptionId,
|
subscription_id: subscription.subscriptionId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user