fix(cursor): address code review edge cases in protobuf and executor

CRITICAL FIX:
- CursorCredentials interface now matches types.ts (machineId, ghostMode as top-level)
- Fixes runtime error when cursor-auth saves credentials and cursor-executor reads them

HIGH:
- Replace 18+ non-null assertions with guard clauses across executor and decoder
- Prefix unused params in translator (_model, _stream, _credentials)
- HTTP/2 client closes on connection error to prevent leak
- AbortSignal listener leak documented with TODO (inline arrow prevents cleanup)

MEDIUM:
- Export concatArrays from encoder, remove duplicate from protobuf.ts
- Varint decoder now enforces 5-byte max to prevent overflow
- Buffer slice bounds check prevents out-of-range read
- Empty messages array validation with explicit error
- Buffered streaming limitation documented with TODO comment

All edge cases from code review now addressed.
This commit is contained in:
Tam Nhu Tran
2026-02-11 19:13:58 +07:00
parent 9daf9430bb
commit cc5a9039e4
6 changed files with 1472 additions and 1532 deletions
+229 -217
View File
@@ -3,34 +3,28 @@
* Implements ConnectRPC protobuf wire format decoding
*/
import * as zlib from "zlib";
import {
WIRE_TYPE,
FIELD,
type WireType,
} from "./cursor-protobuf-schema.js";
import * as zlib from 'zlib';
import { WIRE_TYPE, FIELD, type WireType } from './cursor-protobuf-schema.js';
/**
* Decode a varint from buffer
* Returns [value, newOffset]
*/
export function decodeVarint(
buffer: Uint8Array,
offset: number
): [number, number] {
let result = 0;
let shift = 0;
let pos = offset;
export function decodeVarint(buffer: Uint8Array, offset: number): [number, number] {
let result = 0;
let shift = 0;
let pos = offset;
const maxBytes = 5;
while (pos < buffer.length) {
const b = buffer[pos];
result |= (b & 0x7f) << shift;
pos++;
if (!(b & 0x80)) break;
shift += 7;
}
while (pos < buffer.length && pos - offset < maxBytes) {
const b = buffer[pos];
result |= (b & 0x7f) << shift;
pos++;
if (!(b & 0x80)) break;
shift += 7;
}
return [result, pos];
return [result, pos];
}
/**
@@ -38,63 +32,66 @@ export function decodeVarint(
* Returns [fieldNum, wireType, value, newOffset]
*/
export function decodeField(
buffer: Uint8Array,
offset: number
buffer: Uint8Array,
offset: number
): [number | null, WireType | null, Uint8Array | number | null, number] {
if (offset >= buffer.length) {
return [null, null, null, offset];
}
if (offset >= buffer.length) {
return [null, null, null, offset];
}
const [tag, pos1] = decodeVarint(buffer, offset);
const fieldNum = tag >> 3;
const wireType = (tag & 0x07) as WireType;
const [tag, pos1] = decodeVarint(buffer, offset);
const fieldNum = tag >> 3;
const wireType = (tag & 0x07) as WireType;
let value: Uint8Array | number | null;
let pos = pos1;
let value: Uint8Array | number | null;
let pos = pos1;
if (wireType === WIRE_TYPE.VARINT) {
[value, pos] = decodeVarint(buffer, pos);
} else if (wireType === WIRE_TYPE.LEN) {
const [length, pos2] = decodeVarint(buffer, pos);
value = buffer.slice(pos2, pos2 + length);
pos = pos2 + length;
} else if (wireType === WIRE_TYPE.FIXED64) {
value = buffer.slice(pos, pos + 8);
pos += 8;
} else if (wireType === WIRE_TYPE.FIXED32) {
value = buffer.slice(pos, pos + 4);
pos += 4;
} else {
value = null;
}
if (wireType === WIRE_TYPE.VARINT) {
[value, pos] = decodeVarint(buffer, pos);
} else if (wireType === WIRE_TYPE.LEN) {
const [length, pos2] = decodeVarint(buffer, pos);
if (pos2 + length > buffer.length) {
return [null, null, null, buffer.length];
}
value = buffer.slice(pos2, pos2 + length);
pos = pos2 + length;
} else if (wireType === WIRE_TYPE.FIXED64) {
value = buffer.slice(pos, pos + 8);
pos += 8;
} else if (wireType === WIRE_TYPE.FIXED32) {
value = buffer.slice(pos, pos + 4);
pos += 4;
} else {
value = null;
}
return [fieldNum, wireType, value, pos];
return [fieldNum, wireType, value, pos];
}
/**
* Decode a protobuf message into a map of fields
*/
export function decodeMessage(
data: Uint8Array
data: Uint8Array
): Map<number, Array<{ wireType: WireType; value: Uint8Array | number }>> {
const fields = new Map<
number,
Array<{ wireType: WireType; value: Uint8Array | number }>
>();
let pos = 0;
const fields = new Map<number, Array<{ wireType: WireType; value: Uint8Array | number }>>();
let pos = 0;
while (pos < data.length) {
const [fieldNum, wireType, value, newPos] = decodeField(data, pos);
if (fieldNum === null || wireType === null || value === null) break;
while (pos < data.length) {
const [fieldNum, wireType, value, newPos] = decodeField(data, pos);
if (fieldNum === null || wireType === null || value === null) break;
if (!fields.has(fieldNum)) {
fields.set(fieldNum, []);
}
fields.get(fieldNum)!.push({ wireType, value: value as Uint8Array | number });
pos = newPos;
}
if (!fields.has(fieldNum)) {
fields.set(fieldNum, []);
}
const fieldArray = fields.get(fieldNum);
if (fieldArray) {
fieldArray.push({ wireType, value: value as Uint8Array | number });
}
pos = newPos;
}
return fields;
return fields;
}
/**
@@ -102,200 +99,215 @@ export function decodeMessage(
* Returns frame data or null if incomplete
*/
export function parseConnectRPCFrame(buffer: Buffer): {
flags: number;
length: number;
payload: Uint8Array;
consumed: number;
flags: number;
length: number;
payload: Uint8Array;
consumed: number;
} | null {
if (buffer.length < 5) return null;
if (buffer.length < 5) return null;
const flags = buffer[0];
const length =
(buffer[1] << 24) | (buffer[2] << 16) | (buffer[3] << 8) | buffer[4];
const flags = buffer[0];
const length = (buffer[1] << 24) | (buffer[2] << 16) | (buffer[3] << 8) | buffer[4];
if (buffer.length < 5 + length) return null;
if (buffer.length < 5 + length) return null;
let payload = buffer.slice(5, 5 + length);
let payload = buffer.slice(5, 5 + length);
// Decompress if gzip
if (flags === 0x01 || flags === 0x02 || flags === 0x03) {
try {
payload = Buffer.from(zlib.gunzipSync(payload));
} catch {
// Decompression failed, use raw payload
}
}
// Decompress if gzip
if (flags === 0x01 || flags === 0x02 || flags === 0x03) {
try {
payload = Buffer.from(zlib.gunzipSync(payload));
} catch {
// Decompression failed, use raw payload
}
}
return {
flags,
length,
payload: new Uint8Array(payload),
consumed: 5 + length,
};
return {
flags,
length,
payload: new Uint8Array(payload),
consumed: 5 + length,
};
}
/**
* Extract tool call from protobuf data
*/
function extractToolCall(toolCallData: Uint8Array): {
id: string;
type: string;
function: { name: string; arguments: string };
isLast: boolean;
id: string;
type: string;
function: { name: string; arguments: string };
isLast: boolean;
} | null {
const toolCall = decodeMessage(toolCallData);
let toolCallId = "";
let toolName = "";
let rawArgs = "";
let isLast = false;
const toolCall = decodeMessage(toolCallData);
let toolCallId = '';
let toolName = '';
let rawArgs = '';
let isLast = false;
// Extract tool call ID
if (toolCall.has(FIELD.TOOL_ID)) {
const fullId = new TextDecoder().decode(
toolCall.get(FIELD.TOOL_ID)![0].value as Uint8Array
);
toolCallId = fullId.split("\n")[0]; // Take first line
}
// Extract tool call ID
if (toolCall.has(FIELD.TOOL_ID)) {
const idField = toolCall.get(FIELD.TOOL_ID);
if (idField && idField[0]) {
const fullId = new TextDecoder().decode(idField[0].value as Uint8Array);
toolCallId = fullId.split('\n')[0]; // Take first line
}
}
// Extract tool name
if (toolCall.has(FIELD.TOOL_NAME)) {
toolName = new TextDecoder().decode(
toolCall.get(FIELD.TOOL_NAME)![0].value as Uint8Array
);
}
// Extract tool name
if (toolCall.has(FIELD.TOOL_NAME)) {
const nameField = toolCall.get(FIELD.TOOL_NAME);
if (nameField && nameField[0]) {
toolName = new TextDecoder().decode(nameField[0].value as Uint8Array);
}
}
// Extract is_last flag
if (toolCall.has(FIELD.TOOL_IS_LAST)) {
isLast = (toolCall.get(FIELD.TOOL_IS_LAST)![0].value as number) !== 0;
}
// Extract is_last flag
if (toolCall.has(FIELD.TOOL_IS_LAST)) {
const lastField = toolCall.get(FIELD.TOOL_IS_LAST);
if (lastField && lastField[0]) {
isLast = (lastField[0].value as number) !== 0;
}
}
// Extract MCP params - nested real tool info
if (toolCall.has(FIELD.TOOL_MCP_PARAMS)) {
try {
const mcpParams = decodeMessage(
toolCall.get(FIELD.TOOL_MCP_PARAMS)![0].value as Uint8Array
);
// Extract MCP params - nested real tool info
if (toolCall.has(FIELD.TOOL_MCP_PARAMS)) {
try {
const mcpField = toolCall.get(FIELD.TOOL_MCP_PARAMS);
if (!mcpField || !mcpField[0]) return null;
if (mcpParams.has(FIELD.MCP_TOOLS_LIST)) {
const tool = decodeMessage(
mcpParams.get(FIELD.MCP_TOOLS_LIST)![0].value as Uint8Array
);
const mcpParams = decodeMessage(mcpField[0].value as Uint8Array);
if (tool.has(FIELD.MCP_NESTED_NAME)) {
toolName = new TextDecoder().decode(
tool.get(FIELD.MCP_NESTED_NAME)![0].value as Uint8Array
);
}
if (mcpParams.has(FIELD.MCP_TOOLS_LIST)) {
const toolsList = mcpParams.get(FIELD.MCP_TOOLS_LIST);
if (!toolsList || !toolsList[0]) return null;
if (tool.has(FIELD.MCP_NESTED_PARAMS)) {
rawArgs = new TextDecoder().decode(
tool.get(FIELD.MCP_NESTED_PARAMS)![0].value as Uint8Array
);
}
}
} catch {
// MCP parse error, continue
}
}
const tool = decodeMessage(toolsList[0].value as Uint8Array);
// Fallback to raw_args
if (!rawArgs && toolCall.has(FIELD.TOOL_RAW_ARGS)) {
rawArgs = new TextDecoder().decode(
toolCall.get(FIELD.TOOL_RAW_ARGS)![0].value as Uint8Array
);
}
if (tool.has(FIELD.MCP_NESTED_NAME)) {
const nestedName = tool.get(FIELD.MCP_NESTED_NAME);
if (nestedName && nestedName[0]) {
toolName = new TextDecoder().decode(nestedName[0].value as Uint8Array);
}
}
if (toolCallId && toolName) {
return {
id: toolCallId,
type: "function",
function: {
name: toolName,
arguments: rawArgs || "{}",
},
isLast,
};
}
if (tool.has(FIELD.MCP_NESTED_PARAMS)) {
const nestedParams = tool.get(FIELD.MCP_NESTED_PARAMS);
if (nestedParams && nestedParams[0]) {
rawArgs = new TextDecoder().decode(nestedParams[0].value as Uint8Array);
}
}
}
} catch {
// MCP parse error, continue
}
}
return null;
// Fallback to raw_args
if (!rawArgs && toolCall.has(FIELD.TOOL_RAW_ARGS)) {
const rawArgsField = toolCall.get(FIELD.TOOL_RAW_ARGS);
if (rawArgsField && rawArgsField[0]) {
rawArgs = new TextDecoder().decode(rawArgsField[0].value as Uint8Array);
}
}
if (toolCallId && toolName) {
return {
id: toolCallId,
type: 'function',
function: {
name: toolName,
arguments: rawArgs || '{}',
},
isLast,
};
}
return null;
}
/**
* Extract text and thinking from response data
*/
function extractTextAndThinking(
responseData: Uint8Array
): { text: string | null; thinking: string | null } {
const nested = decodeMessage(responseData);
let text: string | null = null;
let thinking: string | null = null;
function extractTextAndThinking(responseData: Uint8Array): {
text: string | null;
thinking: string | null;
} {
const nested = decodeMessage(responseData);
let text: string | null = null;
let thinking: string | null = null;
// Extract text
if (nested.has(FIELD.RESPONSE_TEXT)) {
text = new TextDecoder().decode(
nested.get(FIELD.RESPONSE_TEXT)![0].value as Uint8Array
);
}
// Extract text
if (nested.has(FIELD.RESPONSE_TEXT)) {
const textField = nested.get(FIELD.RESPONSE_TEXT);
if (textField && textField[0]) {
text = new TextDecoder().decode(textField[0].value as Uint8Array);
}
}
// Extract thinking
if (nested.has(FIELD.THINKING)) {
try {
const thinkingMsg = decodeMessage(
nested.get(FIELD.THINKING)![0].value as Uint8Array
);
if (thinkingMsg.has(FIELD.THINKING_TEXT)) {
thinking = new TextDecoder().decode(
thinkingMsg.get(FIELD.THINKING_TEXT)![0].value as Uint8Array
);
}
} catch {
// Thinking parse error, continue
}
}
// Extract thinking
if (nested.has(FIELD.THINKING)) {
try {
const thinkingField = nested.get(FIELD.THINKING);
if (thinkingField && thinkingField[0]) {
const thinkingMsg = decodeMessage(thinkingField[0].value as Uint8Array);
if (thinkingMsg.has(FIELD.THINKING_TEXT)) {
const thinkingTextField = thinkingMsg.get(FIELD.THINKING_TEXT);
if (thinkingTextField && thinkingTextField[0]) {
thinking = new TextDecoder().decode(thinkingTextField[0].value as Uint8Array);
}
}
}
} catch {
// Thinking parse error, continue
}
}
return { text, thinking };
return { text, thinking };
}
/**
* Extract text and tool calls from response payload
*/
export function extractTextFromResponse(payload: Uint8Array): {
text: string | null;
error: string | null;
toolCall: {
id: string;
type: string;
function: { name: string; arguments: string };
isLast: boolean;
} | null;
thinking: string | null;
text: string | null;
error: string | null;
toolCall: {
id: string;
type: string;
function: { name: string; arguments: string };
isLast: boolean;
} | null;
thinking: string | null;
} {
try {
const fields = decodeMessage(payload);
try {
const fields = decodeMessage(payload);
// Field 1: ClientSideToolV2Call
if (fields.has(FIELD.TOOL_CALL)) {
const toolCall = extractToolCall(
fields.get(FIELD.TOOL_CALL)![0].value as Uint8Array
);
if (toolCall) {
return { text: null, error: null, toolCall, thinking: null };
}
}
// Field 1: ClientSideToolV2Call
if (fields.has(FIELD.TOOL_CALL)) {
const toolCallField = fields.get(FIELD.TOOL_CALL);
if (toolCallField && toolCallField[0]) {
const toolCall = extractToolCall(toolCallField[0].value as Uint8Array);
if (toolCall) {
return { text: null, error: null, toolCall, thinking: null };
}
}
}
// Field 2: StreamUnifiedChatResponse
if (fields.has(FIELD.RESPONSE)) {
const { text, thinking } = extractTextAndThinking(
fields.get(FIELD.RESPONSE)![0].value as Uint8Array
);
// Field 2: StreamUnifiedChatResponse
if (fields.has(FIELD.RESPONSE)) {
const responseField = fields.get(FIELD.RESPONSE);
if (responseField && responseField[0]) {
const { text, thinking } = extractTextAndThinking(responseField[0].value as Uint8Array);
if (text || thinking) {
return { text, error: null, toolCall: null, thinking };
}
}
if (text || thinking) {
return { text, error: null, toolCall: null, thinking };
}
}
}
return { text: null, error: null, toolCall: null, thinking: null };
} catch {
return { text: null, error: null, toolCall: null, thinking: null };
}
return { text: null, error: null, toolCall: null, thinking: null };
} catch {
return { text: null, error: null, toolCall: null, thinking: null };
}
}