From 0faa8dfc8bc4408c813032107a509917b192c38d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Mar 2026 05:01:12 +0000 Subject: [PATCH 1/3] Enhance browser detection with precise environment information - Replace hardcoded browser info with navigator.userAgentData API to get real brand, full version (e.g. 122.0.6261.112), OS platform and version - Remove deprecated navigator.platform and raw userAgent dump - Add screen resolution, browser language, timezone and cookies to session data - Make getSystemInfo() async to support high-entropy API values - Update background.js to await getSystemInfo() and use brand for filenames - Update HTML report to display all 8 new fields with backwards compatibility for sessions stored with the old format (browser/browserVersion fields) - Update jest mocks and browserInfo.test.js to match new async API https://claude.ai/code/session_013y1QH2gkj8wg427JpzjdJe --- HTMLReport/modules/reportUI.js | 27 ++++++++++++++-- background.js | 6 ++-- jest.setup.js | 33 ++++++++++++++++++- src/Session.js | 12 +------ src/browserInfo.js | 58 +++++++++++++++++++++++++++++----- test/spec/browserInfo.test.js | 52 +++++++++++++++--------------- 6 files changed, 138 insertions(+), 50 deletions(-) diff --git a/HTMLReport/modules/reportUI.js b/HTMLReport/modules/reportUI.js index 0233bc8..f0a2965 100644 --- a/HTMLReport/modules/reportUI.js +++ b/HTMLReport/modules/reportUI.js @@ -20,6 +20,13 @@ export function displaySessionInfo(session) { const browserInfo = session.getBrowserInfo(); const startDateTime = session.getStartDateTime(); + const brand = browserInfo.brand || browserInfo.browser || 'N/A'; + const model = browserInfo.model || ''; + const browserLabel = model ? `${brand} (${model})` : brand; + const os = browserInfo.os || 'N/A'; + const osVersion = browserInfo.osVersion || ''; + const osLabel = osVersion ? `${os} ${osVersion}` : os; + sessionInfo.innerHTML = `
Start Date @@ -27,11 +34,27 @@ export function displaySessionInfo(session) {
Browser - ${browserInfo.browser} ${browserInfo.browserVersion} + ${browserLabel} +
+
+ Version + ${browserInfo.browserVersion || 'N/A'}
Operating System - ${browserInfo.os} + ${osLabel} +
+
+ Resolution + ${browserInfo.screenResolution || 'N/A'} +
+
+ Language + ${browserInfo.language || 'N/A'} +
+
+ Timezone + ${browserInfo.timezone || 'N/A'}
Cookies diff --git a/background.js b/background.js index 7cc7b40..18afd87 100644 --- a/background.js +++ b/background.js @@ -504,7 +504,7 @@ async function addAnnotation(type, name, imageURL) { } async function startSession() { - var systemInfo = getSystemInfo(); + var systemInfo = await getSystemInfo(); session = new Session(Date.now(), systemInfo); await saveSession(); } @@ -521,7 +521,7 @@ function exportSessionCSV() { var csvData = exportService.getCSVData(); var browserInfo = session.getBrowserInfo(); - var browserInfoString = browserInfo.browser + "_" + browserInfo.browserVersion; + var browserInfoString = (browserInfo.brand || browserInfo.browser || 'Chrome') + "_" + browserInfo.browserVersion; // Formatear la fecha correctamente const date = new Date(session.getStartDateTime()); @@ -552,7 +552,7 @@ function exportSessionJSon() { var jsonData = exportJSonService.getJSon(session); var browserInfo = session.getBrowserInfo(); - var browserInfoString = browserInfo.browser + "_" + browserInfo.browserVersion; + var browserInfoString = (browserInfo.brand || browserInfo.browser || 'Chrome') + "_" + browserInfo.browserVersion; // Formatear la fecha correctamente const date = new Date(session.getStartDateTime()); diff --git a/jest.setup.js b/jest.setup.js index 382f5cf..047e5f8 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -22,10 +22,41 @@ global.chrome = { // Mock navigator properties used in browserInfo.js global.navigator = { - ...global.navigator, // Preserve existing navigator properties if any + ...global.navigator, platform: 'TestPlatform', userAgent: 'TestUserAgent/1.0', cookieEnabled: true, + language: 'es-ES', + userAgentData: { + platform: 'Windows', + brands: [ + { brand: 'Google Chrome', version: '122' }, + { brand: 'Chromium', version: '122' }, + { brand: 'Not A;Brand', version: '99' } + ], + getHighEntropyValues: jest.fn(() => Promise.resolve({ + fullVersionList: [ + { brand: 'Google Chrome', version: '122.0.6261.112' }, + { brand: 'Chromium', version: '122.0.6261.112' }, + { brand: 'Not A;Brand', version: '99.0.0.0' } + ], + platformVersion: '10.0', + model: '' + })) + } +}; + +// Mock screen properties +global.screen = { + width: 1920, + height: 1080 +}; + +// Mock Intl +global.Intl = { + DateTimeFormat: () => ({ + resolvedOptions: () => ({ timeZone: 'Europe/Madrid' }) + }) }; diff --git a/src/Session.js b/src/Session.js index 353e7d5..965cbee 100644 --- a/src/Session.js +++ b/src/Session.js @@ -1,17 +1,7 @@ import { Bug, Note, Idea, Question } from './Annotation.js'; -import { getSystemInfo } from './browserInfo.js'; - export class Session { constructor(dateTime, browserInfo) { - // Check if provided browserInfo is sufficiently complete - if (browserInfo && typeof browserInfo.browser === 'string' && browserInfo.browser !== '' && - typeof browserInfo.browserVersion === 'string' && browserInfo.browserVersion !== '' && - typeof browserInfo.os === 'string' && browserInfo.os !== '') { - this.BrowserInfo = browserInfo; - } else { - // If browserInfo is missing, null, undefined, or incomplete, get current system info - this.BrowserInfo = getSystemInfo(); - } + this.BrowserInfo = (browserInfo && typeof browserInfo === 'object') ? browserInfo : {}; this.StartDateTime = dateTime || Date.now(); // Provide a fallback for dateTime this.annotations = []; } diff --git a/src/browserInfo.js b/src/browserInfo.js index 49e2410..18f95cf 100644 --- a/src/browserInfo.js +++ b/src/browserInfo.js @@ -1,10 +1,52 @@ -export function getSystemInfo() { +export async function getSystemInfo() { + let brand = 'Chrome'; + let model = ''; + let browserVersion = ''; + let os = ''; + let osVersion = ''; + + if (navigator.userAgentData) { + os = navigator.userAgentData.platform || ''; + + try { + const highEntropy = await navigator.userAgentData.getHighEntropyValues([ + 'fullVersionList', + 'platformVersion', + 'model' + ]); + + const brands = highEntropy.fullVersionList || navigator.userAgentData.brands || []; + const significantBrand = brands.find(b => + !b.brand.includes('Not A') && b.brand !== 'Chromium' + ); + if (significantBrand) { + brand = significantBrand.brand; + browserVersion = significantBrand.version; + } + + osVersion = highEntropy.platformVersion || ''; + model = highEntropy.model || ''; + } catch (e) { + const brands = navigator.userAgentData.brands || []; + const significantBrand = brands.find(b => + !b.brand.includes('Not A') && b.brand !== 'Chromium' + ); + if (significantBrand) { + brand = significantBrand.brand; + browserVersion = significantBrand.version; + } + } + } + return { - browser: "Chrome", - browserVersion: chrome.runtime.getManifest().version, - os: navigator.platform, - osVersion: navigator.userAgent, - cookies: navigator.cookieEnabled, - flashVersion: "N/A" // Flash ya no se usa en navegadores modernos + brand, + model, + browserVersion, + os, + osVersion, + screenResolution: `${screen.width} × ${screen.height}`, + language: navigator.language, + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + cookies: navigator.cookieEnabled }; -} \ No newline at end of file +} diff --git a/test/spec/browserInfo.test.js b/test/spec/browserInfo.test.js index 86af9e3..22d2923 100644 --- a/test/spec/browserInfo.test.js +++ b/test/spec/browserInfo.test.js @@ -3,10 +3,8 @@ import { getSystemInfo } from '../../src/browserInfo'; describe('getSystemInfo', () => { let systemInfo; - beforeAll(() => { - // getSystemInfo relies on global mocks set in jest.setup.js - // We can call it once if the global mocks are static for these tests - systemInfo = getSystemInfo(); + beforeAll(async () => { + systemInfo = await getSystemInfo(); }); it('should return an object', () => { @@ -15,42 +13,46 @@ describe('getSystemInfo', () => { }); it('should contain all expected keys', () => { - expect(systemInfo).toHaveProperty('browser'); + expect(systemInfo).toHaveProperty('brand'); + expect(systemInfo).toHaveProperty('model'); expect(systemInfo).toHaveProperty('browserVersion'); expect(systemInfo).toHaveProperty('os'); expect(systemInfo).toHaveProperty('osVersion'); + expect(systemInfo).toHaveProperty('screenResolution'); + expect(systemInfo).toHaveProperty('language'); + expect(systemInfo).toHaveProperty('timezone'); expect(systemInfo).toHaveProperty('cookies'); - expect(systemInfo).toHaveProperty('flashVersion'); }); - it('should retrieve browser name correctly', () => { - expect(systemInfo.browser).toBe('Chrome'); // Hardcoded in function + it('should retrieve browser brand from userAgentData', () => { + expect(systemInfo.brand).toBe('Google Chrome'); }); - it('should retrieve browser version from chrome.runtime.getManifest', () => { - // Assuming jest.setup.js mocks chrome.runtime.getManifest().version to '1.0.0' - expect(systemInfo.browserVersion).toBe('1.0.0'); + it('should retrieve full browser version from userAgentData', () => { + expect(systemInfo.browserVersion).toBe('122.0.6261.112'); }); - it('should retrieve OS platform from navigator.platform', () => { - // Assuming jest.setup.js mocks navigator.platform to 'TestPlatform' - expect(systemInfo.os).toBe('TestPlatform'); + it('should retrieve OS platform from userAgentData', () => { + expect(systemInfo.os).toBe('Windows'); }); - it('should retrieve OS version from navigator.userAgent', () => { - // Assuming jest.setup.js mocks navigator.userAgent to 'TestUserAgent/1.0' - // The function extracts this specifically, so the test should reflect that. - // If getSystemInfo is more complex, this might need adjustment. - // For now, assuming it directly uses navigator.userAgent for osVersion. - expect(systemInfo.osVersion).toBe('TestUserAgent/1.0'); + it('should retrieve OS version from userAgentData high entropy values', () => { + expect(systemInfo.osVersion).toBe('10.0'); }); - it('should retrieve cookie status from navigator.cookieEnabled', () => { - // Assuming jest.setup.js mocks navigator.cookieEnabled to true - expect(systemInfo.cookies).toBe(true); + it('should retrieve screen resolution from screen dimensions', () => { + expect(systemInfo.screenResolution).toBe('1920 × 1080'); + }); + + it('should retrieve language from navigator.language', () => { + expect(systemInfo.language).toBe('es-ES'); }); - it('should report Flash version as N/A', () => { - expect(systemInfo.flashVersion).toBe('N/A'); // Hardcoded in function + it('should retrieve timezone from Intl', () => { + expect(systemInfo.timezone).toBe('Europe/Madrid'); + }); + + it('should retrieve cookie status from navigator.cookieEnabled', () => { + expect(systemInfo.cookies).toBe(true); }); }); From 7aefd13050361d54c70be72a52a322a7e0798a44 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Mar 2026 05:25:16 +0000 Subject: [PATCH 2/3] Fix: guard screen access for service worker context The background.js service worker does not have access to the window screen object. Accessing screen.width threw a ReferenceError which silently prevented startSession() from completing and blocked all annotation saves. Added typeof screen !== 'undefined' guard so screenResolution falls back to 'N/A' when running in service worker context. https://claude.ai/code/session_013y1QH2gkj8wg427JpzjdJe --- src/browserInfo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browserInfo.js b/src/browserInfo.js index 18f95cf..a31c82a 100644 --- a/src/browserInfo.js +++ b/src/browserInfo.js @@ -44,7 +44,7 @@ export async function getSystemInfo() { browserVersion, os, osVersion, - screenResolution: `${screen.width} × ${screen.height}`, + screenResolution: (typeof screen !== 'undefined' && screen.width) ? `${screen.width} × ${screen.height}` : 'N/A', language: navigator.language, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, cookies: navigator.cookieEnabled From 49a3116b502c79250fc7874438e06a1cfce049b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Mar 2026 05:32:52 +0000 Subject: [PATCH 3/3] Fix browser info: correct brand detection and remove unreliable fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix getFullSession handler to pass BrowserInfo directly instead of mapping to old format (this was causing all new fields to be lost) - Fix brand detection: match real browser by comparing major version against Chromium's version, avoiding randomized fake brand names - Remove screenResolution, language, timezone and cookies from getSystemInfo() — these cannot be obtained reliably from service worker - Update reportUI.js to display only: start date, browser, version, OS - Remove unnecessary screen/Intl mocks from jest.setup.js https://claude.ai/code/session_013y1QH2gkj8wg427JpzjdJe --- HTMLReport/modules/reportUI.js | 16 ---------------- background.js | 9 +-------- jest.setup.js | 14 -------------- src/browserInfo.js | 31 ++++++++++++++++++++++--------- test/spec/browserInfo.test.js | 28 ++++++++-------------------- 5 files changed, 31 insertions(+), 67 deletions(-) diff --git a/HTMLReport/modules/reportUI.js b/HTMLReport/modules/reportUI.js index f0a2965..76457b9 100644 --- a/HTMLReport/modules/reportUI.js +++ b/HTMLReport/modules/reportUI.js @@ -44,22 +44,6 @@ export function displaySessionInfo(session) { Operating System ${osLabel}
-
- Resolution - ${browserInfo.screenResolution || 'N/A'} -
-
- Language - ${browserInfo.language || 'N/A'} -
-
- Timezone - ${browserInfo.timezone || 'N/A'} -
-
- Cookies - ${browserInfo.cookies ? 'Enabled' : 'Disabled'} -
`; } diff --git a/background.js b/background.js index 18afd87..33d0b0b 100644 --- a/background.js +++ b/background.js @@ -311,14 +311,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { } sendResponse({ startDateTime: session.StartDateTime, - browserInfo: { - browser: session.BrowserInfo.browser || "Chrome", - browserVersion: session.BrowserInfo.browserVersion || chrome.runtime.getManifest().version, - os: session.BrowserInfo.os || navigator.platform, - osVersion: session.BrowserInfo.osVersion || navigator.userAgent, - cookies: session.BrowserInfo.cookies || navigator.cookieEnabled, - flashVersion: session.BrowserInfo.flashVersion || "N/A" - }, + browserInfo: session.BrowserInfo, annotations: session.annotations.map(annotation => ({ type: annotation.constructor.name, name: annotation.name, diff --git a/jest.setup.js b/jest.setup.js index 047e5f8..abcb541 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -46,20 +46,6 @@ global.navigator = { } }; -// Mock screen properties -global.screen = { - width: 1920, - height: 1080 -}; - -// Mock Intl -global.Intl = { - DateTimeFormat: () => ({ - resolvedOptions: () => ({ timeZone: 'Europe/Madrid' }) - }) -}; - - // Attempt to load the custom date.js library. // IMPORTANT: This path is relative to the project root. // Ensure 'lib/date.js' exists and this path is correct. diff --git a/src/browserInfo.js b/src/browserInfo.js index a31c82a..49b21f3 100644 --- a/src/browserInfo.js +++ b/src/browserInfo.js @@ -1,5 +1,5 @@ export async function getSystemInfo() { - let brand = 'Chrome'; + let brand = ''; let model = ''; let browserVersion = ''; let os = ''; @@ -15,25 +15,42 @@ export async function getSystemInfo() { 'model' ]); - const brands = highEntropy.fullVersionList || navigator.userAgentData.brands || []; + const brands = highEntropy.fullVersionList || []; + + // Find Chromium's version to use as reference for the real browser version + const chromiumEntry = brands.find(b => b.brand === 'Chromium'); + const chromiumMajor = chromiumEntry ? chromiumEntry.version.split('.')[0] : null; + + // Real browser brand: not Chromium, and has the same major version as Chromium const significantBrand = brands.find(b => - !b.brand.includes('Not A') && b.brand !== 'Chromium' + b.brand !== 'Chromium' && + chromiumMajor !== null && + b.version.split('.')[0] === chromiumMajor ); + if (significantBrand) { brand = significantBrand.brand; browserVersion = significantBrand.version; + } else if (chromiumEntry) { + brand = 'Chrome'; + browserVersion = chromiumEntry.version; } osVersion = highEntropy.platformVersion || ''; model = highEntropy.model || ''; } catch (e) { const brands = navigator.userAgentData.brands || []; + const chromiumEntry = brands.find(b => b.brand === 'Chromium'); + const chromiumMajor = chromiumEntry ? chromiumEntry.version : null; const significantBrand = brands.find(b => - !b.brand.includes('Not A') && b.brand !== 'Chromium' + b.brand !== 'Chromium' && b.version === chromiumMajor ); if (significantBrand) { brand = significantBrand.brand; browserVersion = significantBrand.version; + } else if (chromiumEntry) { + brand = 'Chrome'; + browserVersion = chromiumEntry.version; } } } @@ -43,10 +60,6 @@ export async function getSystemInfo() { model, browserVersion, os, - osVersion, - screenResolution: (typeof screen !== 'undefined' && screen.width) ? `${screen.width} × ${screen.height}` : 'N/A', - language: navigator.language, - timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, - cookies: navigator.cookieEnabled + osVersion }; } diff --git a/test/spec/browserInfo.test.js b/test/spec/browserInfo.test.js index 22d2923..c72e13d 100644 --- a/test/spec/browserInfo.test.js +++ b/test/spec/browserInfo.test.js @@ -18,17 +18,14 @@ describe('getSystemInfo', () => { expect(systemInfo).toHaveProperty('browserVersion'); expect(systemInfo).toHaveProperty('os'); expect(systemInfo).toHaveProperty('osVersion'); - expect(systemInfo).toHaveProperty('screenResolution'); - expect(systemInfo).toHaveProperty('language'); - expect(systemInfo).toHaveProperty('timezone'); - expect(systemInfo).toHaveProperty('cookies'); }); - it('should retrieve browser brand from userAgentData', () => { + it('should retrieve real browser brand (not Chromium or fake brand)', () => { expect(systemInfo.brand).toBe('Google Chrome'); + expect(systemInfo.brand).not.toBe('Chromium'); }); - it('should retrieve full browser version from userAgentData', () => { + it('should retrieve full browser version matching Chromium version', () => { expect(systemInfo.browserVersion).toBe('122.0.6261.112'); }); @@ -40,19 +37,10 @@ describe('getSystemInfo', () => { expect(systemInfo.osVersion).toBe('10.0'); }); - it('should retrieve screen resolution from screen dimensions', () => { - expect(systemInfo.screenResolution).toBe('1920 × 1080'); - }); - - it('should retrieve language from navigator.language', () => { - expect(systemInfo.language).toBe('es-ES'); - }); - - it('should retrieve timezone from Intl', () => { - expect(systemInfo.timezone).toBe('Europe/Madrid'); - }); - - it('should retrieve cookie status from navigator.cookieEnabled', () => { - expect(systemInfo.cookies).toBe(true); + it('should not include screenResolution, language, timezone or cookies', () => { + expect(systemInfo).not.toHaveProperty('screenResolution'); + expect(systemInfo).not.toHaveProperty('language'); + expect(systemInfo).not.toHaveProperty('timezone'); + expect(systemInfo).not.toHaveProperty('cookies'); }); });