fix(license): bind deviceId to physical machine, not OS user
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>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
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 });
|
||||
});
|
||||
Reference in New Issue
Block a user