test/device-fingerprint.test.js
Rama master · réplica de lectura del repositorio original.
import { describe, it, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { buildGetChatMessageRequest, __testing } from '../src/devin-connect.js';
import { isStableDeviceEnabled, ensureDeviceSeed } from '../src/auth.js';
const { generateFingerprint } = __testing;
// Per-account stable device fingerprint (opt-in, WINDSURFAPI_STABLE_DEVICE).
// Default OFF: #31 stays per-request random and byte-identical to before. When a
// deviceSeed is supplied, the fingerprint is deterministically derived so an
// account presents one stable device across requests. See the machine-id study.
afterEach(() => { delete process.env.WINDSURFAPI_STABLE_DEVICE; });
describe('generateFingerprint — #31 ClientMetadata (732 hex / 366 bytes)', () => {
it('no seed → per-request random, always the correct shape', () => {
const a = generateFingerprint();
const b = generateFingerprint();
assert.equal(a.length, 732, '732 hex chars');
assert.match(a, /^[0-9a-f]{732}$/, 'lowercase hex');
assert.notEqual(a, b, 'two random fingerprints differ (anti-fingerprint default)');
});
it('same seed → SAME fingerprint every call (stable per account)', () => {
const seed = 'a'.repeat(64);
const a = generateFingerprint(seed);
const b = generateFingerprint(seed);
assert.equal(a, b, 'deterministic for a given seed');
assert.equal(a.length, 732, 'still 732 hex chars (shape preserved)');
assert.match(a, /^[0-9a-f]{732}$/);
});
it('different seeds → different fingerprints (each account its own device)', () => {
assert.notEqual(generateFingerprint('seed-account-1'), generateFingerprint('seed-account-2'));
});
it('derived fingerprint is NOT trivially the seed (HKDF expand, not echo)', () => {
const seed = 'deadbeef';
const fp = generateFingerprint(seed);
assert.ok(!fp.includes(seed) || fp.length === 732, 'seed not naively embedded');
assert.equal(fp.length, 732);
});
});
describe('buildGetChatMessageRequest — deviceSeed threads to #31, byte-equivalence when absent', () => {
const base = {
token: 'tok_test',
messages: [{ role: 'user', content: 'hi' }],
model: 'swe-1-6-slow',
sessionId: 'fixed-session',
};
it('same deviceSeed → identical request bytes (stable device is reproducible)', () => {
// sessionId is pinned so the only per-request randomness left is #31 and the
// ModelConfig #15.1 UUID; with a fixed seed #31 is stable. We assert the #31
// field region is identical across two builds with the same seed by checking
// the derived fingerprint appears in both proto buffers.
const seed = 'c'.repeat(64);
const fp = generateFingerprint(seed);
// #31 is written as the fingerprint STRING (UTF-8) into the proto, so in the
// buffer's hex dump it appears as the hex-of-the-ascii-of-fp. Same seed →
// same fp → that byte region is present in both builds.
const needle = Buffer.from(fp, 'utf8').toString('hex');
const hex1 = Buffer.from(p1FromSeed(seed)).toString('hex');
const hex2 = Buffer.from(p1FromSeed(seed)).toString('hex');
assert.ok(hex1.includes(needle), 'proto embeds the derived stable fingerprint');
assert.ok(hex2.includes(needle), 'second build embeds the same fingerprint');
function p1FromSeed(s) { return buildGetChatMessageRequest({ ...base, deviceSeed: s }); }
});
it('no deviceSeed → random #31 each build (default path unchanged)', () => {
const p1 = Buffer.from(buildGetChatMessageRequest({ ...base })).toString('hex');
const p2 = Buffer.from(buildGetChatMessageRequest({ ...base })).toString('hex');
// With no seed the #31 fingerprint is random, so the two proto buffers differ.
assert.notEqual(p1, p2, 'default path stays per-request random');
});
});
describe('auth: isStableDeviceEnabled + ensureDeviceSeed lifecycle', () => {
it('disabled by default → ensureDeviceSeed returns undefined and does not stamp the account', () => {
assert.equal(isStableDeviceEnabled({}), false);
const acct = { apiKey: 'k' };
assert.equal(ensureDeviceSeed(acct, {}), undefined);
assert.equal(acct.deviceSeed, undefined, 'account left untouched when mode off');
});
it('enabled → mints a stable 64-hex seed on first use, reuses it after', () => {
const env = { WINDSURFAPI_STABLE_DEVICE: '1' };
assert.equal(isStableDeviceEnabled(env), true);
const acct = { apiKey: 'k' };
const s1 = ensureDeviceSeed(acct, env);
assert.match(s1, /^[0-9a-f]{64}$/, 'seed is 64 hex');
const s2 = ensureDeviceSeed(acct, env);
assert.equal(s1, s2, 'same account reuses its minted seed');
assert.equal(acct.deviceSeed, s1, 'seed persisted on the account object');
});
it('distinct accounts get distinct seeds', () => {
const env = { WINDSURFAPI_STABLE_DEVICE: '1' };
const a = { apiKey: 'a' };
const b = { apiKey: 'b' };
assert.notEqual(ensureDeviceSeed(a, env), ensureDeviceSeed(b, env));
});
it('preexisting seed is respected (loaded from accounts.json), not regenerated', () => {
const env = { WINDSURFAPI_STABLE_DEVICE: '1' };
const acct = { apiKey: 'k', deviceSeed: 'preexisting-seed-value' };
assert.equal(ensureDeviceSeed(acct, env), 'preexisting-seed-value');
});
it('end-to-end: a stable seed produces a stable #31 across the account', () => {
const env = { WINDSURFAPI_STABLE_DEVICE: '1' };
const acct = { apiKey: 'k' };
const seed = ensureDeviceSeed(acct, env);
assert.equal(generateFingerprint(seed), generateFingerprint(acct.deviceSeed), 'stable device end to end');
});
});