Feat/browser privacy consent (#21119)

This commit is contained in:
Aditya Bijalwan
2026-03-19 01:03:24 +05:30
committed by GitHub
parent 0082e1ec97
commit b6d5374fb7
4 changed files with 271 additions and 0 deletions

View File

@@ -44,6 +44,11 @@ vi.mock('../../utils/debugLogger.js', () => ({
},
}));
// Mock browser consent to always grant consent by default
vi.mock('../../utils/browserConsent.js', () => ({
getBrowserConsentIfNeeded: vi.fn().mockResolvedValue(true),
}));
vi.mock('./automationOverlay.js', () => ({
injectAutomationOverlay: vi.fn().mockResolvedValue(undefined),
}));
@@ -64,6 +69,7 @@ vi.mock('node:fs', async (importOriginal) => {
import * as fs from 'node:fs';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { getBrowserConsentIfNeeded } from '../../utils/browserConsent.js';
describe('BrowserManager', () => {
let mockConfig: Config;
@@ -72,6 +78,9 @@ describe('BrowserManager', () => {
vi.resetAllMocks();
vi.mocked(injectAutomationOverlay).mockClear();
// Re-establish consent mock after resetAllMocks
vi.mocked(getBrowserConsentIfNeeded).mockResolvedValue(true);
// Setup mock config
mockConfig = makeFakeConfig({
agents: {
@@ -527,6 +536,41 @@ describe('BrowserManager', () => {
/sessionMode: persistent/,
);
});
it('should pass --no-usage-statistics and --no-performance-crux when privacy is disabled', async () => {
const privacyDisabledConfig = makeFakeConfig({
agents: {
overrides: {
browser_agent: {
enabled: true,
},
},
browser: {
headless: false,
},
},
usageStatisticsEnabled: false,
});
const manager = new BrowserManager(privacyDisabledConfig);
await manager.ensureConnection();
const args = vi.mocked(StdioClientTransport).mock.calls[0]?.[0]
?.args as string[];
expect(args).toContain('--no-usage-statistics');
expect(args).toContain('--no-performance-crux');
});
it('should NOT pass privacy flags when usage statistics are enabled', async () => {
// Default config has usageStatisticsEnabled: true (or undefined)
const manager = new BrowserManager(mockConfig);
await manager.ensureConnection();
const args = vi.mocked(StdioClientTransport).mock.calls[0]?.[0]
?.args as string[];
expect(args).not.toContain('--no-usage-statistics');
expect(args).not.toContain('--no-performance-crux');
});
});
describe('MCP isolation', () => {

View File

@@ -23,6 +23,7 @@ import type { Tool as McpTool } from '@modelcontextprotocol/sdk/types.js';
import { debugLogger } from '../../utils/debugLogger.js';
import type { Config } from '../../config/config.js';
import { Storage } from '../../config/storage.js';
import { getBrowserConsentIfNeeded } from '../../utils/browserConsent.js';
import { injectInputBlocker } from './inputBlocker.js';
import * as path from 'node:path';
import * as fs from 'node:fs';
@@ -260,6 +261,16 @@ export class BrowserManager {
if (this.rawMcpClient) {
return;
}
// Request browser consent if needed (first-run privacy notice)
const consentGranted = await getBrowserConsentIfNeeded();
if (!consentGranted) {
throw new Error(
'Browser agent requires user consent to proceed. ' +
'Please re-run and accept the privacy notice.',
);
}
await this.connectMcp();
}
@@ -352,6 +363,11 @@ export class BrowserManager {
mcpArgs.push('--userDataDir', defaultProfilePath);
}
// Respect the user's privacy.usageStatisticsEnabled setting
if (!this.config.getUsageStatisticsEnabled()) {
mcpArgs.push('--no-usage-statistics', '--no-performance-crux');
}
if (
browserConfig.customConfig.allowedDomains &&
browserConfig.customConfig.allowedDomains.length > 0