feat(cursor_hook): 增强storage.json telemetry字段保护机制

- 🔧 补齐核心ID字段(machineId、macMachineId、devDeviceId、sqmId)用于Hook与storage.json保护
- 新增normalizeFilePath函数规范化文件路径处理(兼容string/Buffer/URL等类型)
- 实现coerceContentToUtf8Text函数统一文本内容编码处理
- 扩展fs模块Hook覆盖appendFile、createWriteStream、open/close等API
- 添加fd追踪机制保护基于文件描述符的storage.json写入操作
- 优化注册表读取拦截逻辑增强健壮性
- 完善错误处理和日志输出机制
```
This commit is contained in:
煎饼果子卷鲨鱼辣椒
2026-02-03 22:30:23 +08:00
parent b9eeebc6fb
commit 8587a14bbf

View File

@@ -120,6 +120,23 @@ var __cursor_hook_config__ = {
ids = JSON.parse(content);
// 补全缺失字段,保持向后兼容
let updated = false;
// 🔧 补齐核心 ID 字段(用于 Hook 与 storage.json 保护)
if (!ids.machineId || typeof ids.machineId !== 'string') {
ids.machineId = generateHex64();
updated = true;
}
if (!ids.macMachineId || typeof ids.macMachineId !== 'string') {
ids.macMachineId = generateHex64();
updated = true;
}
if (!ids.devDeviceId || typeof ids.devDeviceId !== 'string') {
ids.devDeviceId = generateUUID();
updated = true;
}
if (!ids.sqmId || typeof ids.sqmId !== 'string') {
ids.sqmId = `{${generateUUID().toUpperCase()}}`;
updated = true;
}
if (!ids.machineGuid) {
ids.machineGuid = generateUUID();
updated = true;
@@ -326,12 +343,81 @@ var __cursor_hook_config__ = {
'telemetry.sqmId'
];
// 规范化 filePath兼容 string/Buffer/URL 等)
function normalizeFilePath(filePath) {
try {
if (filePath === undefined || filePath === null) return '';
if (typeof filePath === 'string') return filePath;
if (Buffer.isBuffer(filePath)) return filePath.toString('utf8');
// WHATWG URL (fs 支持 URL 对象)
if (typeof filePath === 'object' && typeof filePath.href === 'string') {
// 优先将 file:// URL 转为本地路径,避免传递 "file:///..." 字符串导致 existsSync/readFileSync 失败
if (typeof filePath.protocol === 'string' && filePath.protocol === 'file:') {
try {
const url = require('url');
if (url && typeof url.fileURLToPath === 'function') {
return url.fileURLToPath(filePath);
}
} catch (_) {
// ignore
}
}
return filePath.href;
}
return String(filePath);
} catch (_) {
return '';
}
}
function previewValue(value) {
try {
const s = String(value);
return s.length > 16 ? s.slice(0, 16) + '...' : s;
} catch (_) {
return '<unprintable>';
}
}
// 将写入内容转为 utf8 文本,并提供回写为“同类类型”的包装器
function coerceContentToUtf8Text(content) {
try {
if (typeof content === 'string') {
return { text: content, wrap: (s) => s };
}
if (Buffer.isBuffer(content)) {
return { text: content.toString('utf8'), wrap: (s) => Buffer.from(s, 'utf8') };
}
// TypedArray / DataView
if (content && typeof content === 'object') {
if (content instanceof Uint8Array) {
// Buffer 也属于 Uint8Array但已在上面处理
const buf = Buffer.from(content);
return { text: buf.toString('utf8'), wrap: (s) => new Uint8Array(Buffer.from(s, 'utf8')) };
}
if (typeof ArrayBuffer !== 'undefined' && content instanceof ArrayBuffer) {
const buf = Buffer.from(content);
return { text: buf.toString('utf8'), wrap: (s) => Buffer.from(s, 'utf8') };
}
if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' && ArrayBuffer.isView(content)) {
const buf = Buffer.from(content.buffer, content.byteOffset, content.byteLength);
return { text: buf.toString('utf8'), wrap: (s) => Buffer.from(s, 'utf8') };
}
}
} catch (_) {
// ignore
}
return null;
}
// 检查路径是否为 storage.json
function isStorageJsonPath(filePath) {
if (!filePath || typeof filePath !== 'string') return false;
const normalized = filePath.replace(/\\/g, '/').toLowerCase();
return normalized.includes('globalStorage/storage.json') ||
normalized.includes('globalstorage/storage.json');
const raw = normalizeFilePath(filePath);
if (!raw) return false;
const normalized = raw.replace(/\\/g, '/').toLowerCase();
return normalized.includes('globalstorage/storage.json');
}
// 保护 storage.json 中的 telemetry 字段
@@ -340,57 +426,80 @@ var __cursor_hook_config__ = {
try {
const fs = require('fs');
let newData = typeof content === 'string' ? JSON.parse(content) : content;
const coerced = coerceContentToUtf8Text(content);
if (!coerced) return content;
let newData;
try {
newData = JSON.parse(coerced.text);
} catch (_) {
return content;
}
// 如果写入的内容不是有效的 JSON 对象,直接返回
if (typeof newData !== 'object' || newData === null) {
return content;
}
// 读取当前文件中的受保护字段
// 保护值优先级:
// 1) __cursor_ids__Hook 配置/环境变量/自动生成)
// 2) 现有 storage.json 中已存在的值
// 3) 本次写入值(最低)
const protectedValues = {
'telemetry.machineId': __cursor_ids__ && __cursor_ids__.machineId,
'telemetry.macMachineId': __cursor_ids__ && __cursor_ids__.macMachineId,
'telemetry.devDeviceId': __cursor_ids__ && __cursor_ids__.devDeviceId,
'telemetry.sqmId': __cursor_ids__ && __cursor_ids__.sqmId
};
// 仅当 Hook 配置不完整时,才读取旧文件值作为二级兜底
let existingProtected = {};
try {
if (fs.existsSync(filePath)) {
const existing = JSON.parse(fs.readFileSync(filePath, 'utf8'));
for (const key of PROTECTED_TELEMETRY_KEYS) {
if (existing[key] !== undefined) {
existingProtected[key] = existing[key];
const needExisting = PROTECTED_TELEMETRY_KEYS.some((k) => !(typeof protectedValues[k] === 'string' && protectedValues[k]));
if (needExisting) {
try {
if (fs.existsSync(filePath)) {
const existingText = fs.readFileSync(filePath, 'utf8');
const existing = JSON.parse(existingText);
if (existing && typeof existing === 'object') {
for (const key of PROTECTED_TELEMETRY_KEYS) {
if (typeof existing[key] === 'string' && existing[key]) {
existingProtected[key] = existing[key];
}
}
}
}
} catch (_) {
// ignore
}
} catch (e) {
// 文件不存在或解析失败,使用 Hook 配置的 ID
}
// 强制使用 Hook 配置的 ID优先级最高
const protectedValues = {
'telemetry.machineId': __cursor_ids__.machineId,
'telemetry.macMachineId': __cursor_ids__.macMachineId,
'telemetry.devDeviceId': __cursor_ids__.devDeviceId,
'telemetry.sqmId': __cursor_ids__.sqmId
};
// 检测并修正被覆盖的字段
let modified = false;
for (const key of PROTECTED_TELEMETRY_KEYS) {
const protectedValue = protectedValues[key];
if (newData[key] !== undefined && newData[key] !== protectedValue) {
log(`[fs Hook] 拦截 ${key} 覆盖: ${newData[key].substring(0, 16)}... -> ${protectedValue.substring(0, 16)}...`);
newData[key] = protectedValue;
modified = true;
} else if (newData[key] === undefined && existingProtected[key]) {
// 如果新数据没有该字段,但旧文件有,保留旧值
newData[key] = existingProtected[key];
const fromIds = protectedValues[key];
const desired = (typeof fromIds === 'string' && fromIds) ? fromIds
: (typeof existingProtected[key] === 'string' && existingProtected[key]) ? existingProtected[key]
: undefined;
if (desired === undefined) {
continue;
}
// 方案B无论写入内容是否包含该字段都确保最终值稳定缺失则补齐
if (newData[key] !== desired) {
log(`[fs Hook] 固定 ${key}: ${previewValue(newData[key])} -> ${previewValue(desired)}`);
newData[key] = desired;
modified = true;
}
}
if (modified) {
log('[fs Hook] storage.json telemetry 字段已保护');
return typeof content === 'string' ? JSON.stringify(newData, null, '\t') : newData;
const nextText = JSON.stringify(newData, null, '\t');
return coerced.wrap(nextText);
}
} catch (e) {
log('[fs Hook] 处理 storage.json 失败:', e.message);
const msg = e && e.message ? e.message : String(e);
log('[fs Hook] 处理 storage.json 失败:', msg);
}
return content;
@@ -399,6 +508,35 @@ var __cursor_hook_config__ = {
function hookFs(fsModule) {
const originalWriteFileSync = fsModule.writeFileSync;
const originalWriteFile = fsModule.writeFile;
const originalAppendFileSync = fsModule.appendFileSync;
const originalAppendFile = fsModule.appendFile;
const originalCreateWriteStream = fsModule.createWriteStream;
const originalOpenSync = fsModule.openSync;
const originalOpen = fsModule.open;
const originalCloseSync = fsModule.closeSync;
const originalClose = fsModule.close;
// fd 追踪:覆盖 open/close 路径(仅用于 storage.json
const storageJsonFds = new Map();
let inFdFix = false;
const fixStorageJsonFile = (filePath) => {
if (inFdFix) return;
inFdFix = true;
try {
const current = fsModule.readFileSync(filePath, 'utf8');
const next = protectStorageJson(current, filePath);
if (typeof next === 'string' && next !== current) {
originalWriteFileSync.call(fsModule, filePath, next, 'utf8');
log('[fs Hook] close-fix: storage.json telemetry 字段已重新保护');
}
} catch (e) {
const msg = e && e.message ? e.message : String(e);
log('[fs Hook] close-fix 失败:', msg);
} finally {
inFdFix = false;
}
};
// Hook writeFileSync
fsModule.writeFileSync = function(filePath, data, options) {
@@ -424,6 +562,126 @@ var __cursor_hook_config__ = {
const protectedData = protectStorageJson(data, filePath);
return originalPromisesWriteFile.call(this, filePath, protectedData, options);
};
if (typeof fsModule.promises.appendFile === 'function') {
const originalPromisesAppendFile = fsModule.promises.appendFile;
fsModule.promises.appendFile = async function(filePath, data, options) {
const protectedData = protectStorageJson(data, filePath);
return originalPromisesAppendFile.call(this, filePath, protectedData, options);
};
}
}
// Hook appendFileSync
if (typeof originalAppendFileSync === 'function') {
fsModule.appendFileSync = function(filePath, data, options) {
const protectedData = protectStorageJson(data, filePath);
return originalAppendFileSync.call(this, filePath, protectedData, options);
};
}
// Hook appendFile (异步版本)
if (typeof originalAppendFile === 'function') {
fsModule.appendFile = function(filePath, data, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
const protectedData = protectStorageJson(data, filePath);
return originalAppendFile.call(this, filePath, protectedData, options, callback);
};
}
// Hook createWriteStream仅对 storage.json保持原生 WriteStream但 close 后做补救性修正)
if (typeof originalCreateWriteStream === 'function') {
fsModule.createWriteStream = function(filePath, options) {
const stream = originalCreateWriteStream.apply(this, arguments);
if (isStorageJsonPath(filePath) && stream && typeof stream.on === 'function') {
stream.on('close', () => {
try {
fixStorageJsonFile(filePath);
} catch (_) {
// ignore
}
});
}
return stream;
};
}
// Hook open/openSync追踪 storage.json 的 fd
if (typeof originalOpenSync === 'function') {
fsModule.openSync = function(filePath) {
const fd = originalOpenSync.apply(this, arguments);
try {
if (!inFdFix && isStorageJsonPath(filePath)) {
storageJsonFds.set(fd, filePath);
}
} catch (_) {
// ignore
}
return fd;
};
}
if (typeof originalOpen === 'function') {
fsModule.open = function(filePath, flags, mode, callback) {
if (typeof mode === 'function') {
callback = mode;
mode = undefined;
}
const wrapped = function(err, fd) {
try {
if (!err && !inFdFix && isStorageJsonPath(filePath)) {
storageJsonFds.set(fd, filePath);
}
} catch (_) {
// ignore
}
if (typeof callback === 'function') {
return callback.apply(this, arguments);
}
};
if (mode === undefined) {
return originalOpen.call(this, filePath, flags, wrapped);
}
return originalOpen.call(this, filePath, flags, mode, wrapped);
};
}
// Hook close/closeSync关闭后再做一次“落盘后修正”覆盖 fd 写入路径)
if (typeof originalCloseSync === 'function') {
fsModule.closeSync = function(fd) {
const filePath = storageJsonFds.get(fd);
const ret = originalCloseSync.apply(this, arguments);
if (filePath !== undefined) {
storageJsonFds.delete(fd);
fixStorageJsonFile(filePath);
}
return ret;
};
}
if (typeof originalClose === 'function') {
fsModule.close = function(fd, callback) {
const filePath = storageJsonFds.get(fd);
const wrapped = function(err) {
try {
if (!err && filePath !== undefined) {
storageJsonFds.delete(fd);
fixStorageJsonFile(filePath);
}
} catch (_) {
// ignore
}
if (typeof callback === 'function') {
return callback.apply(this, arguments);
}
};
return originalClose.call(this, fd, wrapped);
};
}
log('[fs Hook] 已启用 storage.json 写入保护');
@@ -511,17 +769,21 @@ var __cursor_hook_config__ = {
return {
...registryModule,
GetStringRegKey: function(hive, path, name) {
const pathStr = (typeof path === 'string') ? path : '';
// 拦截 MachineId 读取
if (name === 'MachineId' || path.includes('SQMClient')) {
if (name === 'MachineId' || pathStr.includes('SQMClient')) {
log('拦截注册表 MachineId/SQMClient 读取');
return __cursor_ids__.sqmId;
}
// 拦截 MachineGuid 读取
if (name === 'MachineGuid' || path.includes('Cryptography')) {
if (name === 'MachineGuid' || pathStr.includes('Cryptography')) {
log('拦截注册表 MachineGuid 读取');
return getMachineGuid();
}
return originalGetStringRegKey?.apply(this, arguments) || '';
if (typeof originalGetStringRegKey === 'function') {
return originalGetStringRegKey.apply(this, arguments) || '';
}
return '';
}
};
}
@@ -605,15 +867,19 @@ var __cursor_hook_config__ = {
const hooked = {
...registryModule,
GetStringRegKey: function(hive, path, name) {
if (name === 'MachineId' || path?.includes('SQMClient')) {
const pathStr = (typeof path === 'string') ? path : '';
if (name === 'MachineId' || pathStr.includes('SQMClient')) {
log('动态导入: 拦截 SQMClient');
return __cursor_ids__.sqmId;
}
if (name === 'MachineGuid' || path?.includes('Cryptography')) {
if (name === 'MachineGuid' || pathStr.includes('Cryptography')) {
log('动态导入: 拦截 MachineGuid');
return getMachineGuid();
}
return originalGetStringRegKey?.apply(this, arguments) || '';
if (typeof originalGetStringRegKey === 'function') {
return originalGetStringRegKey.apply(this, arguments) || '';
}
return '';
}
};
@@ -630,6 +896,34 @@ var __cursor_hook_config__ = {
const hooked = { ...fsModule };
const originalWriteFileSync = fsModule.writeFileSync;
const originalWriteFile = fsModule.writeFile;
const originalAppendFileSync = fsModule.appendFileSync;
const originalAppendFile = fsModule.appendFile;
const originalCreateWriteStream = fsModule.createWriteStream;
const originalOpenSync = fsModule.openSync;
const originalOpen = fsModule.open;
const originalCloseSync = fsModule.closeSync;
const originalClose = fsModule.close;
const storageJsonFds = new Map();
let inFdFix = false;
const fixStorageJsonFile = (filePath) => {
if (inFdFix) return;
inFdFix = true;
try {
const current = fsModule.readFileSync(filePath, 'utf8');
const next = protectStorageJson(current, filePath);
if (typeof next === 'string' && next !== current) {
originalWriteFileSync.call(fsModule, filePath, next, 'utf8');
log('动态导入: close-fix storage.json telemetry 字段已重新保护');
}
} catch (e) {
const msg = e && e.message ? e.message : String(e);
log('动态导入: close-fix 失败:', msg);
} finally {
inFdFix = false;
}
};
hooked.writeFileSync = function(filePath, data, options) {
const protectedData = protectStorageJson(data, filePath);
@@ -655,6 +949,121 @@ var __cursor_hook_config__ = {
return originalPromisesWriteFile.call(this, filePath, protectedData, options);
}
};
if (typeof fsModule.promises.appendFile === 'function') {
const originalPromisesAppendFile = fsModule.promises.appendFile;
hooked.promises.appendFile = async function(filePath, data, options) {
const protectedData = protectStorageJson(data, filePath);
return originalPromisesAppendFile.call(this, filePath, protectedData, options);
};
}
}
if (typeof originalAppendFileSync === 'function') {
hooked.appendFileSync = function(filePath, data, options) {
const protectedData = protectStorageJson(data, filePath);
return originalAppendFileSync.call(this, filePath, protectedData, options);
};
}
if (typeof originalAppendFile === 'function') {
hooked.appendFile = function(filePath, data, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
const protectedData = protectStorageJson(data, filePath);
return originalAppendFile.call(this, filePath, protectedData, options, callback);
};
}
if (typeof originalCreateWriteStream === 'function') {
hooked.createWriteStream = function(filePath, options) {
const stream = originalCreateWriteStream.apply(this, arguments);
if (isStorageJsonPath(filePath) && stream && typeof stream.on === 'function') {
stream.on('close', () => {
try {
fixStorageJsonFile(filePath);
} catch (_) {
// ignore
}
});
}
return stream;
};
}
if (typeof originalOpenSync === 'function') {
hooked.openSync = function(filePath) {
const fd = originalOpenSync.apply(this, arguments);
try {
if (!inFdFix && isStorageJsonPath(filePath)) {
storageJsonFds.set(fd, filePath);
}
} catch (_) {
// ignore
}
return fd;
};
}
if (typeof originalOpen === 'function') {
hooked.open = function(filePath, flags, mode, callback) {
if (typeof mode === 'function') {
callback = mode;
mode = undefined;
}
const wrapped = function(err, fd) {
try {
if (!err && !inFdFix && isStorageJsonPath(filePath)) {
storageJsonFds.set(fd, filePath);
}
} catch (_) {
// ignore
}
if (typeof callback === 'function') {
return callback.apply(this, arguments);
}
};
if (mode === undefined) {
return originalOpen.call(this, filePath, flags, wrapped);
}
return originalOpen.call(this, filePath, flags, mode, wrapped);
};
}
if (typeof originalCloseSync === 'function') {
hooked.closeSync = function(fd) {
const filePath = storageJsonFds.get(fd);
const ret = originalCloseSync.apply(this, arguments);
if (filePath !== undefined) {
storageJsonFds.delete(fd);
fixStorageJsonFile(filePath);
}
return ret;
};
}
if (typeof originalClose === 'function') {
hooked.close = function(fd, callback) {
const filePath = storageJsonFds.get(fd);
const wrapped = function(err) {
try {
if (!err && filePath !== undefined) {
storageJsonFds.delete(fd);
fixStorageJsonFile(filePath);
}
} catch (_) {
// ignore
}
if (typeof callback === 'function') {
return callback.apply(this, arguments);
}
};
return originalClose.call(this, fd, wrapped);
};
}
log('动态导入: 已 Hook fs 模块');