32a5479086
Use OS machine identifiers (Windows MachineGuid, macOS IOPlatformUUID, Linux machine-id) hashed as deviceId so all accounts on one PC share one license slot. Keep legacy userData/device.id for migration and retire it on re-activation. Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
3.4 KiB
TypeScript
110 lines
3.4 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
|
|
import {
|
|
hashMachineRawId,
|
|
machineWideIdPath,
|
|
parseMacIOPlatformUUID,
|
|
parseWindowsMachineGuid,
|
|
parseWmicUuid,
|
|
resolveMachineFingerprint,
|
|
} from './machineFingerprint';
|
|
|
|
void test('hashMachineRawId: стабилен и не зависит от регистра GUID', () => {
|
|
const a = hashMachineRawId('win32', 'ABCDEF00-1111-2222-3333-444455556666');
|
|
const b = hashMachineRawId('win32', 'abcdef00-1111-2222-3333-444455556666');
|
|
assert.equal(a, b);
|
|
assert.equal(a.length, 64);
|
|
assert.notEqual(a, hashMachineRawId('linux', 'abcdef00-1111-2222-3333-444455556666'));
|
|
});
|
|
|
|
void test('parseWindowsMachineGuid', () => {
|
|
const out = `
|
|
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography
|
|
MachineGuid REG_SZ A1B2C3D4-E5F6-7890-ABCD-EF1234567890
|
|
`;
|
|
assert.equal(parseWindowsMachineGuid(out), 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890');
|
|
assert.equal(parseWindowsMachineGuid('nope'), null);
|
|
});
|
|
|
|
void test('parseMacIOPlatformUUID', () => {
|
|
const out = `
|
|
+-o IOPlatformExpertDevice <class IOPlatformExpertDevice, id 0x1000001ea, registered, matched>
|
|
{
|
|
"IOPlatformUUID" = "A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
|
|
}
|
|
`;
|
|
assert.equal(parseMacIOPlatformUUID(out), 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890');
|
|
});
|
|
|
|
void test('parseWmicUuid', () => {
|
|
assert.equal(parseWmicUuid('UUID\nA1B2C3D4-E5F6-7890-ABCD-EF1234567890\n'), 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890');
|
|
assert.equal(parseWmicUuid('UUID\n00000000-0000-0000-0000-000000000000\n'), null);
|
|
});
|
|
|
|
void test('resolveMachineFingerprint: override через DND_LICENSE_DEVICE_ID', () => {
|
|
const id = resolveMachineFingerprint({
|
|
platform: 'linux',
|
|
env: { DND_LICENSE_DEVICE_ID: 'override-device-id-12345' },
|
|
});
|
|
assert.equal(id, 'override-device-id-12345');
|
|
});
|
|
|
|
void test('resolveMachineFingerprint: Windows MachineGuid → одинаковый hash', () => {
|
|
const exec = () =>
|
|
`
|
|
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography
|
|
MachineGuid REG_SZ A1B2C3D4-E5F6-7890-ABCD-EF1234567890
|
|
`;
|
|
const a = resolveMachineFingerprint({
|
|
platform: 'win32',
|
|
env: {},
|
|
execFileSync: exec as never,
|
|
});
|
|
const b = resolveMachineFingerprint({
|
|
platform: 'win32',
|
|
env: {},
|
|
execFileSync: exec as never,
|
|
});
|
|
assert.equal(a, b);
|
|
assert.equal(a, hashMachineRawId('win32', 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890'));
|
|
});
|
|
|
|
void test('resolveMachineFingerprint: Linux /etc/machine-id', () => {
|
|
const id = resolveMachineFingerprint({
|
|
platform: 'linux',
|
|
env: {},
|
|
readFileSync: (p) => {
|
|
if (p === '/etc/machine-id') return '0123456789abcdef0123456789abcdef\n';
|
|
throw new Error('enoent');
|
|
},
|
|
});
|
|
assert.equal(id, hashMachineRawId('linux', '0123456789abcdef0123456789abcdef'));
|
|
});
|
|
|
|
void test('resolveMachineFingerprint: fallback в machine-wide путь', () => {
|
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'machine-fp-'));
|
|
const env = { PROGRAMDATA: tmp };
|
|
const p = machineWideIdPath('win32', env);
|
|
const id1 = resolveMachineFingerprint({
|
|
platform: 'win32',
|
|
env,
|
|
execFileSync: () => {
|
|
throw new Error('no reg');
|
|
},
|
|
});
|
|
const id2 = resolveMachineFingerprint({
|
|
platform: 'win32',
|
|
env,
|
|
execFileSync: () => {
|
|
throw new Error('no reg');
|
|
},
|
|
});
|
|
assert.equal(id1, id2);
|
|
assert.ok(fs.existsSync(p));
|
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
});
|