diff --git a/src/chrome/profile-manager.ts b/src/chrome/profile-manager.ts index 1f8bb56..3c8a5f1 100644 --- a/src/chrome/profile-manager.ts +++ b/src/chrome/profile-manager.ts @@ -197,6 +197,25 @@ export class ProfileManager { return false; } + // Guard: if the persistent profile's Cookies file has been modified after + // the last sync, a headless session wrote cookies — do not overwrite them. + const persistentCookiesPath = path.join( + ProfileManager.PERSISTENT_PROFILE_DIR, + profileSubdir, + 'Cookies' + ); + try { + const persistentStat = fs.statSync(persistentCookiesPath); + if (persistentStat.mtimeMs > metadata.lastSyncTimestamp) { + console.error( + '[ProfileManager] Persistent profile cookies modified after last sync — skipping overwrite to preserve headless-acquired session' + ); + return false; + } + } catch { + // Persistent Cookies file doesn't exist yet — sync is needed + } + if (currentHash !== metadata.sourceProfileHash) { return true; // Source has changed } diff --git a/tests/chrome/persistent-profile.test.ts b/tests/chrome/persistent-profile.test.ts index 8954c21..fcf7c5f 100644 --- a/tests/chrome/persistent-profile.test.ts +++ b/tests/chrome/persistent-profile.test.ts @@ -180,6 +180,35 @@ describe('ProfileManager', () => { const manager = new ProfileManager(); expect(manager.needsSync(sourceDir)).toBe(false); }); + + it('should return false when persistent profile Cookies were modified after last sync (headless guard)', () => { + // Set up source Cookies file and compute its hash + const cookiesPath = path.join(sourceDir, 'Default', 'Cookies'); + fs.writeFileSync(cookiesPath, 'source-cookie-data'); + const stat = fs.statSync(cookiesPath); + const hash = `${stat.mtimeMs}:${stat.size}`; + + // Write metadata with an old timestamp (simulating last sync happened in the past) + const oldTimestamp = Date.now() - (40 * 60 * 1000); // 40 minutes ago + const metadata: SyncMetadata = { + lastSyncTimestamp: oldTimestamp, + sourceProfileHash: hash, + syncCount: 1, + sourceProfileDir: sourceDir, + }; + fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(metadata)); + + // Create the persistent profile's Cookies file with a recent mtime + // (simulating a headless session that wrote cookies after the last sync) + const persistentCookiesDir = path.join(ProfileManager.PERSISTENT_PROFILE_DIR, 'Default'); + fs.mkdirSync(persistentCookiesDir, { recursive: true }); + fs.writeFileSync(path.join(persistentCookiesDir, 'Cookies'), 'headless-acquired-cookies'); + // The persistent Cookies file was just written — its mtime is newer than oldTimestamp + + const manager = new ProfileManager(); + // Should NOT sync: persistent cookies were written after the last sync by a headless session + expect(manager.needsSync(sourceDir)).toBe(false); + }); }); // =========================================================================