From 7d318d189aab1e92b4c06b692e7a9556e9669c39 Mon Sep 17 00:00:00 2001 From: DYSfu <24732693+DYSfu@users.noreply.github.com> Date: Sun, 24 May 2026 19:09:29 +0800 Subject: [PATCH] fix: reject zero viewport dimensions in fingerprint records --- .../src/record-schema.ts | 8 +- .../generator-networks-creator.test.ts | 117 +++++++++++++++++- 2 files changed, 120 insertions(+), 5 deletions(-) diff --git a/packages/generator-networks-creator/src/record-schema.ts b/packages/generator-networks-creator/src/record-schema.ts index bb572359..da75bcb1 100644 --- a/packages/generator-networks-creator/src/record-schema.ts +++ b/packages/generator-networks-creator/src/record-schema.ts @@ -247,10 +247,10 @@ export async function getRecordSchema() { height: z.number().positive(), availWidth: z.number().positive(), availHeight: z.number().positive(), - clientWidth: z.number().nonnegative(), - clientHeight: z.number().nonnegative(), - innerWidth: z.number().nonnegative(), - innerHeight: z.number().nonnegative(), + clientWidth: z.number().positive(), + clientHeight: z.number().positive(), + innerWidth: z.number().positive(), + innerHeight: z.number().positive(), outerWidth: z.number().positive(), outerHeight: z.number().positive(), colorDepth: z.number().positive().optional(), diff --git a/test/generator-networks-creator/generator-networks-creator.test.ts b/test/generator-networks-creator/generator-networks-creator.test.ts index 7370069f..c0ead18c 100644 --- a/test/generator-networks-creator/generator-networks-creator.test.ts +++ b/test/generator-networks-creator/generator-networks-creator.test.ts @@ -1,6 +1,12 @@ import { GeneratorNetworksCreator } from 'generator-networks-creator'; -import { describe, expect, test } from 'vitest'; +import { describe, expect, test, vi } from 'vitest'; + +import { getRecordSchema } from '../../packages/generator-networks-creator/src/record-schema'; + +vi.mock('node-fetch', () => ({ + default: vi.fn(async () => ({ json: async () => [] })), +})); describe('Processing browser data', () => { const networkGenerator = new GeneratorNetworksCreator(); @@ -168,3 +174,112 @@ describe('Processing browser data', () => { } }); }); + +describe('Browser fingerprint record schema', () => { + const userAgent = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36'; + + const createRecord = () => ({ + requestFingerprint: { + httpVersion: '2', + headers: { + 'user-agent': userAgent, + 'accept-language': 'en-US', + }, + }, + browserFingerprint: { + webdriver: false, + appVersion: userAgent, + hardwareConcurrency: 8, + multimediaDevices: { + speakers: [], + micros: [], + webcams: [], + }, + extraProperties: {}, + plugins: [], + mimeTypes: [], + deviceMemory: 8, + oscpu: null, + battery: null, + platform: 'MacIntel', + doNotTrack: null, + audioCodecs: { + ogg: 'probably', + mp3: 'probably', + wav: 'probably', + m4a: 'maybe', + aac: 'probably', + }, + videoCodecs: { + ogg: 'probably', + h264: 'probably', + webm: 'probably', + }, + userAgentData: { + brands: [ + { brand: 'Chromium', version: '100' }, + { brand: 'Google Chrome', version: '100' }, + ], + mobile: false, + platform: 'macOS', + }, + language: 'en-US', + languages: ['en-US'], + product: 'Gecko', + appName: 'Netscape', + appCodeName: 'Mozilla', + maxTouchPoints: 0, + productSub: '20030107', + vendor: 'Google Inc.', + vendorSub: '', + videoCard: { + renderer: + 'ANGLE (ATI Technologies Inc., AMD Radeon Pro 455 OpenGL Engine, OpenGL 4.1)', + vendor: 'Google Inc. (ATI Technologies Inc.)', + }, + fonts: [], + screen: { + availTop: 25, + availLeft: 0, + pageXOffset: 0, + pageYOffset: 0, + screenX: 0, + hasHDR: false, + width: 1440, + height: 900, + availWidth: 1440, + availHeight: 875, + clientWidth: 1284, + clientHeight: 858, + innerWidth: 1284, + innerHeight: 858, + outerWidth: 1284, + outerHeight: 858, + colorDepth: 24, + pixelDepth: 24, + devicePixelRatio: 2, + }, + userAgent, + }, + }); + + test('accepts records with non-zero viewport dimensions', async () => { + const recordSchema = await getRecordSchema(); + + expect(recordSchema.safeParse(createRecord()).success).toBe(true); + }); + + test.each(['clientWidth', 'clientHeight', 'innerWidth', 'innerHeight'])( + 'rejects zero %s in screen records', + async (screenProperty) => { + const recordSchema = await getRecordSchema(); + const record = createRecord(); + record.browserFingerprint.screen[ + screenProperty as keyof typeof record.browserFingerprint.screen + ] = 0 as never; + + expect(recordSchema.safeParse(record).success).toBe(false); + }, + ); +});