Files
Test User 5152086668
CI / Docker build (push) Has been cancelled
CI / Frontend test and build (push) Has been cancelled
CI / Rust test (push) Has been cancelled
Release / Build and publish release (push) Has been cancelled
Initial browser fingerprint app with Gitea CI
2026-07-04 03:15:26 +08:00

186 lines
5.1 KiB
JavaScript

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { getMessages } from "../src/i18n.mjs";
import {
buildTrackingPayload,
compareFingerprintSnapshots,
createFingerprintIdToken,
createFingerprintShareCard,
fingerprintIdPreview,
parseFingerprintIdToken,
} from "../src/tracking.mjs";
function memoryStorage() {
const values = new Map();
return {
getItem: (key) => values.get(key) ?? null,
setItem: (key, value) => values.set(key, String(value)),
};
}
const result = {
score: 62,
band: "high",
locale: "zh-CN",
timezone: "Asia/Shanghai",
timezoneOffsetMinutes: -480,
userAgentFamily: "Apple",
signals: [
{
id: "timezone",
raw: "Asia/Shanghai",
score: 1,
contribution: 30,
weight: 30,
},
],
};
describe("tracking payload", () => {
it("builds stable hashed components without echoing raw fingerprint values", async () => {
const storage = memoryStorage();
const first = await buildTrackingPayload(
{
result,
localFacts: [
{
id: "canvas-hash",
value: "secret-canvas-digest",
},
],
},
{
storage,
randomId: () => "return-key-alpha",
},
);
const second = await buildTrackingPayload(
{
result,
localFacts: [
{
id: "canvas-hash",
value: "secret-canvas-digest",
},
],
},
{ storage },
);
assert.equal(first.visitorKey, "return-key-alpha");
assert.equal(second.visitorKey, "return-key-alpha");
assert.equal(first.fingerprintHash, second.fingerprintHash);
assert.equal(first.components.some((component) => component.id === "fact-canvas-hash"), true);
assert.doesNotMatch(JSON.stringify(first), /Asia\/Shanghai|secret-canvas-digest/);
});
it("keeps the return key while changing the fingerprint hash when components change", async () => {
const storage = memoryStorage();
const first = await buildTrackingPayload(
{
result,
localFacts: [{ id: "canvas-hash", value: "canvas-a" }],
},
{
storage,
randomId: () => "return-key-beta",
},
);
const second = await buildTrackingPayload(
{
result,
localFacts: [{ id: "canvas-hash", value: "canvas-b" }],
},
{ storage },
);
assert.equal(second.visitorKey, first.visitorKey);
assert.notEqual(second.fingerprintHash, first.fingerprintHash);
});
it("exports and parses a copyable fingerprint ID without raw values", async () => {
const payload = await buildTrackingPayload(
{
result,
localFacts: [{ id: "canvas-hash", value: "secret-canvas-digest" }],
},
{
storage: memoryStorage(),
randomId: () => "return-key-copy",
},
);
const token = createFingerprintIdToken(payload);
const parsed = parseFingerprintIdToken(token);
assert.match(token, /^fp1_/);
assert.equal(parsed.fingerprintHash, payload.fingerprintHash);
assert.equal(fingerprintIdPreview(parsed), payload.fingerprintHash.slice(0, 12));
assert.doesNotMatch(token, /Asia\/Shanghai|secret-canvas-digest/);
assert.doesNotMatch(JSON.stringify(parsed), /Asia\/Shanghai|secret-canvas-digest/);
});
it("compares a pasted fingerprint ID against the current snapshot", async () => {
const storage = memoryStorage();
const previous = await buildTrackingPayload(
{
result,
localFacts: [{ id: "canvas-hash", value: "canvas-a" }],
},
{
storage,
randomId: () => "return-key-compare",
},
);
const current = await buildTrackingPayload(
{
result,
localFacts: [{ id: "canvas-hash", value: "canvas-b" }],
},
{ storage },
);
const comparison = compareFingerprintSnapshots(
current,
parseFingerprintIdToken(createFingerprintIdToken(previous)),
);
assert.equal(comparison.exactMatch, false);
assert.equal(comparison.changedIds.includes("fact-canvas-hash"), true);
assert.equal(comparison.stableComponents > 0, true);
assert.equal(comparison.similarity < 1, true);
});
it("creates a share card that is useful without exposing the full token", async () => {
const payload = await buildTrackingPayload(
{
result,
localFacts: [{ id: "canvas-hash", value: "canvas-a" }],
},
{
storage: memoryStorage(),
randomId: () => "return-key-card",
},
);
const token = createFingerprintIdToken(payload);
const card = createFingerprintShareCard({
copy: getMessages("zh"),
token,
comparison: {
exactMatch: true,
similarity: 1,
changedComponents: 0,
stableComponents: payload.components.length,
componentCount: payload.components.length,
verdict: "exact_repeat",
},
url: "https://example.com",
});
assert.match(card, /我的浏览器指纹/);
assert.match(card, new RegExp(fingerprintIdPreview(payload)));
assert.match(card, /https:\/\/example\.com/);
assert.doesNotMatch(card, new RegExp(token));
});
});