fix(contracts): add isWorkspaceEvent guard + close routeLiveInteractionEvent exhaustiveness gap (#2878)
Fixes two contract violations found in audit (closes #2875): 1. `isWorkspaceEvent()` type guard added next to WorkspaceEvent type definition. Applied at stream.onmessage JSON.parse boundary — replaces unsafe `as WorkspaceEvent` cast with validated parse + explicit error path for malformed payloads. 2. `routeLiveInteractionEvent()` switch extended with explicit cases for all three previously unhandled WorkspaceEvent variants: - bridge_status: handled upstream with early return, never reaches router - live_state_invalidation: handled upstream via handleLiveStateInvalidation - extension_error: terminal line produced by summarizeEvent, no live state update needed
This commit is contained in:
parent
666731f56d
commit
2175f59522
1 changed files with 22 additions and 2 deletions
|
|
@ -470,6 +470,10 @@ export type WorkspaceEvent =
|
|||
| TurnEndEvent
|
||||
| ({ type: Exclude<string, "bridge_status" | "live_state_invalidation" | "extension_ui_request" | "extension_error" | "message_update" | "tool_execution_start" | "tool_execution_end" | "agent_end" | "turn_end">; [key: string]: unknown } & Record<string, unknown>)
|
||||
|
||||
export function isWorkspaceEvent(value: unknown): value is WorkspaceEvent {
|
||||
return value !== null && typeof value === "object" && typeof (value as Record<string, unknown>).type === "string"
|
||||
}
|
||||
|
||||
export interface WorkspaceCommandResponse {
|
||||
type: "response"
|
||||
command: string
|
||||
|
|
@ -4866,8 +4870,15 @@ export class GSDWorkspaceStore {
|
|||
|
||||
stream.onmessage = (message) => {
|
||||
try {
|
||||
const payload = JSON.parse(message.data) as WorkspaceEvent
|
||||
this.handleEvent(payload)
|
||||
const parsed: unknown = JSON.parse(message.data)
|
||||
if (!isWorkspaceEvent(parsed)) {
|
||||
this.patchState({
|
||||
lastClientError: "Malformed event received from stream",
|
||||
terminalLines: withTerminalLine(this.state.terminalLines, createTerminalLine("error", "Malformed event received from stream")),
|
||||
})
|
||||
return
|
||||
}
|
||||
this.handleEvent(parsed)
|
||||
} catch (error) {
|
||||
const text = normalizeClientError(error)
|
||||
this.patchState({
|
||||
|
|
@ -4945,6 +4956,15 @@ export class GSDWorkspaceStore {
|
|||
case "tool_execution_end":
|
||||
this.handleToolExecutionEnd(event as ToolExecutionEndEvent)
|
||||
break
|
||||
case "bridge_status":
|
||||
// Handled upstream in handleEvent with early return — never reaches here
|
||||
break
|
||||
case "live_state_invalidation":
|
||||
// Handled upstream in handleEvent via handleLiveStateInvalidation — no live interaction state update needed
|
||||
break
|
||||
case "extension_error":
|
||||
// Terminal line produced by summarizeEvent — no live interaction state update needed
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue