mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 22:15:33 +00:00
fix(cursor): address code review feedback (attempt 1/5)
- Fix bitwise shift overflow in generateChecksum: use Math.trunc
division for >>40/>>32 which wrap modulo 32 in JS
- Add FIXED64/FIXED32 bounds checks in protobuf decoder to prevent
out-of-bounds slice on truncated buffers
- Consolidate COMPRESS_FLAG to single definition in schema (DRY):
executor now imports from schema, added GZIP_ALT/GZIP_BOTH values
- Fix token split edge case: use indexOf+slice instead of split('::')[1]
to handle tokens containing multiple :: delimiters
- Fix AbortSignal listener leak: store handler ref, use once:true,
remove listener on request end/error
This commit is contained in:
1 parent
cc5a9039e4
commit
31f574118d
3 files changed
+27
-17
No files matched your search
@@ -10,13 +10,7 @@ import { generateCursorBody, extractTextFromResponse } from './cursor-protobuf.j
|
|||||||
import { buildCursorRequest } from './cursor-translator.js';
|
import { buildCursorRequest } from './cursor-translator.js';
|
||||||
import type { CursorTool } from './cursor-protobuf-schema.js';
|
import type { CursorTool } from './cursor-protobuf-schema.js';
|
||||||
|
|
||||||
/** Compression flags for response parsing */
|
import { COMPRESS_FLAG } from './cursor-protobuf-schema.js';
|
||||||
const COMPRESS_FLAG = {
|
|
||||||
NONE: 0x00,
|
|
||||||
GZIP: 0x01,
|
|
||||||
GZIP_ALT: 0x02,
|
|
||||||
GZIP_BOTH: 0x03,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/** Cursor credentials structure */
|
/** Cursor credentials structure */
|
||||||
interface CursorCredentials {
|
interface CursorCredentials {
|
||||||
@@ -163,12 +157,14 @@ export class CursorExecutor {
|
|||||||
*/
|
*/
|
||||||
generateChecksum(machineId: string): string {
|
generateChecksum(machineId: string): string {
|
||||||
const timestamp = Math.floor(Date.now() / 1000000);
|
const timestamp = Math.floor(Date.now() / 1000000);
|
||||||
|
// JS bitwise shifts wrap modulo 32, so >>40 and >>32 give wrong results.
|
||||||
|
// Use Math.trunc division for upper bytes that exceed 32-bit range.
|
||||||
const byteArray = new Uint8Array([
|
const byteArray = new Uint8Array([
|
||||||
(timestamp >> 40) & 0xff,
|
Math.trunc(timestamp / 2 ** 40) & 0xff,
|
||||||
(timestamp >> 32) & 0xff,
|
Math.trunc(timestamp / 2 ** 32) & 0xff,
|
||||||
(timestamp >> 24) & 0xff,
|
(timestamp >>> 24) & 0xff,
|
||||||
(timestamp >> 16) & 0xff,
|
(timestamp >>> 16) & 0xff,
|
||||||
(timestamp >> 8) & 0xff,
|
(timestamp >>> 8) & 0xff,
|
||||||
timestamp & 0xff,
|
timestamp & 0xff,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -209,7 +205,8 @@ export class CursorExecutor {
|
|||||||
throw new Error('Machine ID is required for Cursor API');
|
throw new Error('Machine ID is required for Cursor API');
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanToken = accessToken.includes('::') ? accessToken.split('::')[1] : accessToken;
|
const delimIdx = accessToken.indexOf('::');
|
||||||
|
const cleanToken = delimIdx !== -1 ? accessToken.slice(delimIdx + 2) : accessToken;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authorization: `Bearer ${cleanToken}`,
|
authorization: `Bearer ${cleanToken}`,
|
||||||
@@ -326,13 +323,18 @@ export class CursorExecutor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (signal) {
|
if (signal) {
|
||||||
// TODO: AbortSignal listener is not removed after request completes.
|
const onAbort = () => {
|
||||||
// To fix: store handler reference, remove in end/error callbacks.
|
|
||||||
signal.addEventListener('abort', () => {
|
|
||||||
req.close();
|
req.close();
|
||||||
client.close();
|
client.close();
|
||||||
reject(new Error('Request aborted'));
|
reject(new Error('Request aborted'));
|
||||||
});
|
};
|
||||||
|
signal.addEventListener('abort', onAbort, { once: true });
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
signal.removeEventListener('abort', onAbort);
|
||||||
|
};
|
||||||
|
req.on('end', cleanup);
|
||||||
|
req.on('error', cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
req.write(body);
|
req.write(body);
|
||||||
|
|||||||
@@ -56,9 +56,15 @@ export function decodeField(
|
|||||||
value = buffer.slice(pos2, pos2 + length);
|
value = buffer.slice(pos2, pos2 + length);
|
||||||
pos = pos2 + length;
|
pos = pos2 + length;
|
||||||
} else if (wireType === WIRE_TYPE.FIXED64) {
|
} else if (wireType === WIRE_TYPE.FIXED64) {
|
||||||
|
if (pos + 8 > buffer.length) {
|
||||||
|
return [null, null, null, buffer.length];
|
||||||
|
}
|
||||||
value = buffer.slice(pos, pos + 8);
|
value = buffer.slice(pos, pos + 8);
|
||||||
pos += 8;
|
pos += 8;
|
||||||
} else if (wireType === WIRE_TYPE.FIXED32) {
|
} else if (wireType === WIRE_TYPE.FIXED32) {
|
||||||
|
if (pos + 4 > buffer.length) {
|
||||||
|
return [null, null, null, buffer.length];
|
||||||
|
}
|
||||||
value = buffer.slice(pos, pos + 4);
|
value = buffer.slice(pos, pos + 4);
|
||||||
pos += 4;
|
pos += 4;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -201,4 +201,6 @@ export interface MessageId {
|
|||||||
export const COMPRESS_FLAG = {
|
export const COMPRESS_FLAG = {
|
||||||
NONE: 0x00,
|
NONE: 0x00,
|
||||||
GZIP: 0x01,
|
GZIP: 0x01,
|
||||||
|
GZIP_ALT: 0x02,
|
||||||
|
GZIP_BOTH: 0x03,
|
||||||
} as const;
|
} as const;
|
||||||
Reference in new issue
Block a user