]*data-type[^>]*>([^<]*)<\/span>/gi;
+ const pPattern = /]*>([^<]*<[^>]*sensitive-text[^>]*>[^<]*<\/[^>]*>[^<]*)<\/p>/gi;
+
+ // Combine all patterns
+ const allPatterns = [
+ { pattern: inputPattern, tag: 'input' },
+ { pattern: textareaPattern, tag: 'textarea' },
+ { pattern: selectPattern, tag: 'select' }
+ ];
+
+ allPatterns.forEach(({ pattern, tag }) => {
+ let match;
+ let index = 0;
+
+ while ((match = pattern.exec(html)) !== null) {
+ index++;
+ const elementHTML = match[0];
+
+ // Extract attributes using regex
+ const type = extractAttribute(elementHTML, 'type') || 'text';
+ const name = extractAttribute(elementHTML, 'name') || '';
+ const id = extractAttribute(elementHTML, 'id') || '';
+ const placeholder = extractAttribute(elementHTML, 'placeholder') || '';
+ const className = extractAttribute(elementHTML, 'class') || '';
+ const dataTestId = extractAttribute(elementHTML, 'data-testid') || '';
+ const ariaLabel = extractAttribute(elementHTML, 'aria-label') || '';
+ const role = extractAttribute(elementHTML, 'role') || '';
+
+ let detectedType = null;
+ let confidence = 0.0;
+ let reasoning = '';
+
+ // Enhanced detection patterns for modern web apps
+ const allAttributes = `${name} ${id} ${placeholder} ${className} ${dataTestId} ${ariaLabel} ${role}`.toLowerCase();
+
+ // Password detection
+ if (type === 'password' ||
+ allAttributes.includes('password') ||
+ allAttributes.includes('passwd') ||
+ allAttributes.includes('pwd')) {
+ detectedType = 'password';
+ confidence = 0.95;
+ reasoning = 'Password field detected by type, name, id, placeholder, or other attributes';
+ }
+ // Email detection
+ else if (type === 'email' ||
+ allAttributes.includes('email') ||
+ allAttributes.includes('mail') ||
+ allAttributes.includes('e-mail')) {
+ detectedType = 'email';
+ confidence = 0.95;
+ reasoning = 'Email field detected by type, name, id, placeholder, or other attributes';
+ }
+ // Phone detection
+ else if (type === 'tel' ||
+ allAttributes.includes('phone') ||
+ allAttributes.includes('mobile') ||
+ allAttributes.includes('cell') ||
+ allAttributes.includes('telephone')) {
+ detectedType = 'phone';
+ confidence = 0.90;
+ reasoning = 'Phone field detected by type, name, id, placeholder, or other attributes';
+ }
+ // Token detection
+ else if (allAttributes.includes('token') ||
+ allAttributes.includes('csrf') ||
+ allAttributes.includes('auth') ||
+ allAttributes.includes('session') ||
+ allAttributes.includes('script')) {
+ detectedType = 'token';
+ confidence = 0.90;
+ reasoning = 'Security token field detected by name, id, or other attributes';
+ }
+ // Username detection
+ else if (allAttributes.includes('username') ||
+ allAttributes.includes('user') ||
+ allAttributes.includes('login') ||
+ allAttributes.includes('account')) {
+ detectedType = 'name';
+ confidence = 0.85;
+ reasoning = 'Username field detected by name, id, or other attributes';
+ }
+ // Name detection
+ else if (allAttributes.includes('name') ||
+ allAttributes.includes('first') ||
+ allAttributes.includes('last') ||
+ allAttributes.includes('full')) {
+ detectedType = 'name';
+ confidence = 0.80;
+ reasoning = 'Name field detected by name, id, or other attributes';
+ }
+ // Address detection
+ else if (allAttributes.includes('address') ||
+ allAttributes.includes('street') ||
+ allAttributes.includes('city') ||
+ allAttributes.includes('state') ||
+ allAttributes.includes('zip') ||
+ allAttributes.includes('postal')) {
+ detectedType = 'address';
+ confidence = 0.85;
+ reasoning = 'Address field detected by name, id, or other attributes';
+ }
+ // Date of birth detection
+ else if (type === 'date' ||
+ allAttributes.includes('birth') ||
+ allAttributes.includes('dob') ||
+ allAttributes.includes('age') ||
+ allAttributes.includes('date')) {
+ detectedType = 'date_of_birth';
+ confidence = 0.85;
+ reasoning = 'Date of birth field detected by type, name, id, or other attributes';
+ }
+ // SSN detection
+ else if (allAttributes.includes('ssn') ||
+ allAttributes.includes('social') ||
+ allAttributes.includes('security')) {
+ detectedType = 'ssn';
+ confidence = 0.90;
+ reasoning = 'SSN field detected by name, id, or other attributes';
+ }
+ // Credit card detection
+ else if (allAttributes.includes('credit') ||
+ allAttributes.includes('card') ||
+ allAttributes.includes('cc') ||
+ allAttributes.includes('cvv') ||
+ allAttributes.includes('cvc')) {
+ detectedType = 'credit_card';
+ confidence = 0.90;
+ reasoning = 'Credit card field detected by name, id, or other attributes';
+ }
+ // Account number detection
+ else if (allAttributes.includes('account') ||
+ allAttributes.includes('bank') ||
+ allAttributes.includes('routing') ||
+ allAttributes.includes('iban')) {
+ detectedType = 'account_number';
+ confidence = 0.85;
+ reasoning = 'Account number field detected by name, id, or other attributes';
+ }
+ // Country detection
+ else if (allAttributes.includes('country') ||
+ allAttributes.includes('nation')) {
+ detectedType = 'location';
+ confidence = 0.80;
+ reasoning = 'Country field detected by name, id, or other attributes';
+ }
+
+ if (detectedType) {
+ // Generate a selector
+ let selector = '';
+ if (id) {
+ selector = `#${id}`;
+ } else if (name) {
+ selector = `${tag}[name="${name}"]`;
+ } else if (dataTestId) {
+ selector = `[data-testid="${dataTestId}"]`;
+ } else {
+ selector = `${tag}[type="${type}"]:nth-of-type(${index})`;
+ }
+
+ fields.push({
+ selector: selector,
+ type: detectedType,
+ confidence: confidence,
+ reasoning: reasoning,
+ context: `type="${type}" name="${name}" id="${id}" placeholder="${placeholder}" class="${className}" data-testid="${dataTestId}" aria-label="${ariaLabel}" role="${role}"`,
+ risk_level: 'high',
+ recommendation: 'Ensure proper encryption and secure transmission'
+ });
+ }
+ }
+ });
+
+ // NEW: Detect sensitive text content in regular HTML elements
+ console.log('๐ Scanning for sensitive text content...');
+
+ // Pattern 1: Look for elements with class="sensitive-text"
+ let textContentMatch;
+ let textContentIndex = 0;
+
+ while ((textContentMatch = textContentPattern.exec(html)) !== null) {
+ textContentIndex++;
+ const fullMatch = textContentMatch[0];
+ const textContent = textContentMatch[1] || '';
+
+ // Extract data-type attribute
+ const dataTypeMatch = fullMatch.match(/data-type="([^"]*)"/i);
+ const dataType = dataTypeMatch ? dataTypeMatch[1] : '';
+
+ // Extract id attribute
+ const idMatch = fullMatch.match(/id="([^"]*)"/i);
+ const id = idMatch ? idMatch[1] : '';
+
+ // Extract class attribute
+ const classMatch = fullMatch.match(/class="([^"]*)"/i);
+ const className = classMatch ? classMatch[1] : '';
+
+ if (textContent.trim() && dataType) {
+ let detectedType = dataType;
+ let confidence = 0.85;
+ let reasoning = `Sensitive text content detected with data-type="${dataType}"`;
+
+ // Enhance confidence based on content patterns
+ if (textContent.includes('@') && textContent.includes('.')) {
+ detectedType = 'email';
+ confidence = 0.95;
+ reasoning = 'Email address detected in text content';
+ } else if (textContent.match(/\d{3}[-.]?\d{3}[-.]?\d{4}/)) {
+ detectedType = 'phone';
+ confidence = 0.90;
+ reasoning = 'Phone number pattern detected in text content';
+ } else if (textContent.match(/\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}/)) {
+ detectedType = 'credit-card';
+ confidence = 0.95;
+ reasoning = 'Credit card number pattern detected in text content';
+ } else if (textContent.match(/\d{3}-\d{2}-\d{4}/)) {
+ detectedType = 'ssn';
+ confidence = 0.95;
+ reasoning = 'SSN pattern detected in text content';
+ }
+
+ // Generate more specific selector
+ let selector = '';
+ if (id) {
+ selector = `#${id}`;
+ } else if (dataType) {
+ selector = `.sensitive-text[data-type="${dataType}"]:nth-of-type(${textContentIndex})`;
+ } else {
+ selector = `.sensitive-text:nth-of-type(${textContentIndex})`;
+ }
+
+ fields.push({
+ selector: selector,
+ type: detectedType,
+ confidence: confidence,
+ reasoning: reasoning,
+ context: `data-type="${dataType}" class="${className}" text="${textContent.substring(0, 50)}"`,
+ risk_level: 'high',
+ recommendation: 'Mask sensitive text content to prevent exposure',
+ textContent: textContent,
+ isTextContent: true
+ });
+
+ console.log(`๐ฏ Detected sensitive text: ${detectedType} - ${textContent.substring(0, 30)}... (selector: ${selector})`);
+ }
+ }
+
+ // Pattern 2: Look for span elements with data-type attribute
+ let spanMatch;
+ let spanIndex = 0;
+
+ while ((spanMatch = spanPattern.exec(html)) !== null) {
+ spanIndex++;
+ const fullMatch = spanMatch[0];
+ const textContent = spanMatch[1] || '';
+
+ // Extract data-type attribute
+ const dataTypeMatch = fullMatch.match(/data-type="([^"]*)"/i);
+ const dataType = dataTypeMatch ? dataTypeMatch[1] : '';
+
+ // Extract id attribute
+ const idMatch = fullMatch.match(/id="([^"]*)"/i);
+ const id = idMatch ? idMatch[1] : '';
+
+ if (textContent.trim() && dataType) {
+ // Generate more specific selector for span elements
+ let selector = '';
+ if (id) {
+ selector = `#${id}`;
+ } else if (dataType) {
+ selector = `span[data-type="${dataType}"]:nth-of-type(${spanIndex})`;
+ } else {
+ selector = `span:nth-of-type(${spanIndex})`;
+ }
+
+ fields.push({
+ selector: selector,
+ type: dataType,
+ confidence: 0.90,
+ reasoning: `Sensitive text content detected in span with data-type="${dataType}"`,
+ context: `data-type="${dataType}" text="${textContent.substring(0, 50)}"`,
+ risk_level: 'high',
+ recommendation: 'Mask sensitive text content to prevent exposure',
+ textContent: textContent,
+ isTextContent: true
+ });
+
+ console.log(`๐ฏ Detected sensitive span text: ${dataType} - ${textContent.substring(0, 30)}... (selector: ${selector})`);
+ }
+ }
+
+ // Also check for contenteditable elements
+ const contentEditablePattern = /<[^>]*contenteditable="true"[^>]*>/gi;
+ let contentEditableMatch;
+ let contentEditableIndex = 0;
+
+ while ((contentEditableMatch = contentEditablePattern.exec(html)) !== null) {
+ contentEditableIndex++;
+ const elementHTML = contentEditableMatch[0];
+
+ const className = extractAttribute(elementHTML, 'class') || '';
+ const id = extractAttribute(elementHTML, 'id') || '';
+ const ariaLabel = extractAttribute(elementHTML, 'aria-label') || '';
+
+ const allAttributes = `${className} ${id} ${ariaLabel}`.toLowerCase();
+
+ if (allAttributes.includes('email') || allAttributes.includes('phone') ||
+ allAttributes.includes('address') || allAttributes.includes('name')) {
+
+ let detectedType = 'other';
+ if (allAttributes.includes('email')) detectedType = 'email';
+ else if (allAttributes.includes('phone')) detectedType = 'phone';
+ else if (allAttributes.includes('address')) detectedType = 'address';
+ else if (allAttributes.includes('name')) detectedType = 'name';
+
+ fields.push({
+ selector: `[contenteditable="true"]:nth-of-type(${contentEditableIndex})`,
+ type: detectedType,
+ confidence: 0.75,
+ reasoning: 'Contenteditable field detected as potentially sensitive',
+ context: `class="${className}" id="${id}" aria-label="${ariaLabel}"`,
+ risk_level: 'medium',
+ recommendation: 'Review contenteditable elements for sensitive data'
+ });
+ }
+ }
+
+ console.log(`๐ Total sensitive fields detected: ${fields.length} (including ${fields.filter(f => f.isTextContent).length} text content elements)`);
+
+ return fields;
+}
+
+// Helper function to extract attribute values from HTML
+function extractAttribute(html, attributeName) {
+ const pattern = new RegExp(`${attributeName}=["']([^"']*)["']`, 'i');
+ const match = html.match(pattern);
+ return match ? match[1] : '';
+}
+
+function logSensitiveFieldResults(sensitiveFields, pageInfo) {
+ console.log('๐ SENSITIVE FIELD DETECTION RESULTS:');
+ console.log('=' .repeat(60));
+ console.log('๐ Page:', pageInfo.url || 'Unknown');
+ console.log('๐ Title:', pageInfo.title || 'Unknown');
+ console.log('๐ฏ Total Fields Detected:', sensitiveFields.length);
+
+ // Separate form fields from text content
+ const formFields = sensitiveFields.filter(field => !field.isTextContent);
+ const textContentFields = sensitiveFields.filter(field => field.isTextContent);
+
+ console.log('๐ Form Fields:', formFields.length);
+ console.log('๐ Text Content Fields:', textContentFields.length);
+
+ // Show detection method breakdown if available
+ if (pageInfo.patternFields !== undefined && pageInfo.aiFields !== undefined) {
+ console.log('๐ Detection Method Breakdown:');
+ console.log(` ๐ฏ Pattern Detection: ${pageInfo.patternFields} fields`);
+ console.log(` ๐ง AI Detection: ${pageInfo.totalAIFields || pageInfo.aiFields} total fields`);
+ console.log(` ๐ NEW AI Fields: ${pageInfo.aiFields} fields`);
+ console.log(` ๐ Combined: ${sensitiveFields.length} fields`);
+ }
+
+ console.log('');
+
+ if (sensitiveFields.length === 0) {
+ console.log('โ
No sensitive fields detected - page appears secure');
+ return;
+ }
+
+ // Group by type
+ const fieldsByType = {};
+ const fieldsByRisk = { high: [], medium: [], low: [] };
+
+ sensitiveFields.forEach(field => {
+ // Group by type
+ if (!fieldsByType[field.type]) {
+ fieldsByType[field.type] = [];
+ }
+ fieldsByType[field.type].push(field);
+
+ // Group by risk level
+ fieldsByRisk[field.risk_level].push(field);
+ });
+
+ // Log summary by type
+ console.log('๐ DETECTION SUMMARY BY TYPE:');
+ Object.entries(fieldsByType).forEach(([type, fields]) => {
+ console.log(` ${type.toUpperCase()}: ${fields.length} fields`);
+ });
+
+ console.log('');
+ console.log('โ ๏ธ RISK LEVEL BREAKDOWN:');
+ console.log(` HIGH RISK: ${fieldsByRisk.high.length} fields`);
+ console.log(` MEDIUM RISK: ${fieldsByRisk.medium.length} fields`);
+ console.log(` LOW RISK: ${fieldsByRisk.low.length} fields`);
+
+ console.log('');
+ console.log('๐ DETAILED FIELD ANALYSIS:');
+ sensitiveFields.forEach((field, index) => {
+ const fieldType = field.isTextContent ? 'TEXT CONTENT' : 'FORM FIELD';
+ console.log(`\n${index + 1}. ${field.type.toUpperCase()} ${fieldType}:`);
+ console.log(` Selector: ${field.selector}`);
+ console.log(` Confidence: ${Math.round(field.confidence * 100)}%`);
+ console.log(` Risk Level: ${field.risk_level.toUpperCase()}`);
+ console.log(` Reasoning: ${field.reasoning}`);
+ console.log(` Context: ${field.context}`);
+ if (field.textContent) {
+ console.log(` Text Content: "${field.textContent}"`);
+ }
+ console.log(` Recommendation: ${field.recommendation}`);
+ });
+
+ console.log('');
+ console.log('๐ก๏ธ SECURITY RECOMMENDATIONS:');
+ if (fieldsByRisk.high.length > 0) {
+ console.log(' โ ๏ธ HIGH RISK FIELDS DETECTED - Immediate attention required');
+ fieldsByRisk.high.forEach(field => {
+ console.log(` - ${field.type}: ${field.recommendation}`);
+ });
+ }
+
+ if (fieldsByRisk.medium.length > 0) {
+ console.log(' ๐ถ MEDIUM RISK FIELDS - Review security measures');
+ }
+
+ console.log('=' .repeat(60));
+}
+
+// Helper function to call the masking API using Chrome extension API
+function executeMaskingScript(tabId, selectors, detectionType = 'unknown') {
+ console.log(`๐ญ Calling masking API with selectors (${detectionType}):`, selectors);
+
+ // Debug: Test a few selectors to see if they exist in the DOM
+ if (selectors.length > 0) {
+ console.log('๐ Testing first few selectors for DOM existence...');
+ const testSelectors = selectors.slice(0, 5);
+
+ chrome.scripting.executeScript({
+ target: { tabId: tabId },
+ func: (testSelectors) => {
+ console.log('๐ Testing selectors in page context:');
+ testSelectors.forEach(selector => {
+ const elements = document.querySelectorAll(selector);
+ console.log(` ${selector}: ${elements.length} elements found`);
+ if (elements.length > 0) {
+ console.log(` First element:`, elements[0]);
+ }
+ });
+ },
+ args: [testSelectors]
+ }, (results) => {
+ if (chrome.runtime.lastError) {
+ console.warn('โ ๏ธ Could not test selectors:', chrome.runtime.lastError);
+ } else {
+ console.log('โ
Selector testing completed');
+ }
+ });
+ }
+
+ // Try method 1: Direct Chrome API (if available)
+ if (chrome.wootz && chrome.wootz.maskSensitiveElements) {
+ console.log('โ
Using direct Chrome API: chrome.wootz.maskSensitiveElements');
+ chrome.wootz.maskSensitiveElements(selectors, (response) => {
+ if (chrome.runtime.lastError) {
+ console.error('โ Direct API masking failed:', chrome.runtime.lastError.message);
+ // Fallback to method 2
+ tryMessagingAPI(selectors);
+ } else {
+ console.log('โ
Direct API masking successful:', response);
+
+ // If no elements were masked, try to debug why
+ if (response.masked === 0 && selectors.length > 0) {
+ console.log('โ ๏ธ No elements masked, checking if selectors exist in DOM...');
+ chrome.scripting.executeScript({
+ target: { tabId: tabId },
+ func: (selectors) => {
+ console.log('๐ Checking selector existence for masking...');
+ let foundCount = 0;
+ selectors.forEach(selector => {
+ const elements = document.querySelectorAll(selector);
+ if (elements.length > 0) {
+ foundCount += elements.length;
+ console.log(` โ
${selector}: ${elements.length} elements found`);
+ } else {
+ console.log(` โ ${selector}: No elements found`);
+ }
+ });
+ console.log(`๐ Total elements found: ${foundCount} out of ${selectors.length} selectors`);
+ return { foundCount, totalSelectors: selectors.length };
+ },
+ args: [selectors]
+ }, (results) => {
+ if (results && results[0]) {
+ console.log('๐ Selector existence check result:', results[0].result);
+ }
+ });
+ }
+ }
+ });
+ } else {
+ console.log('โ ๏ธ Direct Chrome API not available, trying messaging API');
+ tryMessagingAPI(selectors);
+ }
+}
+
+// Fallback function using messaging API
+function tryMessagingAPI(selectors) {
+ console.log('๐ Trying messaging API for masking...');
+
+ chrome.runtime.sendMessage({
+ method: "wootz.maskSensitiveElements",
+ args: [selectors]
+ }, (response) => {
+ if (chrome.runtime.lastError) {
+ console.error('โ Messaging API masking failed:', chrome.runtime.lastError.message);
+ // Fallback to method 3: Execute script in tab
+ tryExecuteScriptAPI(selectors);
+ } else {
+ console.log('โ
Messaging API masking successful:', response);
+ }
+ });
+}
+
+// Final fallback: Execute script in tab
+function tryExecuteScriptAPI(selectors) {
+ console.log('๐ Trying execute script API as final fallback...');
+
+ chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
+ if (tabs && tabs[0]) {
+ chrome.scripting.executeScript({
+ target: { tabId: tabs[0].id },
+ func: (selectorsArray) => {
+ console.log('๐ญ Background script calling browser masking API with selectors:', selectorsArray);
+
+ if (window.WootzMaskSensitiveElementsFunction) {
+ window.WootzMaskSensitiveElementsFunction(selectorsArray, (result) => {
+ console.log('โ
Browser masking API completed, result:', result);
+ return result;
+ });
+ } else {
+ console.warn('โ ๏ธ WootzMaskSensitiveElementsFunction not available in browser');
+ return { success: false, error: 'Browser masking API not available' };
+ }
+ },
+ args: [selectors]
+ }, (results) => {
+ if (chrome.runtime.lastError) {
+ console.warn('โ ๏ธ Could not execute masking script:', chrome.runtime.lastError);
+ } else {
+ console.log('โ
Execute script masking successful, results:', results);
+ }
+ });
+ } else {
+ console.error('โ No active tab found for execute script fallback');
+ }
+ });
+}
+
+console.log('โ
Background script ready with enhanced AI classification');
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/background.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/background.js
new file mode 100644
index 000000000..899251902
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/background.js
@@ -0,0 +1,535 @@
+// Background service worker - Handles AI communication and extension management
+// Manages Gemini API integration, settings, and cross-tab communication
+
+class BackgroundService {
+ constructor() {
+ this.aiRequestQueue = [];
+ this.isProcessingAI = false;
+ this.settings = {
+ geminiApiKey: 'AIzaSyCoNFODrVovsQEFa4nseHbv0d56eMqhtDU', // Embedded API key
+ enableAI: true,
+ aiModel: 'gemini-1.5-pro-latest',
+ batchSize: 10,
+ requestDelay: 1000,
+ maxRetries: 3
+ };
+
+ this.statistics = {
+ totalRequests: 0,
+ successfulRequests: 0,
+ failedRequests: 0,
+ averageResponseTime: 0,
+ totalResponseTime: 0
+ };
+
+ this.initialize();
+ }
+
+ async initialize() {
+ console.log('๐ Background service starting...');
+
+ // Load settings
+ await this.loadSettings();
+
+ // Set up message listeners
+ this.setupMessageHandlers();
+
+ // Initialize extension state
+ this.initializeExtensionState();
+
+ console.log('โ
Background service ready');
+ }
+
+ async loadSettings() {
+ try {
+ // Settings loaded from sync storage
+ const stored = await chrome.storage.sync.get('extensionSettings');
+ if (stored.extensionSettings) {
+ this.settings = { ...this.settings, ...stored.extensionSettings };
+ }
+
+ // No need to load API key from storage - using embedded key
+ } catch (error) {
+ console.warn('Failed to load settings:', error);
+ }
+ }
+
+ async saveSettings() {
+ try {
+ await chrome.storage.sync.set({ extensionSettings: this.settings });
+ } catch (error) {
+ console.error('Failed to save settings:', error);
+ }
+ }
+
+ setupMessageHandlers() {
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
+ this.handleMessage(message, sender, sendResponse);
+ return true; // Keep message channel open for async response
+ });
+
+ // Handle extension installation/update
+ chrome.runtime.onInstalled.addListener((details) => {
+ this.handleInstallation(details);
+ });
+
+ // Handle browser startup
+ chrome.runtime.onStartup.addListener(() => {
+ this.handleStartup();
+ });
+
+ // Try programmatic injection as fallback
+ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
+ if (changeInfo.status === 'complete' && tab.url && !tab.url.startsWith('chrome://')) {
+ console.log('๐ง Trying programmatic injection for tab:', tab.url);
+ this.injectContentScript(tabId);
+ }
+ });
+ }
+
+ async injectContentScript(tabId) {
+ try {
+ await chrome.scripting.executeScript({
+ target: { tabId: tabId },
+ func: () => {
+ console.log('๐ PROGRAMMATICALLY INJECTED SCRIPT WORKING!');
+ console.log('๐ Page URL:', window.location.href);
+
+ // Add visual indicator
+ const div = document.createElement('div');
+ div.style.cssText = 'position:fixed;top:50px;left:0;width:100%;background:blue;color:white;padding:10px;z-index:99999;text-align:center;';
+ div.textContent = '๐ PROGRAMMATIC INJECTION WORKING!';
+ document.documentElement.appendChild(div);
+
+ setTimeout(() => {
+ div.remove();
+ }, 3000);
+ }
+ });
+ console.log('โ
Programmatic injection successful for tab:', tabId);
+ } catch (error) {
+ console.log('โ Programmatic injection failed:', error);
+ }
+ }
+
+ async handleMessage(message, sender, sendResponse) {
+ console.log('๐จ Background received message:', message.action, 'from tab:', sender?.tab?.id);
+ try {
+ switch (message.action) {
+ case 'analyzeWithAI':
+ console.log('๐ค Processing AI analysis request with data:', message.data);
+ const result = await this.analyzeWithAI(message.data);
+ console.log('โ
AI analysis complete:', result);
+ sendResponse({ success: true, result });
+ break;
+
+ case 'updateSettings':
+ console.log('โ๏ธ Updating settings:', message.settings);
+ await this.updateSettings(message.settings);
+ sendResponse({ success: true });
+ break;
+
+ case 'getSettings':
+ console.log('๐ Sending settings:', this.settings);
+ sendResponse({ success: true, settings: this.settings });
+ break;
+
+ case 'getStatistics':
+ const stats = this.getStatistics();
+ console.log('๐ Sending statistics:', stats);
+ sendResponse({ success: true, statistics: stats });
+ break;
+
+ case 'clearCache':
+ console.log('๐งน Clearing cache...');
+ await this.clearCache();
+ sendResponse({ success: true });
+ break;
+
+ case 'testAPIKey':
+ console.log('๐ Testing API key...');
+ const isValid = await this.testAPIKey(message.apiKey);
+ console.log('๐ API key test result:', isValid);
+ sendResponse({ success: true, valid: isValid });
+ break;
+
+ default:
+ console.warn('โ Unknown action:', message.action);
+ sendResponse({ success: false, error: 'Unknown action' });
+ }
+ } catch (error) {
+ console.error('โ Message handling error:', error);
+ sendResponse({ success: false, error: error.message });
+ }
+ }
+
+ async analyzeWithAI(batchData) {
+ if (!this.settings.enableAI || !this.settings.geminiApiKey) {
+ throw new Error('AI analysis disabled or API key not configured');
+ }
+
+ const startTime = performance.now();
+
+ try {
+ // Build prompt for Gemini
+ const prompt = this.buildGeminiPrompt(batchData);
+
+ // Check rate limiting
+ await this.enforceRateLimit();
+
+ // Make API request
+ const response = await this.callGeminiAPI(prompt);
+
+ // Parse and validate response
+ const result = this.parseGeminiResponse(response);
+
+ // Update statistics
+ this.updateStatistics(true, performance.now() - startTime);
+
+ return result;
+ } catch (error) {
+ this.updateStatistics(false, performance.now() - startTime);
+ throw error;
+ }
+ }
+
+ buildGeminiPrompt(batchData) {
+ const prompt = `You are an expert in identifying sensitive form fields for privacy and security purposes.
+
+TASK: Analyze the following anonymized form field metadata and identify which fields are likely to contain sensitive personal information.
+
+SENSITIVE FIELD TYPES:
+- Email addresses
+- Passwords and authentication codes
+- Phone numbers
+- Physical addresses
+- Names (first, last, full)
+- Dates of birth
+- Credit card information
+- Social security numbers
+- Government ID numbers
+- Financial account information
+- Medical information
+- Any other personally identifiable information (PII)
+
+INSTRUCTIONS:
+1. Analyze each field's metadata including tag, type, patterns, and context
+2. Consider the overall form structure and purpose
+3. Return ONLY a JSON object with the following structure:
+
+{
+ "sensitiveIndexes": [0, 2, 5],
+ "fieldTypes": ["email", "password", "phone"],
+ "confidence": 0.85,
+ "reasoning": "Brief explanation of detection logic"
+}
+
+Where:
+- sensitiveIndexes: Array of indexes (0-based) of sensitive fields
+- fieldTypes: Array of field types corresponding to each sensitive index
+- confidence: Overall confidence score (0.0 to 1.0)
+- reasoning: Brief explanation of why these fields were classified as sensitive
+
+METADATA TO ANALYZE:
+${JSON.stringify(batchData, null, 2)}
+
+Remember: Only identify fields that are clearly sensitive. When in doubt, err on the side of caution.
+
+RESPONSE (JSON only):`;
+
+ return prompt;
+ }
+
+ async enforceRateLimit() {
+ // Simple rate limiting - wait between requests
+ if (this.lastRequestTime) {
+ const timeSinceLastRequest = Date.now() - this.lastRequestTime;
+ if (timeSinceLastRequest < this.settings.requestDelay) {
+ await new Promise(resolve =>
+ setTimeout(resolve, this.settings.requestDelay - timeSinceLastRequest)
+ );
+ }
+ }
+ this.lastRequestTime = Date.now();
+ }
+
+ async callGeminiAPI(prompt) {
+ const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${this.settings.aiModel}:generateContent?key=${this.settings.geminiApiKey}`;
+
+ const requestBody = {
+ contents: [{
+ parts: [{
+ text: prompt
+ }]
+ }],
+ generationConfig: {
+ temperature: 0.1, // Low temperature for consistent results
+ topK: 40,
+ topP: 0.95,
+ maxOutputTokens: 1024
+ },
+ safetySettings: [
+ {
+ category: "HARM_CATEGORY_HARASSMENT",
+ threshold: "BLOCK_NONE"
+ },
+ {
+ category: "HARM_CATEGORY_HATE_SPEECH",
+ threshold: "BLOCK_NONE"
+ },
+ {
+ category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
+ threshold: "BLOCK_NONE"
+ },
+ {
+ category: "HARM_CATEGORY_DANGEROUS_CONTENT",
+ threshold: "BLOCK_NONE"
+ }
+ ]
+ };
+
+ let lastError;
+
+ for (let attempt = 1; attempt <= this.settings.maxRetries; attempt++) {
+ try {
+ const response = await fetch(apiUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(requestBody)
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
+ }
+
+ const responseData = await response.json();
+
+ if (!responseData.candidates || !responseData.candidates[0]) {
+ throw new Error('Invalid API response structure');
+ }
+
+ return responseData;
+ } catch (error) {
+ lastError = error;
+ console.warn(`API attempt ${attempt} failed:`, error.message);
+
+ if (attempt < this.settings.maxRetries) {
+ // Exponential backoff
+ await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
+ }
+ }
+ }
+
+ throw lastError;
+ }
+
+ parseGeminiResponse(response) {
+ try {
+ const text = response.candidates[0]?.content?.parts[0]?.text;
+ if (!text) {
+ throw new Error('No response text from API');
+ }
+
+ // Extract JSON from response (Gemini sometimes adds extra text)
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
+ if (!jsonMatch) {
+ throw new Error('No JSON found in response');
+ }
+
+ const result = JSON.parse(jsonMatch[0]);
+
+ // Validate response structure
+ if (!Array.isArray(result.sensitiveIndexes)) {
+ throw new Error('Invalid response: sensitiveIndexes must be an array');
+ }
+
+ if (!Array.isArray(result.fieldTypes)) {
+ throw new Error('Invalid response: fieldTypes must be an array');
+ }
+
+ if (typeof result.confidence !== 'number' || result.confidence < 0 || result.confidence > 1) {
+ throw new Error('Invalid response: confidence must be a number between 0 and 1');
+ }
+
+ // Ensure arrays are the same length
+ if (result.sensitiveIndexes.length !== result.fieldTypes.length) {
+ throw new Error('Invalid response: sensitiveIndexes and fieldTypes must have the same length');
+ }
+
+ return result;
+ } catch (error) {
+ console.error('Failed to parse Gemini response:', error);
+ console.error('Raw response:', response);
+
+ // Return safe fallback
+ return {
+ sensitiveIndexes: [],
+ fieldTypes: [],
+ confidence: 0,
+ reasoning: 'Failed to parse AI response'
+ };
+ }
+ }
+
+ updateStatistics(success, responseTime) {
+ this.statistics.totalRequests++;
+
+ if (success) {
+ this.statistics.successfulRequests++;
+ } else {
+ this.statistics.failedRequests++;
+ }
+
+ this.statistics.totalResponseTime += responseTime;
+ this.statistics.averageResponseTime = this.statistics.totalResponseTime / this.statistics.totalRequests;
+ }
+
+ async updateSettings(newSettings) {
+ this.settings = { ...this.settings, ...newSettings };
+ await this.saveSettings();
+
+ // Notify all tabs about settings change
+ this.broadcastToTabs({ action: 'settingsUpdated', settings: this.settings });
+ }
+
+ async testAPIKey(apiKey = null) {
+ const keyToTest = apiKey || this.settings.geminiApiKey;
+ if (!keyToTest) return false;
+
+ try {
+ const testUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=${keyToTest}`;
+
+ const response = await fetch(testUrl, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ contents: [{
+ parts: [{ text: 'Test connection - respond with "API_CONNECTION_SUCCESS"' }]
+ }],
+ generationConfig: {
+ temperature: 0.1,
+ maxOutputTokens: 100
+ }
+ })
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ console.log('โ
Gemini API connection successful');
+ console.log('๐ก Response:', data.candidates?.[0]?.content?.parts?.[0]?.text);
+ return true;
+ } else {
+ const errorText = await response.text();
+ console.error('โ API connection failed:', response.status, response.statusText, errorText);
+ return false;
+ }
+ } catch (error) {
+ console.error('API key test failed:', error);
+ return false;
+ }
+ }
+
+ async clearCache() {
+ try {
+ await chrome.storage.local.clear();
+ this.broadcastToTabs({ action: 'cacheCleared' });
+ } catch (error) {
+ console.error('Failed to clear cache:', error);
+ throw error;
+ }
+ }
+
+ async broadcastToTabs(message) {
+ try {
+ const tabs = await chrome.tabs.query({});
+ for (const tab of tabs) {
+ chrome.tabs.sendMessage(tab.id, message).catch(() => {
+ // Ignore errors for tabs that don't have content script
+ });
+ }
+ } catch (error) {
+ console.error('Failed to broadcast message:', error);
+ }
+ }
+
+ getStatistics() {
+ return {
+ ...this.statistics,
+ successRate: this.statistics.totalRequests > 0
+ ? this.statistics.successfulRequests / this.statistics.totalRequests
+ : 0,
+ uptime: Date.now() - (this.startTime || Date.now())
+ };
+ }
+
+ initializeExtensionState() {
+ this.startTime = Date.now();
+
+ // Set extension badge
+ this.updateBadge();
+
+ // Set up periodic cleanup
+ setInterval(() => this.performMaintenance(), 60 * 60 * 1000); // Every hour
+ }
+
+ updateBadge() {
+ if (this.settings.enableAI && this.settings.geminiApiKey) {
+ chrome.action.setBadgeText({ text: 'ON' });
+ chrome.action.setBadgeBackgroundColor({ color: '#4CAF50' });
+ } else {
+ chrome.action.setBadgeText({ text: 'OFF' });
+ chrome.action.setBadgeBackgroundColor({ color: '#F44336' });
+ }
+ }
+
+ async performMaintenance() {
+ try {
+ // Clean up old cache entries
+ const storage = await chrome.storage.local.get();
+ const cutoffTime = Date.now() - (7 * 24 * 60 * 60 * 1000); // 7 days ago
+
+ const keysToRemove = [];
+ for (const [key, value] of Object.entries(storage)) {
+ if (value.timestamp && value.timestamp < cutoffTime) {
+ keysToRemove.push(key);
+ }
+ }
+
+ if (keysToRemove.length > 0) {
+ await chrome.storage.local.remove(keysToRemove);
+ console.log(`Cleaned up ${keysToRemove.length} expired cache entries`);
+ }
+
+ // Update badge
+ this.updateBadge();
+
+ } catch (error) {
+ console.error('Maintenance failed:', error);
+ }
+ }
+
+ handleInstallation(details) {
+ console.log('Extension installed/updated:', details.reason);
+
+ if (details.reason === 'install') {
+ // First time installation
+ chrome.tabs.create({
+ url: chrome.runtime.getURL('popup/popup.html#welcome')
+ });
+ } else if (details.reason === 'update') {
+ // Extension updated
+ console.log('Extension updated to version:', chrome.runtime.getManifest().version);
+ }
+ }
+
+ handleStartup() {
+ console.log('Browser started, reinitializing extension');
+ this.updateBadge();
+ }
+}
+
+// Initialize background service
+const backgroundService = new BackgroundService();
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/config/dictionaries.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/config/dictionaries.js
new file mode 100644
index 000000000..e19f5078e
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/config/dictionaries.js
@@ -0,0 +1,213 @@
+// Multi-language dictionaries for sensitive field detection
+// Supporting internationalization and regional variations
+
+window.FIELD_DICTIONARIES = {
+ // English
+ en: {
+ email: ['email', 'e-mail', 'electronic mail', 'mail address', 'email address'],
+ password: ['password', 'passcode', 'pass', 'pwd', 'secret', 'pin', 'auth code'],
+ phone: ['phone', 'telephone', 'mobile', 'cell', 'contact number', 'phone number'],
+ address: ['address', 'street', 'residence', 'location', 'home address'],
+ name: ['name', 'full name', 'first name', 'last name', 'given name', 'family name'],
+ dateOfBirth: ['date of birth', 'birth date', 'birthday', 'dob'],
+ creditCard: ['credit card', 'card number', 'payment method', 'cc'],
+ ssn: ['social security', 'ssn', 'tax id', 'national id']
+ },
+
+ // Spanish
+ es: {
+ email: ['correo', 'email', 'correo electrรณnico', 'direcciรณn de correo'],
+ password: ['contraseรฑa', 'clave', 'password', 'cรณdigo', 'pin'],
+ phone: ['telรฉfono', 'mรณvil', 'celular', 'nรบmero de telรฉfono'],
+ address: ['direcciรณn', 'domicilio', 'residencia', 'ubicaciรณn'],
+ name: ['nombre', 'nombre completo', 'primer nombre', 'apellido'],
+ dateOfBirth: ['fecha de nacimiento', 'cumpleaรฑos', 'nacimiento'],
+ creditCard: ['tarjeta de crรฉdito', 'nรบmero de tarjeta', 'mรฉtodo de pago'],
+ ssn: ['seguridad social', 'identificaciรณn nacional', 'cรฉdula']
+ },
+
+ // French
+ fr: {
+ email: ['email', 'courriel', 'adresse email', 'courrier รฉlectronique'],
+ password: ['mot de passe', 'code', 'password', 'secret', 'pin'],
+ phone: ['tรฉlรฉphone', 'mobile', 'numรฉro de tรฉlรฉphone', 'portable'],
+ address: ['adresse', 'domicile', 'rรฉsidence', 'localisation'],
+ name: ['nom', 'nom complet', 'prรฉnom', 'nom de famille'],
+ dateOfBirth: ['date de naissance', 'anniversaire', 'naissance'],
+ creditCard: ['carte de crรฉdit', 'numรฉro de carte', 'mรฉthode de paiement'],
+ ssn: ['sรฉcuritรฉ sociale', 'identification nationale', 'numรฉro national']
+ },
+
+ // German
+ de: {
+ email: ['email', 'e-mail', 'elektronische post', 'mail-adresse'],
+ password: ['passwort', 'kennwort', 'code', 'pin', 'geheim'],
+ phone: ['telefon', 'handy', 'mobiltelefon', 'telefonnummer'],
+ address: ['adresse', 'anschrift', 'wohnort', 'standort'],
+ name: ['name', 'vollstรคndiger name', 'vorname', 'nachname'],
+ dateOfBirth: ['geburtsdatum', 'geburtstag', 'datum der geburt'],
+ creditCard: ['kreditkarte', 'kartennummer', 'zahlungsmethode'],
+ ssn: ['sozialversicherung', 'nationale identifikation', 'personalausweis']
+ },
+
+ // Italian
+ it: {
+ email: ['email', 'posta elettronica', 'indirizzo email', 'mail'],
+ password: ['password', 'parola chiave', 'codice', 'pin', 'segreto'],
+ phone: ['telefono', 'cellulare', 'mobile', 'numero di telefono'],
+ address: ['indirizzo', 'domicilio', 'residenza', 'posizione'],
+ name: ['nome', 'nome completo', 'primo nome', 'cognome'],
+ dateOfBirth: ['data di nascita', 'compleanno', 'nascita'],
+ creditCard: ['carta di credito', 'numero carta', 'metodo di pagamento'],
+ ssn: ['codice fiscale', 'identificazione nazionale', 'carta identitร ']
+ },
+
+ // Portuguese
+ pt: {
+ email: ['email', 'correio eletrรดnico', 'endereรงo de email', 'mail'],
+ password: ['senha', 'palavra-passe', 'cรณdigo', 'pin', 'segredo'],
+ phone: ['telefone', 'celular', 'mรณvel', 'nรบmero de telefone'],
+ address: ['endereรงo', 'morada', 'residรชncia', 'localizaรงรฃo'],
+ name: ['nome', 'nome completo', 'primeiro nome', 'sobrenome'],
+ dateOfBirth: ['data de nascimento', 'aniversรกrio', 'nascimento'],
+ creditCard: ['cartรฃo de crรฉdito', 'nรบmero do cartรฃo', 'mรฉtodo de pagamento'],
+ ssn: ['cpf', 'identificaรงรฃo nacional', 'documento']
+ },
+
+ // Russian
+ ru: {
+ email: ['email', 'ัะปะตะบััะพะฝะฝะฐั ะฟะพััะฐ', 'ะฐะดัะตั ัะปะตะบััะพะฝะฝะพะน ะฟะพััั', 'ะฟะพััะฐ'],
+ password: ['ะฟะฐัะพะปั', 'ะบะพะด', 'ะฟะธะฝ', 'ัะตะบัะตั', 'ะบะปัั'],
+ phone: ['ัะตะปะตัะพะฝ', 'ะผะพะฑะธะปัะฝัะน', 'ะฝะพะผะตั ัะตะปะตัะพะฝะฐ', 'ัะพัะพะฒัะน'],
+ address: ['ะฐะดัะตั', 'ะผะตััะพะฟะพะปะพะถะตะฝะธะต', 'ะฟัะพะถะธะฒะฐะฝะธะต', 'ะผะตััะพ'],
+ name: ['ะธะผั', 'ะฟะพะปะฝะพะต ะธะผั', 'ัะฐะผะธะปะธั', 'ะพััะตััะฒะพ'],
+ dateOfBirth: ['ะดะฐัะฐ ัะพะถะดะตะฝะธั', 'ะดะตะฝั ัะพะถะดะตะฝะธั', 'ัะพะถะดะตะฝะธะต'],
+ creditCard: ['ะบัะตะดะธัะฝะฐั ะบะฐััะฐ', 'ะฝะพะผะตั ะบะฐััั', 'ัะฟะพัะพะฑ ะพะฟะปะฐัั'],
+ ssn: ['ัะฝะธะปั', 'ะฟะฐัะฟะพัั', 'ะฝะฐัะธะพะฝะฐะปัะฝัะน ะธะดะตะฝัะธัะธะบะฐัะพั']
+ },
+
+ // Chinese Simplified
+ zh: {
+ email: ['้ฎ็ฎฑ', '็ตๅญ้ฎไปถ', '้ฎไปถๅฐๅ', '็ต้ฎ'],
+ password: ['ๅฏ็ ', 'ๅฃไปค', 'ๆ็ ', '็ง้ฅ'],
+ phone: ['็ต่ฏ', 'ๆๆบ', '่็ณป็ต่ฏ', '็ต่ฏๅท็ '],
+ address: ['ๅฐๅ', 'ไฝๅ', 'ๅฑ
ไฝๅฐ', 'ไฝ็ฝฎ'],
+ name: ['ๅงๅ', 'ๅ
จๅ', 'ๅๅญ', 'ๅงๆฐ'],
+ dateOfBirth: ['ๅบ็ๆฅๆ', '็ๆฅ', 'ๅบ็ๅนดๆ'],
+ creditCard: ['ไฟก็จๅก', 'ๅกๅท', 'ๆฏไปๆนๅผ'],
+ ssn: ['่บซไปฝ่ฏ', '็คพไผไฟ้ๅท', 'ๅฝๆฐ่บซไปฝ่ฏ']
+ },
+
+ // Japanese
+ ja: {
+ email: ['ใกใผใซ', 'Eใกใผใซ', '้ปๅญใกใผใซ', 'ใกใผใซใขใใฌใน'],
+ password: ['ใในใฏใผใ', 'ๆ่จผ็ชๅท', 'PIN', '็งๅฏ้ต'],
+ phone: ['้ป่ฉฑ', 'ๆบๅธฏ', '้ป่ฉฑ็ชๅท', 'TEL'],
+ address: ['ไฝๆ', 'ๆๅจๅฐ', 'ๅฑ
ไฝๅฐ', 'ใขใใฌใน'],
+ name: ['ๅๅ', 'ๆฐๅ', 'ๅงๅ', 'ใใซใใผใ '],
+ dateOfBirth: ['็ๅนดๆๆฅ', '่ช็ๆฅ', '็ใพใใๆฅ'],
+ creditCard: ['ใฏใฌใธใใใซใผใ', 'ใซใผใ็ชๅท', 'ๆฏๆใๆนๆณ'],
+ ssn: ['ใใคใใณใใผ', '็คพไผไฟ้็ชๅท', '่บซๅ่จผๆๆธ']
+ },
+
+ // Arabic
+ ar: {
+ email: ['ุงูุจุฑูุฏ ุงูุฅููุชุฑููู', 'ุฅูู
ูู', 'ุนููุงู ุงูุจุฑูุฏ', 'ุงูุฅูู
ูู'],
+ password: ['ููู
ุฉ ุงูู
ุฑูุฑ', 'ุงูุฑูู
ุงูุณุฑู', 'ููุฏ', 'ุณุฑ'],
+ phone: ['ุงููุงุชู', 'ุงูุฌูุงู', 'ุฑูู
ุงููุงุชู', 'ู
ูุจุงูู'],
+ address: ['ุงูุนููุงู', 'ุงูุณูู', 'ุงูู
ููุน', 'ุงูู
ูุฒู'],
+ name: ['ุงูุงุณู
', 'ุงูุงุณู
ุงููุงู
ู', 'ุงูุงุณู
ุงูุฃูู', 'ุงูููุจ'],
+ dateOfBirth: ['ุชุงุฑูุฎ ุงูู
ููุงุฏ', 'ุนูุฏ ุงูู
ููุงุฏ', 'ุงูููุงุฏุฉ'],
+ creditCard: ['ุจุทุงูุฉ ุงุฆุชู
ุงู', 'ุฑูู
ุงูุจุทุงูุฉ', 'ุทุฑููุฉ ุงูุฏูุน'],
+ ssn: ['ุงููููุฉ ุงููุทููุฉ', 'ุฑูู
ุงููููุฉ', 'ุงูุจุทุงูุฉ ุงูุดุฎุตูุฉ']
+ },
+
+ // Hindi
+ hi: {
+ email: ['เคเคฎเฅเคฒ', 'เคเคฒเฅเคเฅเคเฅเคฐเฅเคจเคฟเค เคฎเฅเคฒ', 'เคฎเฅเคฒ เคชเคคเคพ', 'เคกเคพเค'],
+ password: ['เคชเคพเคธเคตเคฐเฅเคก', 'เคเฅเคชเฅเคค เคเฅเคก', 'เคชเคฟเคจ', 'เคฐเคนเคธเฅเคฏ'],
+ phone: ['เคซเฅเคจ', 'เคฎเฅเคฌเคพเคเคฒ', 'เคเฅเคฒเฅเคซเฅเคจ', 'เคธเคเคชเคฐเฅเค เคจเคเคฌเคฐ'],
+ address: ['เคชเคคเคพ', 'เคเคฐ เคเคพ เคชเคคเคพ', 'เคจเคฟเคตเคพเคธ', 'เคธเฅเคฅเคพเคจ'],
+ name: ['เคจเคพเคฎ', 'เคชเฅเคฐเคพ เคจเคพเคฎ', 'เคชเคนเคฒเคพ เคจเคพเคฎ', 'เค
เคเคคเคฟเคฎ เคจเคพเคฎ'],
+ dateOfBirth: ['เคเคจเฅเคฎ เคคเคฟเคฅเคฟ', 'เคเคจเฅเคฎเคฆเคฟเคจ', 'เคเคจเฅเคฎ'],
+ creditCard: ['เคเฅเคฐเฅเคกเคฟเค เคเคพเคฐเฅเคก', 'เคเคพเคฐเฅเคก เคจเคเคฌเคฐ', 'เคญเฅเคเคคเคพเคจ เคตเคฟเคงเคฟ'],
+ ssn: ['เคเคงเคพเคฐ', 'เคฐเคพเคทเฅเคเฅเคฐเฅเคฏ เคชเคนเคเคพเคจ', 'เคชเคนเคเคพเคจ เคธเคเคเฅเคฏเคพ']
+ }
+};
+
+// Regional specific patterns
+window.REGIONAL_PATTERNS = {
+ // US specific
+ US: {
+ ssn: /^\d{3}-?\d{2}-?\d{4}$/,
+ zipCode: /^\d{5}(-\d{4})?$/,
+ phone: /^(\+1[-.\s]?)?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})$/,
+ state: ['AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'FL', 'GA', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY']
+ },
+
+ // UK specific
+ UK: {
+ postCode: /^[A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2}$/i,
+ phone: /^(\+44[-.\s]?)?(\d{4}[-.\s]?\d{6}|\d{5}[-.\s]?\d{5})$/,
+ niNumber: /^[A-Z]{2}\d{6}[A-Z]$/i
+ },
+
+ // Canada specific
+ CA: {
+ postalCode: /^[A-Z]\d[A-Z]\s*\d[A-Z]\d$/i,
+ phone: /^(\+1[-.\s]?)?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})$/,
+ sin: /^\d{3}[-.\s]?\d{3}[-.\s]?\d{3}$/
+ },
+
+ // Germany specific
+ DE: {
+ postCode: /^\d{5}$/,
+ phone: /^(\+49[-.\s]?)?(\d{3,4}[-.\s]?\d{7,8})$/,
+ taxId: /^\d{11}$/
+ },
+
+ // France specific
+ FR: {
+ postCode: /^\d{5}$/,
+ phone: /^(\+33[-.\s]?)?(\d{2}[-.\s]?\d{2}[-.\s]?\d{2}[-.\s]?\d{2}[-.\s]?\d{2})$/,
+ insee: /^[12]\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}\d{2}$/
+ },
+
+ // Japan specific
+ JP: {
+ postCode: /^\d{3}-?\d{4}$/,
+ phone: /^(\+81[-.\s]?)?(\d{2,4}[-.\s]?\d{4}[-.\s]?\d{4})$/,
+ myNumber: /^\d{4}[-.\s]?\d{4}[-.\s]?\d{4}$/
+ },
+
+ // India specific
+ IN: {
+ pinCode: /^\d{6}$/,
+ phone: /^(\+91[-.\s]?)?[6-9]\d{9}$/,
+ aadhaar: /^\d{4}[-.\s]?\d{4}[-.\s]?\d{4}$/,
+ pan: /^[A-Z]{5}\d{4}[A-Z]$/i
+ }
+};
+
+// Industry-specific sensitive field types
+window.INDUSTRY_PATTERNS = {
+ healthcare: {
+ fields: ['medical record number', 'patient id', 'insurance id', 'diagnosis', 'medication'],
+ sensitivity: 0.99
+ },
+
+ financial: {
+ fields: ['account number', 'routing number', 'investment account', 'loan number', 'mortgage'],
+ sensitivity: 0.95
+ },
+
+ education: {
+ fields: ['student id', 'grade', 'transcript', 'education record', 'school'],
+ sensitivity: 0.85
+ },
+
+ government: {
+ fields: ['case number', 'permit number', 'license plate', 'voter id', 'tax return'],
+ sensitivity: 0.90
+ }
+};
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/config/patterns.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/config/patterns.js
new file mode 100644
index 000000000..e781a2eba
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/config/patterns.js
@@ -0,0 +1,122 @@
+// Sensitive Field Detection Patterns
+// Comprehensive patterns for client-side detection
+
+console.log('๐ง patterns.js loading...', window.location.href);
+
+window.SENSITIVE_PATTERNS = {
+ // Input type-based detection
+ INPUT_TYPES: {
+ email: { sensitivity: 0.95, type: 'email' },
+ password: { sensitivity: 0.99, type: 'password' },
+ tel: { sensitivity: 0.90, type: 'phone' },
+ url: { sensitivity: 0.70, type: 'url' },
+ search: { sensitivity: 0.30, type: 'search' }
+ },
+
+ // Name/ID attribute patterns
+ NAME_PATTERNS: {
+ // Email patterns
+ email: /^(email|e[-_]?mail|user[-_]?name|login[-_]?id|account[-_]?name)$/i,
+ emailConfirm: /^(confirm[-_]?email|email[-_]?confirm|verify[-_]?email)$/i,
+
+ // Password patterns
+ password: /^(password|passwd|pwd|pass|pin|secret|auth[-_]?code)$/i,
+ passwordConfirm: /^(confirm[-_]?password|password[-_]?confirm|verify[-_]?password|repeat[-_]?password)$/i,
+
+ // Phone patterns
+ phone: /^(phone|tel|mobile|cell|contact[-_]?number|phone[-_]?number)$/i,
+
+ // Address patterns
+ address: /^(address|street|addr|location|residence)$/i,
+ city: /^(city|town|municipality)$/i,
+ state: /^(state|province|region)$/i,
+ zip: /^(zip|postal[-_]?code|post[-_]?code)$/i,
+ country: /^(country|nation)$/i,
+
+ // Personal info patterns
+ firstName: /^(first[-_]?name|given[-_]?name|fname)$/i,
+ lastName: /^(last[-_]?name|family[-_]?name|surname|lname)$/i,
+ fullName: /^(full[-_]?name|name|display[-_]?name)$/i,
+ dateOfBirth: /^(dob|date[-_]?of[-_]?birth|birth[-_]?date|birthday)$/i,
+
+ // Financial patterns
+ creditCard: /^(cc|credit[-_]?card|card[-_]?number|payment[-_]?method)$/i,
+ cvv: /^(cvv|cvc|security[-_]?code|card[-_]?code)$/i,
+ bankAccount: /^(account[-_]?number|bank[-_]?account|routing[-_]?number)$/i,
+
+ // Government ID patterns
+ ssn: /^(ssn|social[-_]?security|tax[-_]?id|national[-_]?id)$/i,
+ license: /^(license|dl|driver[-_]?license|id[-_]?number)$/i,
+ passport: /^(passport|passport[-_]?number)$/i
+ },
+
+ // Placeholder text patterns
+ PLACEHOLDER_PATTERNS: {
+ email: /enter.*(email|e[-\s]mail)|email.*address|your.*email/i,
+ password: /enter.*(password|pwd)|your.*password|create.*password/i,
+ phone: /enter.*(phone|number)|phone.*number|your.*phone/i,
+ address: /enter.*(address|street)|your.*address|home.*address/i,
+ name: /enter.*(name|full.*name)|your.*name|first.*name|last.*name/i,
+ creditCard: /card.*number|enter.*card|credit.*card/i,
+ ssn: /social.*security|enter.*ssn|your.*ssn/i
+ },
+
+ // Label text patterns
+ LABEL_PATTERNS: {
+ email: /^(email|e[-\s]mail|email\s+address|login|username)$/i,
+ password: /^(password|pwd|pass|secret|passcode)$/i,
+ phone: /^(phone|telephone|mobile|cell|contact)$/i,
+ address: /^(address|street|location|residence)$/i,
+ name: /^(name|full\s+name|first\s+name|last\s+name)$/i,
+ dateOfBirth: /^(date\s+of\s+birth|dob|birthday|birth\s+date)$/i,
+ creditCard: /^(credit\s+card|card\s+number|payment\s+method)$/i,
+ ssn: /^(social\s+security|ssn|tax\s+id|national\s+id)$/i
+ },
+
+ // Content patterns for regex matching
+ CONTENT_PATTERNS: {
+ email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
+ phone: /^[\+]?[(]?[\d\s\-\(\)\.]{10,}$/,
+ creditCard: /^\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}$/,
+ ssn: /^\d{3}[-\s]?\d{2}[-\s]?\d{4}$/,
+ zipCode: /^\d{5}(-\d{4})?$/,
+ dateOfBirth: /^(0[1-9]|1[0-2])\/(0[1-9]|[12]\d|3[01])\/\d{4}$/
+ },
+
+ // Autocomplete attribute values
+ AUTOCOMPLETE_PATTERNS: {
+ 'email': { sensitivity: 0.95, type: 'email' },
+ 'username': { sensitivity: 0.85, type: 'username' },
+ 'current-password': { sensitivity: 0.99, type: 'password' },
+ 'new-password': { sensitivity: 0.99, type: 'password' },
+ 'tel': { sensitivity: 0.90, type: 'phone' },
+ 'tel-national': { sensitivity: 0.90, type: 'phone' },
+ 'street-address': { sensitivity: 0.85, type: 'address' },
+ 'address-line1': { sensitivity: 0.85, type: 'address' },
+ 'address-line2': { sensitivity: 0.75, type: 'address' },
+ 'postal-code': { sensitivity: 0.80, type: 'zipcode' },
+ 'country': { sensitivity: 0.70, type: 'country' },
+ 'given-name': { sensitivity: 0.85, type: 'firstName' },
+ 'family-name': { sensitivity: 0.85, type: 'lastName' },
+ 'name': { sensitivity: 0.85, type: 'fullName' },
+ 'cc-number': { sensitivity: 0.95, type: 'creditCard' },
+ 'cc-csc': { sensitivity: 0.95, type: 'cvv' },
+ 'cc-exp': { sensitivity: 0.90, type: 'cardExpiry' },
+ 'bday': { sensitivity: 0.85, type: 'dateOfBirth' }
+ },
+
+ // Visual cues and indicators
+ VISUAL_INDICATORS: {
+ passwordToggle: ['password-toggle', 'show-password', 'eye-icon'],
+ requiredField: ['required', 'mandatory', 'asterisk'],
+ securityIcon: ['lock', 'shield', 'secure', 'key'],
+ emailIcon: ['email', 'envelope', 'mail']
+ },
+
+ // Context keywords that suggest sensitivity
+ CONTEXT_KEYWORDS: {
+ high: ['personal', 'private', 'confidential', 'sensitive', 'secure'],
+ medium: ['account', 'profile', 'contact', 'billing', 'payment'],
+ low: ['preferences', 'settings', 'options', 'display']
+ }
+};
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/content-basic.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/content-basic.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/content-simple.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/content-simple.js
new file mode 100644
index 000000000..9973e8523
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/content-simple.js
@@ -0,0 +1,21 @@
+// ULTRA SIMPLE CONTENT SCRIPT TEST
+console.log('๐ฏ๐ฏ๐ฏ CONTENT SCRIPT WORKING! ๐ฏ๐ฏ๐ฏ');
+console.log('๐ฏ Page URL:', window.location.href);
+console.log('๐ฏ Page loaded!');
+
+// Immediate test
+alert('๐ Extension content script is working!');
+
+// Add visual indicator
+try {
+ const div = document.createElement('div');
+ div.style.cssText = 'position:fixed;top:0;left:0;width:100%;background:red;color:white;padding:10px;z-index:99999;text-align:center;';
+ div.textContent = '๐ EXTENSION CONTENT SCRIPT LOADED!';
+ document.documentElement.appendChild(div);
+
+ setTimeout(() => {
+ div.remove();
+ }, 5000);
+} catch (e) {
+ console.log('๐ฏ Error adding visual indicator:', e);
+}
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/content-step1.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/content-step1.js
new file mode 100644
index 000000000..5589e5824
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/content-step1.js
@@ -0,0 +1,3658 @@
+// STEP 1: Pre-Visibility Smart DOM Detection
+console.log('๐ STEP 1: Content script loaded EARLY!', {
+ url: window.location.href,
+ readyState: document.readyState,
+ timestamp: new Date().toISOString()
+});
+
+// Global state
+let isExtensionEnabled = false;
+let detectedElements = new Map();
+let lastScanHash = '';
+let scanTimeout = null;
+let userActivityTimeout = null;
+let isUserActive = false;
+let earlyDetectionComplete = false;
+let pendingAIAnalysis = []; // Track pending AI operations
+let isRawDOMAnalysisRunning = false; // NEW: Track raw DOM analysis state
+
+// Browser API Integration
+let preVisibilityFieldsList = []; // Pattern-based sensitive fields detected pre-visibility
+let aiDetectedFieldsList = []; // AI-detected sensitive fields from Gemini analysis
+let lastSentPreVisibilityHash = ''; // Track what was last sent for pre-visibility
+let lastSentAIHash = ''; // Track what was last sent for AI detection
+
+// Performance tracking
+let performanceTimestamps = {
+ scriptStart: Date.now(),
+ extensionEnabled: null,
+ preVisibilityStart: null,
+ firstElementDetected: null,
+ preVisibilityListSent: null,
+ aiAnalysisStart: null,
+ aiAnalysisComplete: null,
+ aiListSent: null,
+ rawDOMAnalysisStart: null // NEW: Track raw DOM analysis start time
+};
+
+// URL change detection for SPA navigation
+let currentURL = window.location.href;
+
+// Configuration
+const CONFIG = {
+ SCAN_DEBOUNCE_DELAY: 1000, // Wait time before scanning
+ USER_ACTIVITY_TIMEOUT: 5000, // Consider user inactive after 5s
+ MAX_SCAN_FREQUENCY: 5000, // Maximum scan frequency
+ SIGNIFICANT_CHANGE_THRESHOLD: 1, // Trigger AI analysis for any new sensitive elements
+ EARLY_SCAN_INTERVAL: 100 // Check every 100ms during early loading
+};
+
+// Initialize URL change detection
+setupURLChangeDetection();
+
+// Start immediately - even before DOM is ready
+initializeEarly();
+
+async function initializeEarly() {
+ console.log('โก Early initialization starting...');
+ performanceTimestamps.initStart = Date.now();
+
+ // Get extension state as early as possible
+ try {
+ const result = await chrome.storage.sync.get(['extensionEnabled']);
+ isExtensionEnabled = result.extensionEnabled || false;
+ performanceTimestamps.extensionEnabled = Date.now();
+
+ console.log('โ
Extension enabled state loaded early:', isExtensionEnabled, {
+ timeSinceStart: performanceTimestamps.extensionEnabled - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ if (isExtensionEnabled) {
+ startPreVisibilityDetection();
+ }
+ } catch (error) {
+ console.log('โ ๏ธ Early storage access failed, will retry on DOM ready');
+ }
+
+ // Set up storage listener
+ chrome.storage.onChanged.addListener((changes) => {
+ if (changes.extensionEnabled) {
+ isExtensionEnabled = changes.extensionEnabled.newValue;
+ console.log('๐ Extension state changed early:', isExtensionEnabled);
+
+ if (isExtensionEnabled) {
+ startPreVisibilityDetection();
+ } else {
+ stopDOMMonitoring();
+ }
+ }
+ });
+
+ performanceTimestamps.initComplete = Date.now();
+ console.log('๐ฏ Early initialization complete', {
+ totalInitTime: performanceTimestamps.initComplete - performanceTimestamps.scriptStart + 'ms'
+ });
+}
+
+function startPreVisibilityDetection() {
+ console.log('๐๏ธ Starting PRE-VISIBILITY detection...');
+ performanceTimestamps.preVisibilityStart = Date.now();
+
+ console.log('โฑ๏ธ TIMESTAMP: Pre-visibility detection started', {
+ timeSinceStart: performanceTimestamps.preVisibilityStart - performanceTimestamps.scriptStart + 'ms',
+ timeSinceInit: performanceTimestamps.preVisibilityStart - performanceTimestamps.initComplete + 'ms'
+ });
+
+ // Immediate scan of whatever exists
+ performEarlyScan('immediate').catch(error => {
+ console.warn('โ ๏ธ Early scan failed:', error);
+ });
+
+ // Keep scanning during page load
+ const earlyDetectionInterval = setInterval(async () => {
+ if (document.readyState === 'complete') {
+ console.log('๐ Page fully loaded, stopping early detection');
+ clearInterval(earlyDetectionInterval);
+ earlyDetectionComplete = true;
+
+ performanceTimestamps.pageLoadComplete = Date.now();
+ console.log('โฑ๏ธ TIMESTAMP: Page load complete', {
+ totalLoadTime: performanceTimestamps.pageLoadComplete - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // WAIT for any pending AI analysis to complete before showing page
+ await waitForPendingAIAnalysis();
+
+ console.log('๐ Early detection complete');
+ return;
+ }
+
+ // Continue early scanning while page loads
+ await performEarlyScan('pre-load');
+ }, CONFIG.EARLY_SCAN_INTERVAL);
+
+ // Backup: start normal monitoring on DOMContentLoaded
+ document.addEventListener('DOMContentLoaded', () => {
+ performanceTimestamps.domContentLoaded = Date.now();
+ console.log('๐ DOMContentLoaded - ensuring monitoring is active', {
+ timeSinceStart: performanceTimestamps.domContentLoaded - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ if (!earlyDetectionComplete) {
+ clearInterval(earlyDetectionInterval);
+ }
+
+ // Perform a final scan to catch any elements missed during early detection
+ setTimeout(async () => {
+ console.log('๐ Final backup scan after DOMContentLoaded...');
+ await performEarlyScan('dom-content-loaded');
+ }, 500);
+ });
+
+ // Additional backup scan when window loads completely
+ window.addEventListener('load', async () => {
+ performanceTimestamps.windowLoaded = Date.now();
+ console.log('๐ฏ Window fully loaded - performing final scan...', {
+ timeSinceStart: performanceTimestamps.windowLoaded - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ await performEarlyScan('window-loaded');
+ });
+}
+
+async function performEarlyScan(phase) {
+ if (!isExtensionEnabled) return;
+
+ const scanStartTime = Date.now();
+ console.log(`โฑ๏ธ TIMESTAMP: Early scan started (${phase})`, {
+ timeSinceStart: scanStartTime - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // Try to scan even if document isn't ready
+ try {
+ // RAW DOM ANALYSIS TRIGGER - MOVED TO TOP FOR EARLIER EXECUTION
+ // Trigger raw DOM analysis immediately at the start of each scan phase
+ if (phase === 'immediate' || phase === 'pre-load') {
+ console.log('๐ ULTRA-EARLY: Triggering raw DOM analysis at scan start...', {
+ phase: phase,
+ timeSinceStart: Date.now() - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // Check if raw DOM analysis is already running
+ if (isRawDOMAnalysisRunning) {
+ console.log('โธ๏ธ ULTRA-EARLY: Raw DOM analysis already running, skipping...');
+ } else {
+ // Set flag to prevent multiple simultaneous analyses
+ isRawDOMAnalysisRunning = true;
+ performanceTimestamps.rawDOMAnalysisStart = Date.now();
+
+ // Trigger raw DOM analysis in background (don't await to avoid blocking)
+ sendRawDOMToGemini()
+ .then(result => {
+ console.log('โ
ULTRA-EARLY: Raw DOM analysis completed successfully', {
+ fieldsDetected: result.sensitiveFields?.length || 0,
+ processingTime: result.processingTime,
+ phase: phase,
+ totalTime: Date.now() - performanceTimestamps.rawDOMAnalysisStart + 'ms'
+ });
+ isRawDOMAnalysisRunning = false; // Reset flag
+ })
+ .catch(error => {
+ console.error('โ ULTRA-EARLY: Raw DOM analysis failed:', error);
+ isRawDOMAnalysisRunning = false; // Reset flag on error
+ });
+ }
+ }
+
+ // COMMENTED OUT: Phase 1: Scan for visible text content containing sensitive information
+ // await scanVisibleTextContent(phase);
+
+ // Phase 2: Scan for form input elements (PRE-VISIBILITY DETECTION - KEPT ACTIVE)
+ const inputs = document.querySelectorAll('input, textarea, select');
+ const allTextElements = document.querySelectorAll('*');
+
+ if (inputs.length === 0 && allTextElements.length === 0) {
+ // Log that we're waiting for elements to appear
+ if (phase === 'immediate') {
+ console.log('โณ No elements found yet, will keep trying...');
+ }
+ return; // Nothing to scan yet
+ }
+
+ const elementsFoundTime = Date.now();
+ console.log(`๐ Early scan (${phase}): Found ${inputs.length} form elements and ${allTextElements.length} text elements`, {
+ scanTime: elementsFoundTime - scanStartTime + 'ms'
+ });
+
+ // Process form elements (PRE-VISIBILITY DETECTION - KEPT ACTIVE)
+ const currentHash = generateElementsHash(inputs);
+ if (currentHash !== lastScanHash) {
+ lastScanHash = currentHash;
+ let newElementsCount = 0;
+
+ inputs.forEach((element, index) => {
+ const elementKey = generateElementKey(element);
+
+ if (detectedElements.has(elementKey)) {
+ return; // Already processed
+ }
+
+ newElementsCount++;
+ const elementInfo = extractElementInfo(element, index + 1);
+
+ console.log(`๐ฏ PRE-VISIBILITY Form Element ${index + 1}:`, elementInfo);
+
+ // Store with early detection flag
+ detectedElements.set(elementKey, {
+ element: element,
+ info: elementInfo,
+ timestamp: Date.now(),
+ detectedAt: phase,
+ preVisibility: true,
+ elementType: 'form-input'
+ });
+ });
+
+ if (newElementsCount > 0) {
+ const processingCompleteTime = Date.now();
+
+ if (!performanceTimestamps.firstElementDetected) {
+ performanceTimestamps.firstElementDetected = processingCompleteTime;
+ console.log('โฑ๏ธ TIMESTAMP: First form elements detected', {
+ timeToFirstElement: performanceTimestamps.firstElementDetected - performanceTimestamps.scriptStart + 'ms'
+ });
+ }
+
+ console.log(`โ
Form elements scan complete. New: ${newElementsCount}, Total: ${detectedElements.size}`, {
+ processingTime: processingCompleteTime - scanStartTime + 'ms'
+ });
+
+ // Process form elements for sensitive field detection (PRE-VISIBILITY - KEPT ACTIVE)
+ const newElements = Array.from(detectedElements.values()).slice(-newElementsCount);
+ logDetectedSensitiveFields(newElements);
+ }
+ }
+
+ // COMMENTED OUT: Process text content for displayed sensitive information
+ // await scanVisibleTextContent(phase);
+
+ // COMMENTED OUT: Secondary raw DOM analysis trigger (now handled at the top)
+ // RAW DOM ANALYSIS TRIGGER - KEPT ACTIVE
+ // Trigger raw DOM analysis immediately after DOM elements are captured
+ // if (inputs.length > 0 || allTextElements.length > 0) {
+ // console.log('๐ AUTOMATIC: Triggering raw DOM analysis after DOM capture...', {
+ // phase: phase,
+ // formElements: inputs.length,
+ // textElements: allTextElements.length,
+ // timeSinceStart: Date.now() - performanceTimestamps.scriptStart + 'ms'
+ // });
+ //
+ // // Check if raw DOM analysis is already running
+ // if (isRawDOMAnalysisRunning) {
+ // console.log('โธ๏ธ AUTOMATIC: Raw DOM analysis already running, skipping...');
+ // return;
+ // }
+ //
+ // // Set flag to prevent multiple simultaneous analyses
+ // isRawDOMAnalysisRunning = true;
+ // performanceTimestamps.rawDOMAnalysisStart = Date.now();
+ //
+ // // Trigger raw DOM analysis in background (don't await to avoid blocking)
+ // sendRawDOMToGemini()
+ // .then(result => {
+ // console.log('โ
AUTOMATIC: Raw DOM analysis completed successfully', {
+ // fieldsDetected: result.sensitiveFields?.length || 0,
+ // processingTime: result.processingTime,
+ // phase: phase,
+ // totalTime: Date.now() - performanceTimestamps.rawDOMAnalysisStart + 'ms'
+ // });
+ // isRawDOMAnalysisRunning = false; // Reset flag
+ // })
+ // .catch(error => {
+ // console.error('โ AUTOMATIC: Raw DOM analysis failed:', error);
+ // isRawDOMAnalysisRunning = false; // Reset flag on error
+ // });
+ // }
+
+ // COMMENTED OUT: Trigger AI analysis for both form elements and text content
+ // if (inputs.length > 0 || allTextElements.length > 0) {
+ // performanceTimestamps.aiAnalysisStart = Date.now();
+ // console.log('๐ค Triggering COMPREHENSIVE AI analysis for all page content', {
+ // timeSinceStart: performanceTimestamps.aiAnalysisStart - performanceTimestamps.scriptStart + 'ms'
+ // });
+ //
+ // await prepareForComprehensiveAIAnalysis();
+ // }
+
+ } catch (error) {
+ // DOM might not be ready yet, that's okay
+ console.log('โณ DOM not ready for scanning yet...');
+ }
+}
+
+function logDetectedSensitiveFields(elements) {
+ console.log('๏ฟฝ Logging detected sensitive fields:', elements.length, 'elements');
+
+ // Pattern-based detection for immediate logging
+ elements.forEach(item => {
+ const element = item.element;
+ const info = item.info;
+
+ // Simple sensitive field detection
+ const isSensitive = detectSensitiveField(info);
+
+ if (isSensitive) {
+ console.log('๐ SENSITIVE FIELD DETECTED (pre-visibility):', {
+ type: info.type,
+ name: info.name,
+ id: info.id,
+ placeholder: info.placeholder,
+ url: window.location.href,
+ timestamp: new Date().toISOString()
+ });
+
+ // Mark for tracking (no visual changes)
+ element.setAttribute('data-sensitive-field', 'true');
+ element.setAttribute('data-detection-phase', 'pre-visibility');
+ element.setAttribute('data-detection-method', 'pattern-based');
+
+ // Add to PRE-VISIBILITY browser API list (separate from AI list)
+ addToPreVisibilityFieldsList(element, info, {
+ detectionMethod: 'pattern-based',
+ phase: 'pre-visibility',
+ confidence: 0.8, // Pattern-based confidence
+ category: classifyFieldCategory(info),
+ sensitivity: 'high'
+ });
+
+ // Just log, no visual masking
+ logSensitiveFieldDetails(element, info);
+ }
+ });
+
+ const processingCompleteTime = Date.now();
+ console.log('โฑ๏ธ TIMESTAMP: Pattern-based detection complete', {
+ processingTime: processingCompleteTime - logStartTime + 'ms',
+ sensitiveFieldsFound: preVisibilityFieldsList.length
+ });
+
+ // Send PRE-VISIBILITY list to browser API immediately
+ sendPreVisibilityFieldsToBrowser();
+}
+
+function detectSensitiveField(elementInfo) {
+ const sensitiveKeywords = [
+ 'password', 'passwd', 'pwd', 'pass',
+ 'ssn', 'social', 'security',
+ 'credit', 'card', 'cvv', 'cvc',
+ 'pin', 'code', 'otp',
+ 'account', 'routing', 'bank'
+ ];
+
+ const textToCheck = [
+ elementInfo.type,
+ elementInfo.name,
+ elementInfo.id,
+ elementInfo.placeholder
+ ].join(' ').toLowerCase();
+
+ return sensitiveKeywords.some(keyword => textToCheck.includes(keyword)) ||
+ elementInfo.type === 'password';
+}
+
+function logSensitiveFieldDetails(element, info) {
+ // Log sensitive field details for browser-level handling
+ console.log('๐ SENSITIVE FIELD LOGGED:', {
+ element: element,
+ details: info,
+ selector: generateSelector(element),
+ context: getFieldContext(element),
+ timestamp: new Date().toISOString()
+ });
+}
+
+function generateSelector(element) {
+ // Generate a unique CSS selector for the element
+ if (element.id) {
+ return `#${element.id}`;
+ }
+
+ if (element.name) {
+ return `input[name="${element.name}"]`;
+ }
+
+ // Fallback to tag + index
+ const siblings = Array.from(element.parentNode?.children || []).filter(el => el.tagName === element.tagName);
+ const index = siblings.indexOf(element);
+ return `${element.tagName.toLowerCase()}:nth-child(${index + 1})`;
+}
+
+function getFieldContext(element) {
+ // Get surrounding context for the field
+ const context = {
+ form: element.closest('form')?.id || element.closest('form')?.className || 'no-form',
+ label: element.labels?.[0]?.textContent?.trim() || 'no-label',
+ placeholder: element.placeholder || 'no-placeholder',
+ parentClass: element.parentNode?.className || 'no-parent-class'
+ };
+
+ return context;
+}
+
+// ====== TEXT CONTENT ANALYSIS FUNCTIONS ======
+
+async function scanVisibleTextContent(phase) {
+ const textScanStartTime = Date.now();
+ console.log(`๐ Scanning visible text content for sensitive information (${phase})`);
+
+ try {
+ // Get all visible text elements
+ const textElements = getVisibleTextElements();
+
+ if (textElements.length === 0) {
+ console.log('๐ No visible text elements found');
+ return;
+ }
+
+ console.log(`๐ Found ${textElements.length} visible text elements to analyze`);
+
+ // Pattern-based detection for immediate identification
+ const detectedTextInfo = [];
+
+ textElements.forEach((elementData, index) => {
+ const sensitiveInfo = detectSensitiveTextContent(elementData.text, elementData.element);
+
+ if (sensitiveInfo.length > 0) {
+ console.log(`๐ SENSITIVE TEXT CONTENT DETECTED:`, {
+ element: elementData.element.tagName,
+ selector: generateSelector(elementData.element),
+ detectedInfo: sensitiveInfo,
+ text: elementData.text.substring(0, 100) + (elementData.text.length > 100 ? '...' : '')
+ });
+
+ detectedTextInfo.push({
+ element: elementData.element,
+ text: elementData.text,
+ sensitiveInfo: sensitiveInfo,
+ index: index,
+ detectedAt: phase,
+ elementType: 'text-content'
+ });
+ }
+ });
+
+ if (detectedTextInfo.length > 0) {
+ console.log(`๐ Text content scan complete. Found ${detectedTextInfo.length} elements with sensitive information`);
+
+ // Add to pre-visibility list for immediate browser notification
+ logDetectedSensitiveTextContent(detectedTextInfo);
+ }
+
+ const textScanTime = Date.now() - textScanStartTime;
+ console.log(`โฑ๏ธ TIMESTAMP: Text content scan complete`, {
+ processingTime: textScanTime + 'ms',
+ elementsScanned: textElements.length,
+ sensitiveFound: detectedTextInfo.length
+ });
+
+ } catch (error) {
+ console.error('โ Error scanning text content:', error);
+ }
+}
+
+function getVisibleTextElements() {
+ const textElements = [];
+
+ // Select elements that typically contain visible text
+ const candidateSelectors = [
+ 'p', 'span', 'div', 'td', 'th', 'li', 'label', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
+ '.text', '.content', '.info', '.data', '.details', '.summary', '.description',
+ '[class*="text"]', '[class*="content"]', '[class*="info"]', '[class*="data"]'
+ ];
+
+ candidateSelectors.forEach(selector => {
+ try {
+ const elements = document.querySelectorAll(selector);
+ elements.forEach(element => {
+ if (isElementVisibleForText(element)) {
+ const text = getElementTextContent(element);
+ if (text && text.trim().length > 3) { // Only meaningful text
+ textElements.push({
+ element: element,
+ text: text.trim(),
+ selector: generateSelector(element)
+ });
+ }
+ }
+ });
+ } catch (error) {
+ // Skip invalid selectors
+ }
+ });
+
+ return textElements;
+}
+
+function isElementVisibleForText(element) {
+ try {
+ const rect = element.getBoundingClientRect();
+ const style = window.getComputedStyle(element);
+
+ return rect.width > 0 &&
+ rect.height > 0 &&
+ style.display !== 'none' &&
+ style.visibility !== 'hidden' &&
+ style.opacity !== '0' &&
+ !element.hidden &&
+ // Exclude script/style content
+ !['SCRIPT', 'STYLE', 'NOSCRIPT'].includes(element.tagName) &&
+ // Must have actual text content
+ element.textContent && element.textContent.trim().length > 0;
+ } catch (e) {
+ return false;
+ }
+}
+
+function getElementTextContent(element) {
+ // Get direct text content, excluding nested input fields
+ let text = '';
+
+ for (let node of element.childNodes) {
+ if (node.nodeType === Node.TEXT_NODE) {
+ text += node.textContent;
+ } else if (node.nodeType === Node.ELEMENT_NODE &&
+ !['INPUT', 'TEXTAREA', 'SELECT', 'SCRIPT', 'STYLE'].includes(node.tagName)) {
+ // Recursively get text from non-input elements
+ text += ' ' + getElementTextContent(node);
+ }
+ }
+
+ return text.replace(/\s+/g, ' ').trim();
+}
+
+function detectSensitiveTextContent(text, element) {
+ const sensitiveMatches = [];
+
+ // Phone number patterns
+ const phonePatterns = [
+ /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, // US phone numbers
+ /\b\(\d{3}\)\s?\d{3}[-.]?\d{4}\b/g, // (123) 456-7890
+ /\b\+\d{1,3}[-.\s]?\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}\b/g // International
+ ];
+
+ // Email patterns
+ const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
+
+ // Credit card patterns (basic)
+ const cardPatterns = [
+ /\b4\d{3}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g, // Visa
+ /\b5[1-5]\d{2}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g, // MasterCard
+ /\b3[47]\d{2}[-\s]?\d{6}[-\s]?\d{5}\b/g, // Amex
+ /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g // Generic card format
+ ];
+
+ // SSN patterns
+ const ssnPattern = /\b\d{3}[-]?\d{2}[-]?\d{4}\b/g;
+
+ // IP Address patterns
+ const ipPattern = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
+
+ // Check for phone numbers
+ phonePatterns.forEach((pattern, index) => {
+ const matches = text.match(pattern);
+ if (matches) {
+ matches.forEach(match => {
+ sensitiveMatches.push({
+ type: 'phone',
+ value: match,
+ category: 'personal-info',
+ confidence: 0.85,
+ pattern: `phone-${index + 1}`
+ });
+ });
+ }
+ });
+
+ // Check for email addresses
+ const emailMatches = text.match(emailPattern);
+ if (emailMatches) {
+ emailMatches.forEach(match => {
+ sensitiveMatches.push({
+ type: 'email',
+ value: match,
+ category: 'personal-info',
+ confidence: 0.9,
+ pattern: 'email'
+ });
+ });
+ }
+
+ // Check for credit cards
+ cardPatterns.forEach((pattern, index) => {
+ const matches = text.match(pattern);
+ if (matches) {
+ matches.forEach(match => {
+ // Basic Luhn algorithm check for credit cards
+ const cleaned = match.replace(/[-\s]/g, '');
+ if (cleaned.length >= 13 && cleaned.length <= 19) {
+ sensitiveMatches.push({
+ type: 'credit-card',
+ value: match,
+ category: 'financial',
+ confidence: 0.8,
+ pattern: `card-${index + 1}`
+ });
+ }
+ });
+ }
+ });
+
+ // Check for SSN
+ const ssnMatches = text.match(ssnPattern);
+ if (ssnMatches) {
+ ssnMatches.forEach(match => {
+ sensitiveMatches.push({
+ type: 'ssn',
+ value: match,
+ category: 'personal-id',
+ confidence: 0.75,
+ pattern: 'ssn'
+ });
+ });
+ }
+
+ // Check for IP addresses
+ const ipMatches = text.match(ipPattern);
+ if (ipMatches) {
+ ipMatches.forEach(match => {
+ sensitiveMatches.push({
+ type: 'ip-address',
+ value: match,
+ category: 'technical-info',
+ confidence: 0.7,
+ pattern: 'ip'
+ });
+ });
+ }
+
+ return sensitiveMatches;
+}
+
+function logDetectedSensitiveTextContent(detectedTextInfo) {
+ const logStartTime = Date.now();
+ console.log('๐ Logging detected sensitive text content:', detectedTextInfo.length, 'elements');
+
+ detectedTextInfo.forEach(item => {
+ item.sensitiveInfo.forEach(info => {
+ console.log('๐ SENSITIVE TEXT CONTENT DETECTED:', {
+ type: info.type,
+ category: info.category,
+ confidence: info.confidence,
+ element: item.element.tagName,
+ selector: generateSelector(item.element),
+ maskedValue: maskSensitiveValue(info.value, info.type),
+ timestamp: new Date().toISOString()
+ });
+
+ // Mark element for tracking
+ item.element.setAttribute('data-sensitive-text', 'true');
+ item.element.setAttribute('data-text-category', info.category);
+ item.element.setAttribute('data-text-type', info.type);
+
+ // Add to pre-visibility list for browser API
+ addToPreVisibilityTextContentList(item.element, info, {
+ detectionMethod: 'pattern-based-text',
+ phase: 'pre-visibility',
+ confidence: info.confidence,
+ category: info.category,
+ sensitivity: getSensitivityLevel(info.category),
+ originalText: item.text,
+ sensitiveValue: info.value
+ });
+ });
+ });
+
+ const processingCompleteTime = Date.now();
+ console.log('โฑ๏ธ TIMESTAMP: Text content detection complete', {
+ processingTime: processingCompleteTime - logStartTime + 'ms',
+ totalSensitiveItems: detectedTextInfo.reduce((sum, item) => sum + item.sensitiveInfo.length, 0)
+ });
+
+ // Send text content to browser API
+ sendPreVisibilityTextContentToBrowser();
+}
+
+function maskSensitiveValue(value, type) {
+ switch (type) {
+ case 'phone':
+ return value.replace(/\d/g, '*').replace(/\*/g, (match, offset) =>
+ offset < 3 ? value[offset] : '*');
+ case 'email':
+ const parts = value.split('@');
+ return parts[0].substring(0, 2) + '***@' + parts[1];
+ case 'credit-card':
+ return '**** **** **** ' + value.slice(-4);
+ case 'ssn':
+ return '***-**-' + value.slice(-4);
+ default:
+ return '***';
+ }
+}
+
+function getSensitivityLevel(category) {
+ switch (category) {
+ case 'financial':
+ case 'personal-id':
+ return 'high';
+ case 'personal-info':
+ return 'medium';
+ default:
+ return 'low';
+ }
+}
+
+// ====== BROWSER API INTEGRATION FOR TEXT CONTENT ======
+
+function addToPreVisibilityTextContentList(element, sensitiveInfo, metadata) {
+ if (!window.preVisibilityTextContentList) {
+ window.preVisibilityTextContentList = [];
+ }
+
+ const uniqueId = generateUniqueId(element);
+ const selector = generateSelector(element);
+
+ const textContentItem = {
+ uniqueId: uniqueId,
+ selector: selector,
+ contentType: 'text-content',
+ sensitiveType: sensitiveInfo.type,
+ category: sensitiveInfo.category,
+ confidence: sensitiveInfo.confidence,
+ sensitivity: metadata.sensitivity,
+ detectionMethod: metadata.detectionMethod,
+ phase: metadata.phase,
+ maskedValue: maskSensitiveValue(sensitiveInfo.value, sensitiveInfo.type),
+ originalLength: sensitiveInfo.value.length,
+ elementTag: element.tagName.toLowerCase(),
+ timestamp: new Date().toISOString(),
+ metadata: {
+ ...metadata,
+ textLength: metadata.originalText ? metadata.originalText.length : 0,
+ patternMatch: sensitiveInfo.pattern || 'unknown'
+ }
+ };
+
+ window.preVisibilityTextContentList.push(textContentItem);
+
+ console.log('๐ Added to pre-visibility text content list:', {
+ uniqueId: uniqueId,
+ type: sensitiveInfo.type,
+ category: sensitiveInfo.category,
+ listSize: window.preVisibilityTextContentList.length
+ });
+}
+
+function addToAIDetectedTextContentList(element, aiAnalysis, metadata) {
+ if (!window.aiDetectedTextContentList) {
+ window.aiDetectedTextContentList = [];
+ }
+
+ const uniqueId = generateUniqueId(element);
+ const selector = generateSelector(element);
+
+ const aiTextContentItem = {
+ uniqueId: uniqueId,
+ selector: selector,
+ contentType: 'text-content',
+ sensitiveType: aiAnalysis.type || 'ai-detected',
+ category: aiAnalysis.category || 'uncategorized',
+ confidence: aiAnalysis.confidence || 0.7,
+ sensitivity: metadata.sensitivity || 'medium',
+ detectionMethod: 'ai-analysis',
+ phase: 'ai-enhanced',
+ aiReasoning: aiAnalysis.reasoning || '',
+ elementTag: element.tagName.toLowerCase(),
+ timestamp: new Date().toISOString(),
+ metadata: {
+ ...metadata,
+ aiModelUsed: 'gemini-pro',
+ analysisType: 'text-content-classification'
+ }
+ };
+
+ window.aiDetectedTextContentList.push(aiTextContentItem);
+
+ console.log('๐ค Added to AI-detected text content list:', {
+ uniqueId: uniqueId,
+ type: aiAnalysis.type,
+ confidence: aiAnalysis.confidence,
+ listSize: window.aiDetectedTextContentList.length
+ });
+}
+
+function sendPreVisibilityTextContentToBrowser() {
+ const sendStartTime = Date.now();
+
+ if (!window.preVisibilityTextContentList || window.preVisibilityTextContentList.length === 0) {
+ console.log('๐ค No pre-visibility text content to send to browser');
+ return;
+ }
+
+ console.log(`๐ค Sending ${window.preVisibilityTextContentList.length} text content items to browser API`);
+
+ try {
+ if (typeof wootzSubmitSensitiveFields === 'function') {
+ wootzSubmitSensitiveFields({
+ type: 'text-content-detection',
+ phase: 'pre-visibility',
+ detectionMethod: 'pattern-based',
+ textContentList: window.preVisibilityTextContentList,
+ timestamp: new Date().toISOString(),
+ summary: {
+ totalItems: window.preVisibilityTextContentList.length,
+ categories: [...new Set(window.preVisibilityTextContentList.map(item => item.category))],
+ sensitivityLevels: [...new Set(window.preVisibilityTextContentList.map(item => item.sensitivity))],
+ detectedTypes: [...new Set(window.preVisibilityTextContentList.map(item => item.sensitiveType))]
+ }
+ });
+
+ console.log('โ
Text content successfully sent to browser API');
+ } else {
+ console.warn('โ ๏ธ wootzSubmitSensitiveFields function not available');
+ }
+ } catch (error) {
+ console.error('โ Failed to send text content to browser API:', error);
+ }
+
+ const sendCompleteTime = Date.now();
+ console.log('โฑ๏ธ TIMESTAMP: Text content browser API submission complete', {
+ processingTime: sendCompleteTime - sendStartTime + 'ms',
+ itemsSent: window.preVisibilityTextContentList.length
+ });
+}
+
+function sendAIDetectedTextContentToBrowser() {
+ const sendStartTime = Date.now();
+
+ if (!window.aiDetectedTextContentList || window.aiDetectedTextContentList.length === 0) {
+ console.log('๐ค No AI-detected text content to send to browser');
+ return;
+ }
+
+ console.log(`๐ค Sending ${window.aiDetectedTextContentList.length} AI-detected text content items to browser API`);
+
+ try {
+ if (typeof wootzSubmitSensitiveFields === 'function') {
+ wootzSubmitSensitiveFields({
+ type: 'text-content-detection',
+ phase: 'ai-enhanced',
+ detectionMethod: 'ai-analysis',
+ textContentList: window.aiDetectedTextContentList,
+ timestamp: new Date().toISOString(),
+ summary: {
+ totalItems: window.aiDetectedTextContentList.length,
+ categories: [...new Set(window.aiDetectedTextContentList.map(item => item.category))],
+ confidenceRange: {
+ min: Math.min(...window.aiDetectedTextContentList.map(item => item.confidence)),
+ max: Math.max(...window.aiDetectedTextContentList.map(item => item.confidence)),
+ average: window.aiDetectedTextContentList.reduce((sum, item) => sum + item.confidence, 0) / window.aiDetectedTextContentList.length
+ },
+ detectedTypes: [...new Set(window.aiDetectedTextContentList.map(item => item.sensitiveType))]
+ }
+ });
+
+ console.log('โ
AI-detected text content successfully sent to browser API');
+ } else {
+ console.warn('โ ๏ธ wootzSubmitSensitiveFields function not available');
+ }
+ } catch (error) {
+ console.error('โ Failed to send AI-detected text content to browser API:', error);
+ }
+
+ const sendCompleteTime = Date.now();
+ console.log('โฑ๏ธ TIMESTAMP: AI text content browser API submission complete', {
+ processingTime: sendCompleteTime - sendStartTime + 'ms',
+ itemsSent: window.aiDetectedTextContentList.length
+ });
+}
+
+// ====== SEPARATE BROWSER API INTEGRATION FUNCTIONS ======
+
+function addToPreVisibilityFieldsList(element, info, detectionData) {
+ try {
+ const fieldStartTime = Date.now();
+ const fieldData = {
+ // Element identification for masking
+ selector: generateSelector(element),
+ id: element.id || null,
+ name: element.name || null,
+ type: element.type || 'text',
+
+ // Field characteristics for masking
+ tag: element.tagName.toLowerCase(),
+ placeholder: element.placeholder || null,
+ autocomplete: element.autocomplete || null,
+
+ // Position and visibility info for immediate masking
+ boundingRect: element.getBoundingClientRect(),
+ visible: isElementVisible(element),
+ disabled: element.disabled,
+ hidden: element.hidden,
+
+ // Detection metadata
+ detectionMethod: detectionData.detectionMethod,
+ phase: detectionData.phase,
+ confidence: detectionData.confidence,
+ category: detectionData.category,
+ sensitivity: detectionData.sensitivity,
+
+ // Context for better masking
+ context: getFieldContext(element),
+ formContext: {
+ formId: element.closest('form')?.id || null,
+ formAction: element.closest('form')?.action || null,
+ formMethod: element.closest('form')?.method || 'get'
+ },
+
+ // Page context
+ url: window.location.href,
+ pageTitle: document.title,
+ detectedAt: fieldStartTime,
+
+ // Unique identifier for list management
+ uniqueId: generateUniqueFieldId(element, detectionData)
+ };
+
+ // Add to pre-visibility list (no duplicates check needed as it's early detection)
+ preVisibilityFieldsList.push(fieldData);
+
+ const processingTime = Date.now() - fieldStartTime;
+ console.log('โ Added pre-visibility sensitive field:', {
+ selector: fieldData.selector,
+ category: fieldData.category,
+ total: preVisibilityFieldsList.length,
+ processingTime: processingTime + 'ms'
+ });
+
+ } catch (error) {
+ console.error('โ Failed to add field to pre-visibility list:', error);
+ }
+}
+
+function addToAIDetectedFieldsList(element, info, detectionData) {
+ try {
+ const fieldStartTime = Date.now();
+ const fieldData = {
+ // Element identification for masking
+ selector: generateSelector(element),
+ id: element.id || null,
+ name: element.name || null,
+ type: element.type || 'text',
+
+ // Field characteristics for masking
+ tag: element.tagName.toLowerCase(),
+ placeholder: element.placeholder || null,
+ autocomplete: element.autocomplete || null,
+
+ // Position and visibility info
+ boundingRect: element.getBoundingClientRect(),
+ visible: isElementVisible(element),
+ disabled: element.disabled,
+ hidden: element.hidden,
+
+ // AI Detection metadata
+ detectionMethod: detectionData.detectionMethod,
+ phase: detectionData.phase,
+ confidence: detectionData.confidence,
+ category: detectionData.category,
+ sensitivity: detectionData.sensitivity,
+ aiAnalysis: detectionData.aiAnalysis,
+ aiReason: detectionData.aiReason,
+
+ // Context for better masking
+ context: getFieldContext(element),
+ formContext: {
+ formId: element.closest('form')?.id || null,
+ formAction: element.closest('form')?.action || null,
+ formMethod: element.closest('form')?.method || 'get'
+ },
+
+ // Page context
+ url: window.location.href,
+ pageTitle: document.title,
+ detectedAt: fieldStartTime,
+
+ // Unique identifier for list management
+ uniqueId: generateUniqueFieldId(element, detectionData)
+ };
+
+ // Check if this field is already in AI list
+ const existingIndex = aiDetectedFieldsList.findIndex(field =>
+ field.uniqueId === fieldData.uniqueId
+ );
+
+ if (existingIndex === -1) {
+ // Add new AI-detected field
+ aiDetectedFieldsList.push(fieldData);
+ console.log('๐ค Added AI-detected sensitive field:', {
+ selector: fieldData.selector,
+ category: fieldData.category,
+ confidence: Math.round(fieldData.confidence * 100) + '%',
+ total: aiDetectedFieldsList.length,
+ processingTime: Date.now() - fieldStartTime + 'ms'
+ });
+ } else {
+ // Update existing field with enhanced AI data
+ aiDetectedFieldsList[existingIndex] = fieldData;
+ console.log('๐ Updated AI-detected sensitive field:', {
+ selector: fieldData.selector,
+ confidence: Math.round(fieldData.confidence * 100) + '%'
+ });
+ }
+
+ } catch (error) {
+ console.error('โ Failed to add field to AI detected list:', error);
+ }
+}
+
+function sendPreVisibilityFieldsToBrowser() {
+ try {
+ const sendStartTime = Date.now();
+
+ // Create hash of current pre-visibility list
+ const currentListHash = btoa(JSON.stringify(preVisibilityFieldsList.map(f => f.uniqueId))).slice(0, 16);
+
+ if (currentListHash === lastSentPreVisibilityHash) {
+ console.log('โญ๏ธ Skipping pre-visibility browser API call - no changes');
+ return;
+ }
+
+ lastSentPreVisibilityHash = currentListHash;
+
+ console.log('๐ค Sending PRE-VISIBILITY fields list to browser API:', {
+ fieldsCount: preVisibilityFieldsList.length,
+ url: window.location.href,
+ timeSinceStart: sendStartTime - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // Check if browser API is available
+ if (typeof window.wootzSubmitSensitiveFields === 'function') {
+ // Prepare data for browser API - PRE-VISIBILITY FIELDS
+ const browserAPIData = {
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: sendStartTime,
+ trigger: 'pre-visibility-detection',
+ detectionType: 'pattern-based',
+ fields: preVisibilityFieldsList.map(field => ({
+ // Core identification for masking
+ selector: field.selector,
+ id: field.id,
+ name: field.name,
+ type: field.type,
+ tag: field.tag,
+
+ // Masking requirements
+ boundingRect: field.boundingRect,
+ visible: field.visible,
+ disabled: field.disabled,
+
+ // Security classification
+ category: field.category,
+ sensitivity: field.sensitivity,
+ confidence: field.confidence,
+ detectionMethod: field.detectionMethod,
+
+ // Context for smart masking
+ placeholder: field.placeholder,
+ formContext: field.formContext,
+ context: field.context,
+
+ // Metadata
+ detectedAt: field.detectedAt,
+ uniqueId: field.uniqueId
+ }))
+ };
+
+ // Call browser API
+ window.wootzSubmitSensitiveFields(browserAPIData);
+
+ performanceTimestamps.preVisibilityListSent = Date.now();
+ console.log('โ
PRE-VISIBILITY fields sent to browser API:', {
+ fieldsCount: preVisibilityFieldsList.length,
+ sendTime: performanceTimestamps.preVisibilityListSent - sendStartTime + 'ms',
+ totalTime: performanceTimestamps.preVisibilityListSent - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // Log summary for debugging
+ logPreVisibilityBrowserAPISummary(browserAPIData);
+
+ } else {
+ console.warn('โ ๏ธ Browser API wootzSubmitSensitiveFields not available, queuing pre-visibility data...');
+
+ // Store for later when API becomes available
+ window.queuedPreVisibilityFieldsData = {
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: sendStartTime,
+ trigger: 'pre-visibility-detection',
+ detectionType: 'pattern-based',
+ fields: preVisibilityFieldsList
+ };
+ }
+
+ } catch (error) {
+ console.error('โ Failed to send pre-visibility fields to browser API:', error);
+ }
+}
+
+function sendAIDetectedFieldsToBrowser() {
+ try {
+ const sendStartTime = Date.now();
+
+ // Create hash of current AI list
+ const currentListHash = btoa(JSON.stringify(aiDetectedFieldsList.map(f => f.uniqueId))).slice(0, 16);
+
+ if (currentListHash === lastSentAIHash) {
+ console.log('โญ๏ธ Skipping AI browser API call - no changes');
+ return;
+ }
+
+ lastSentAIHash = currentListHash;
+
+ console.log('๐ค Sending AI-DETECTED fields list to browser API:', {
+ fieldsCount: aiDetectedFieldsList.length,
+ url: window.location.href,
+ timeSinceStart: sendStartTime - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // Check if browser API is available
+ if (typeof window.wootzSubmitSensitiveFields === 'function') {
+ // Prepare data for browser API - AI-DETECTED FIELDS
+ const browserAPIData = {
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: sendStartTime,
+ trigger: 'ai-enhanced-detection',
+ detectionType: 'AI-Complete-DOM',
+ fields: aiDetectedFieldsList.map(field => ({
+ // Core identification for masking
+ selector: field.selector,
+ id: field.id,
+ name: field.name,
+ type: field.type,
+ tag: field.tag,
+
+ // Masking requirements
+ boundingRect: field.boundingRect,
+ visible: field.visible,
+ disabled: field.disabled,
+
+ // AI Security classification
+ category: field.category,
+ sensitivity: field.sensitivity,
+ confidence: field.confidence,
+ detectionMethod: field.detectionMethod,
+ aiAnalysis: field.aiAnalysis,
+ aiReason: field.aiReason,
+
+ // Context for smart masking
+ placeholder: field.placeholder,
+ formContext: field.formContext,
+ context: field.context,
+
+ // Metadata
+ detectedAt: field.detectedAt,
+ uniqueId: field.uniqueId
+ }))
+ };
+
+ // Call browser API
+ window.wootzSubmitSensitiveFields(browserAPIData);
+
+ performanceTimestamps.aiListSent = Date.now();
+ console.log('โ
AI-DETECTED fields sent to browser API:', {
+ fieldsCount: aiDetectedFieldsList.length,
+ sendTime: performanceTimestamps.aiListSent - sendStartTime + 'ms',
+ totalTime: performanceTimestamps.aiListSent - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // Log summary for debugging
+ logAIBrowserAPISummary(browserAPIData);
+
+ } else {
+ console.warn('โ ๏ธ Browser API wootzSubmitSensitiveFields not available, queuing AI data...');
+
+ // Store for later when API becomes available
+ window.queuedAIFieldsData = {
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: sendStartTime,
+ trigger: 'ai-enhanced-detection',
+ detectionType: 'AI-Complete-DOM',
+ fields: aiDetectedFieldsList
+ };
+ }
+
+ } catch (error) {
+ console.error('โ Failed to send AI-detected fields to browser API:', error);
+ }
+}
+
+function logPreVisibilityBrowserAPISummary(data) {
+ console.log('๐ PRE-VISIBILITY Browser API Summary:', {
+ totalFields: data.fields.length,
+ detectionType: data.detectionType,
+ categories: data.fields.reduce((acc, field) => {
+ acc[field.category] = (acc[field.category] || 0) + 1;
+ return acc;
+ }, {}),
+ averageConfidence: Math.round(data.fields.reduce((sum, field) => sum + field.confidence, 0) / data.fields.length * 100) + '%',
+ timeFromStart: performanceTimestamps.preVisibilityListSent - performanceTimestamps.scriptStart + 'ms'
+ });
+}
+
+function logAIBrowserAPISummary(data) {
+ console.log('๐ AI-DETECTED Browser API Summary:', {
+ totalFields: data.fields.length,
+ detectionType: data.detectionType,
+ categories: data.fields.reduce((acc, field) => {
+ acc[field.category] = (acc[field.category] || 0) + 1;
+ return acc;
+ }, {}),
+ averageConfidence: Math.round(data.fields.reduce((sum, field) => sum + field.confidence, 0) / data.fields.length * 100) + '%',
+ aiAnalysisTime: performanceTimestamps.aiListSent - performanceTimestamps.aiAnalysisStart + 'ms',
+ totalTime: performanceTimestamps.aiListSent - performanceTimestamps.scriptStart + 'ms'
+ });
+}
+
+// ====== ORIGINAL BROWSER API INTEGRATION FUNCTIONS ======
+
+function addToBrowserSensitiveFieldsList(element, info, detectionData) {
+ try {
+ const fieldData = {
+ // Element identification
+ selector: generateSelector(element),
+ id: element.id || null,
+ name: element.name || null,
+ type: element.type || 'text',
+
+ // Field characteristics for masking
+ tag: element.tagName.toLowerCase(),
+ placeholder: element.placeholder || null,
+ autocomplete: element.autocomplete || null,
+
+ // Position and visibility info
+ boundingRect: element.getBoundingClientRect(),
+ visible: isElementVisible(element),
+ disabled: element.disabled,
+ hidden: element.hidden,
+
+ // Detection metadata
+ detectionMethod: detectionData.detectionMethod,
+ phase: detectionData.phase,
+ confidence: detectionData.confidence,
+ category: detectionData.category,
+ sensitivity: detectionData.sensitivity,
+
+ // Context for better masking
+ context: getFieldContext(element),
+ formContext: {
+ formId: element.closest('form')?.id || null,
+ formAction: element.closest('form')?.action || null,
+ formMethod: element.closest('form')?.method || 'get'
+ },
+
+ // Page context
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+
+ // Unique identifier for list management
+ uniqueId: generateUniqueFieldId(element, detectionData)
+ };
+
+ // Check if this field is already in the list
+ const existingIndex = sensitiveFieldsList.findIndex(field =>
+ field.uniqueId === fieldData.uniqueId
+ );
+
+ if (existingIndex === -1) {
+ // Add new field
+ sensitiveFieldsList.push(fieldData);
+ console.log('โ Added new sensitive field to browser list:', {
+ selector: fieldData.selector,
+ method: fieldData.detectionMethod,
+ category: fieldData.category,
+ total: sensitiveFieldsList.length
+ });
+ } else {
+ // Update existing field with enhanced data
+ sensitiveFieldsList[existingIndex] = {
+ ...sensitiveFieldsList[existingIndex],
+ ...fieldData,
+ // Keep track of detection methods
+ detectionMethods: [
+ ...(sensitiveFieldsList[existingIndex].detectionMethods || [fieldData.detectionMethod]),
+ fieldData.detectionMethod
+ ].filter((method, index, arr) => arr.indexOf(method) === index) // Remove duplicates
+ };
+ console.log('๐ Updated existing sensitive field in browser list:', {
+ selector: fieldData.selector,
+ methods: sensitiveFieldsList[existingIndex].detectionMethods
+ });
+ }
+
+ } catch (error) {
+ console.error('โ Failed to add field to browser sensitive fields list:', error);
+ }
+}
+
+function classifyFieldCategory(info) {
+ const type = info.type?.toLowerCase() || '';
+ const name = (info.name || '').toLowerCase();
+ const id = (info.id || '').toLowerCase();
+ const placeholder = (info.placeholder || '').toLowerCase();
+
+ const allText = `${type} ${name} ${id} ${placeholder}`;
+
+ // Classification logic
+ if (type === 'password' || allText.includes('password') || allText.includes('passwd')) {
+ return 'authentication';
+ } else if (allText.includes('ssn') || allText.includes('social') || allText.includes('security')) {
+ return 'personal-id';
+ } else if (allText.includes('credit') || allText.includes('card') || allText.includes('cvv') || allText.includes('cvc')) {
+ return 'financial';
+ } else if (allText.includes('pin') || allText.includes('code') || allText.includes('otp')) {
+ return 'authentication';
+ } else if (allText.includes('account') || allText.includes('routing') || allText.includes('bank')) {
+ return 'financial';
+ } else if (allText.includes('email') || type === 'email') {
+ return 'personal-info';
+ } else if (allText.includes('phone') || type === 'tel') {
+ return 'personal-info';
+ } else {
+ return 'sensitive-data';
+ }
+}
+
+function generateUniqueFieldId(element, detectionData) {
+ // Generate a unique ID that persists across page reloads for the same field
+ const selector = generateSelector(element);
+ const position = element.getBoundingClientRect();
+ return btoa(`${window.location.pathname}_${selector}_${Math.round(position.top)}_${Math.round(position.left)}`);
+}
+
+function sendSensitiveFieldsToBrowser(trigger) {
+ try {
+ // Create hash of current list to avoid sending duplicates
+ const currentListHash = btoa(JSON.stringify(sensitiveFieldsList.map(f => f.uniqueId))).slice(0, 16);
+
+ if (currentListHash === lastSentListHash && trigger !== 'page-reload') {
+ console.log('โญ๏ธ Skipping browser API call - no changes in sensitive fields list');
+ return;
+ }
+
+ lastSentListHash = currentListHash;
+
+ console.log('๐ค Sending sensitive fields list to browser API:', {
+ trigger: trigger,
+ fieldsCount: sensitiveFieldsList.length,
+ url: window.location.href,
+ timestamp: new Date().toISOString()
+ });
+
+ // Check if browser API is available
+ if (typeof window.wootzSubmitSensitiveFields === 'function') {
+ // Prepare data for browser API
+ const browserAPIData = {
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+ trigger: trigger,
+ fields: sensitiveFieldsList.map(field => ({
+ // Core identification for masking
+ selector: field.selector,
+ id: field.id,
+ name: field.name,
+ type: field.type,
+ tag: field.tag,
+
+ // Masking requirements
+ boundingRect: field.boundingRect,
+ visible: field.visible,
+ disabled: field.disabled,
+
+ // Security classification
+ category: field.category,
+ sensitivity: field.sensitivity,
+ confidence: field.confidence,
+ detectionMethod: field.detectionMethod,
+ detectionMethods: field.detectionMethods || [field.detectionMethod],
+
+ // Context for smart masking
+ placeholder: field.placeholder,
+ formContext: field.formContext,
+ context: field.context,
+
+ // Metadata
+ timestamp: field.timestamp,
+ uniqueId: field.uniqueId
+ }))
+ };
+
+ // Call browser API
+ window.wootzSubmitSensitiveFields(browserAPIData);
+
+ console.log('โ
Successfully sent', sensitiveFieldsList.length, 'sensitive fields to browser API');
+
+ // Log summary for debugging
+ logBrowserAPISummary(browserAPIData);
+
+ } else {
+ console.warn('โ ๏ธ Browser API wootzSubmitSensitiveFields not available, queuing data...');
+
+ // Store for later when API becomes available
+ window.queuedSensitiveFieldsData = {
+ ...{
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+ trigger: trigger
+ },
+ fields: sensitiveFieldsList
+ };
+ }
+
+ } catch (error) {
+ console.error('โ Failed to send sensitive fields to browser API:', error);
+ }
+}
+
+function logBrowserAPISummary(data) {
+ console.log('๐ Browser API Summary:', {
+ totalFields: data.fields.length,
+ categories: data.fields.reduce((acc, field) => {
+ acc[field.category] = (acc[field.category] || 0) + 1;
+ return acc;
+ }, {}),
+ detectionMethods: data.fields.reduce((acc, field) => {
+ (field.detectionMethods || [field.detectionMethod]).forEach(method => {
+ acc[method] = (acc[method] || 0) + 1;
+ });
+ return acc;
+ }, {}),
+ confidenceLevels: data.fields.reduce((acc, field) => {
+ const level = field.confidence >= 0.9 ? 'high' : field.confidence >= 0.7 ? 'medium' : 'low';
+ acc[level] = (acc[level] || 0) + 1;
+ return acc;
+ }, {})
+ });
+}
+
+function clearSensitiveFieldsList() {
+ console.log('๐งน Clearing sensitive fields lists for new page...');
+
+ const preVisibilityCount = preVisibilityFieldsList.length;
+ const aiDetectedCount = aiDetectedFieldsList.length;
+
+ // Clear both lists
+ preVisibilityFieldsList = [];
+ aiDetectedFieldsList = [];
+ lastSentPreVisibilityHash = '';
+ lastSentAIHash = '';
+
+ // Reset performance timestamps for new page
+ performanceTimestamps = {
+ scriptStart: Date.now(),
+ extensionEnabled: null,
+ preVisibilityStart: null,
+ firstElementDetected: null,
+ preVisibilityListSent: null,
+ aiAnalysisStart: null,
+ aiAnalysisComplete: null,
+ aiListSent: null
+ };
+
+ // Send empty lists to browser to clear previous page data
+ if (typeof window.wootzSubmitSensitiveFields === 'function') {
+ // Clear pre-visibility fields
+ window.wootzSubmitSensitiveFields({
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+ trigger: 'page-reload',
+ detectionType: 'pattern-based',
+ fields: []
+ });
+
+ // Clear AI-detected fields
+ window.wootzSubmitSensitiveFields({
+ url: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+ trigger: 'page-reload',
+ detectionType: 'AI-Complete-DOM',
+ fields: []
+ });
+ }
+
+ console.log('โ
Cleared sensitive fields lists:', {
+ preVisibilityFields: preVisibilityCount,
+ aiDetectedFields: aiDetectedCount,
+ total: preVisibilityCount + aiDetectedCount
+ });
+}
+
+// Page reload detection
+window.addEventListener('beforeunload', () => {
+ console.log('๐ Page unloading - preparing for reload...');
+ clearSensitiveFieldsList();
+});
+
+// Page load detection
+window.addEventListener('load', () => {
+ console.log('๐ Page fully loaded - ensuring clean state...');
+ if (isExtensionEnabled) {
+ // Ensure we have a fresh scan after page load
+ setTimeout(() => {
+ performEarlyScan('page-load-complete');
+ }, 500);
+ }
+});
+
+// Check for browser API availability periodically
+function checkBrowserAPIAvailability() {
+ if (typeof window.wootzSubmitSensitiveFields === 'function') {
+
+ // Send queued pre-visibility data
+ if (window.queuedPreVisibilityFieldsData) {
+ console.log('๐ Browser API now available, sending queued pre-visibility data...');
+ window.wootzSubmitSensitiveFields(window.queuedPreVisibilityFieldsData);
+ delete window.queuedPreVisibilityFieldsData;
+ console.log('โ
Queued pre-visibility fields data sent to browser API');
+ }
+
+ // Send queued AI data
+ if (window.queuedAIFieldsData) {
+ console.log('๐ Browser API now available, sending queued AI data...');
+ window.wootzSubmitSensitiveFields(window.queuedAIFieldsData);
+ delete window.queuedAIFieldsData;
+ console.log('โ
Queued AI fields data sent to browser API');
+ }
+
+ // Legacy support for old queued data
+ if (window.queuedSensitiveFieldsData) {
+ console.log('๐ Browser API now available, sending legacy queued data...');
+ window.wootzSubmitSensitiveFields(window.queuedSensitiveFieldsData);
+ delete window.queuedSensitiveFieldsData;
+ console.log('โ
Legacy queued sensitive fields data sent to browser API');
+ }
+ }
+}
+
+// Check for API availability every 2 seconds for the first minute
+let apiCheckInterval = setInterval(() => {
+ checkBrowserAPIAvailability();
+}, 2000);
+
+// Stop checking after 1 minute
+setTimeout(() => {
+ clearInterval(apiCheckInterval);
+ console.log('โฐ Stopped checking for browser API availability');
+
+ // Log final performance summary
+ logFinalPerformanceSummary();
+}, 60000);
+
+function logFinalPerformanceSummary() {
+ console.log('๐ FINAL PERFORMANCE SUMMARY:', {
+ totalExecutionTime: Date.now() - performanceTimestamps.scriptStart + 'ms',
+ timings: {
+ scriptStart: '0ms (baseline)',
+ extensionEnabled: performanceTimestamps.extensionEnabled ?
+ (performanceTimestamps.extensionEnabled - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ preVisibilityStart: performanceTimestamps.preVisibilityStart ?
+ (performanceTimestamps.preVisibilityStart - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ firstElementDetected: performanceTimestamps.firstElementDetected ?
+ (performanceTimestamps.firstElementDetected - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ preVisibilityListSent: performanceTimestamps.preVisibilityListSent ?
+ (performanceTimestamps.preVisibilityListSent - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ rawDOMAnalysisStart: performanceTimestamps.rawDOMAnalysisStart ?
+ (performanceTimestamps.rawDOMAnalysisStart - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ aiAnalysisStart: performanceTimestamps.aiAnalysisStart ?
+ (performanceTimestamps.aiAnalysisStart - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ aiAnalysisComplete: performanceTimestamps.aiAnalysisComplete ?
+ (performanceTimestamps.aiAnalysisComplete - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ aiListSent: performanceTimestamps.aiListSent ?
+ (performanceTimestamps.aiListSent - performanceTimestamps.scriptStart) + 'ms' : 'N/A'
+ },
+ phases: {
+ initialization: performanceTimestamps.extensionEnabled ?
+ (performanceTimestamps.extensionEnabled - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ preVisibilityDetection: performanceTimestamps.firstElementDetected && performanceTimestamps.preVisibilityStart ?
+ (performanceTimestamps.firstElementDetected - performanceTimestamps.preVisibilityStart) + 'ms' : 'N/A',
+ preVisibilityProcessing: performanceTimestamps.preVisibilityListSent && performanceTimestamps.firstElementDetected ?
+ (performanceTimestamps.preVisibilityListSent - performanceTimestamps.firstElementDetected) + 'ms' : 'N/A',
+ rawDOMAnalysis: performanceTimestamps.rawDOMAnalysisStart ?
+ 'Triggered automatically after DOM capture' : 'N/A',
+ aiAnalysis: performanceTimestamps.aiAnalysisComplete && performanceTimestamps.aiAnalysisStart ?
+ (performanceTimestamps.aiAnalysisComplete - performanceTimestamps.aiAnalysisStart) + 'ms' : 'N/A',
+ aiProcessing: performanceTimestamps.aiListSent && performanceTimestamps.aiAnalysisComplete ?
+ (performanceTimestamps.aiListSent - performanceTimestamps.aiAnalysisComplete) + 'ms' : 'N/A'
+ },
+ results: {
+ preVisibilityFields: preVisibilityFieldsList.length,
+ aiDetectedFields: aiDetectedFieldsList.length,
+ totalUniqueFields: preVisibilityFieldsList.length + aiDetectedFieldsList.length
+ },
+ criticalPath: {
+ timeToFirstMasking: performanceTimestamps.preVisibilityListSent ?
+ (performanceTimestamps.preVisibilityListSent - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ timeToRawDOMAnalysis: performanceTimestamps.rawDOMAnalysisStart ?
+ (performanceTimestamps.rawDOMAnalysisStart - performanceTimestamps.scriptStart) + 'ms' : 'N/A',
+ timeToEnhancedDetection: performanceTimestamps.aiListSent ?
+ (performanceTimestamps.aiListSent - performanceTimestamps.scriptStart) + 'ms' : 'N/A'
+ }
+ });
+}
+
+async function prepareForAIDetectionSync(newElements) {
+ console.log('๐ฏ Preparing COMPLETE DOM for SYNCHRONOUS AI analysis...');
+
+ // Instead of filtering elements, send complete DOM to AI
+ console.log('๐ Sending COMPLETE DOM to AI for comprehensive field classification');
+
+ // Create a promise for this AI analysis
+ const analysisPromise = sendCompleteDOMToAI();
+ pendingAIAnalysis.push(analysisPromise);
+
+ // Wait for this analysis to complete
+ await analysisPromise;
+
+ // Remove from pending list
+ const index = pendingAIAnalysis.indexOf(analysisPromise);
+ if (index > -1) {
+ pendingAIAnalysis.splice(index, 1);
+ }
+}
+
+async function sendCompleteDOMToAI() {
+ try {
+ const aiStartTime = Date.now();
+ console.log('๐ Capturing COMPLETE DOM for comprehensive AI analysis...');
+ console.log('โฑ๏ธ TIMESTAMP: AI analysis DOM capture started', {
+ timeSinceStart: aiStartTime - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ // Get the complete, clean DOM
+ const completeDOM = createComprehensiveCleanDOM();
+
+ const domPreparedTime = Date.now();
+ console.log('๐ค Sending COMPLETE DOM to AI for sensitive field detection...', {
+ domSize: Math.round(completeDOM.length / 1024) + 'KB',
+ domPrepTime: domPreparedTime - aiStartTime + 'ms'
+ });
+
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({
+ action: 'analyzeCompleteDOM',
+ completeDOM: completeDOM,
+ pageURL: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+ comprehensive: true
+ }, (response) => {
+ if (response?.success) {
+ performanceTimestamps.aiAnalysisComplete = Date.now();
+ console.log('โ
COMPLETE DOM Analysis complete:', {
+ sensitiveFieldsFound: response.sensitiveFields?.length || 0,
+ processingTime: response.processingTime,
+ analysisType: 'comprehensive-dom',
+ totalAITime: performanceTimestamps.aiAnalysisComplete - aiStartTime + 'ms',
+ timeSinceStart: performanceTimestamps.aiAnalysisComplete - performanceTimestamps.scriptStart + 'ms'
+ });
+
+ if (response.sensitiveFields && response.sensitiveFields.length > 0) {
+ handleCompleteDOMResults(response.sensitiveFields);
+ }
+ resolve(response);
+ } else {
+ console.error('โ COMPLETE DOM Analysis failed:', response?.error);
+ reject(new Error(response?.error || 'Complete DOM analysis failed'));
+ }
+ });
+ });
+
+ } catch (error) {
+ console.error('โ Failed to send complete DOM to AI:', error);
+ throw error;
+ }
+}
+
+function createComprehensiveCleanDOM() {
+ console.log('๐งน Creating comprehensive clean DOM for AI analysis...');
+
+ // Clone the entire document
+ const clonedDoc = document.cloneNode(true);
+ const clonedHTML = clonedDoc.documentElement;
+
+ // Remove script and style tags but keep structure
+ const elementsToRemove = clonedHTML.querySelectorAll('script, style, noscript');
+ elementsToRemove.forEach(el => el.remove());
+
+ // Clean all elements but preserve form structure completely
+ const allElements = clonedHTML.querySelectorAll('*');
+ allElements.forEach(el => {
+ // Remove event handlers
+ const attributes = Array.from(el.attributes || []);
+ attributes.forEach(attr => {
+ if (attr.name.startsWith('on') || attr.name.includes('javascript:')) {
+ el.removeAttribute(attr.name);
+ }
+ });
+
+ // For form elements, preserve ALL attributes but clear values
+ if (['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName)) {
+ // Clear values but keep all other attributes for AI context
+ if (el.type === 'password' || el.type === 'text' || el.type === 'email' || el.type === 'tel') {
+ el.removeAttribute('value');
+ if (el.value) el.value = '';
+ }
+ }
+
+ // Preserve labels, placeholders, and surrounding context
+ // Truncate only very large text blocks that aren't form-related
+ if (['P', 'DIV', 'SPAN'].includes(el.tagName) &&
+ !el.querySelector('input, textarea, select') &&
+ !el.closest('label') &&
+ el.textContent.length > 500) {
+ el.textContent = el.textContent.substring(0, 250) + '... [TRUNCATED]';
+ }
+ });
+
+ // Add comprehensive analysis context
+ const analysisContext = clonedHTML.ownerDocument.createElement('div');
+ analysisContext.setAttribute('data-ai-analysis-context', 'true');
+ analysisContext.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ clonedHTML.insertBefore(analysisContext, clonedHTML.firstChild);
+
+ const finalDOM = clonedHTML.outerHTML;
+ console.log('โ
Comprehensive DOM prepared:', {
+ originalSize: document.documentElement.outerHTML.length,
+ cleanedSize: finalDOM.length,
+ compressionRatio: Math.round((finalDOM.length / document.documentElement.outerHTML.length) * 100) + '%'
+ });
+
+ return finalDOM;
+}
+
+function detectPageType() {
+ const url = window.location.href.toLowerCase();
+ const title = document.title.toLowerCase();
+
+ if (url.includes('login') || title.includes('login') || title.includes('sign in')) {
+ return 'login-page';
+ } else if (url.includes('register') || url.includes('signup') || title.includes('sign up') || title.includes('register')) {
+ return 'registration-page';
+ } else if (url.includes('checkout') || url.includes('payment') || title.includes('checkout') || title.includes('payment')) {
+ return 'payment-page';
+ } else if (url.includes('profile') || url.includes('account') || title.includes('profile') || title.includes('account')) {
+ return 'profile-page';
+ } else if (url.includes('bank') || title.includes('bank')) {
+ return 'banking-page';
+ } else {
+ return 'general-page';
+ }
+}
+
+function handleCompleteDOMResults(aiResults) {
+ const processingStartTime = Date.now();
+ console.log('๐ Processing', aiResults.length, 'sensitive fields from COMPLETE DOM analysis...');
+ console.log('โฑ๏ธ TIMESTAMP: AI results processing started', {
+ timeSinceStart: processingStartTime - performanceTimestamps.scriptStart + 'ms',
+ timeSinceAIStart: processingStartTime - performanceTimestamps.aiAnalysisStart + 'ms'
+ });
+
+ aiResults.forEach(result => {
+ try {
+ // Find element using the CSS selector provided by AI
+ let element = null;
+
+ // Try multiple selector strategies
+ if (result.selector) {
+ element = document.querySelector(result.selector);
+ }
+
+ // If not found by selector, try by ID
+ if (!element && result.id) {
+ element = document.getElementById(result.id);
+ }
+
+ // If not found by ID, try by name
+ if (!element && result.name) {
+ element = document.querySelector(`input[name="${result.name}"], textarea[name="${result.name}"], select[name="${result.name}"]`);
+ }
+
+ if (element) {
+ console.log('๐จ AI DETECTED SENSITIVE FIELD (Complete DOM):', {
+ selector: result.selector,
+ type: result.type,
+ category: result.category,
+ sensitivity: result.sensitivity,
+ reason: result.reason,
+ confidence: result.confidence,
+ aiAnalysis: result.aiAnalysis || 'N/A',
+ fieldContext: result.context || 'N/A'
+ });
+
+ // Add AI-detected field to SEPARATE AI list (not merged with pre-visibility)
+ const elementInfo = extractElementInfo(element, 0);
+ addToAIDetectedFieldsList(element, elementInfo, {
+ detectionMethod: 'AI-Complete-DOM',
+ phase: 'ai-enhancement',
+ confidence: result.confidence || 0.9,
+ category: result.category || 'sensitive-data',
+ sensitivity: result.sensitivity || 'high',
+ aiAnalysis: result.aiAnalysis,
+ aiReason: result.reason
+ });
+
+ // Enhanced logging for complete DOM detection
+ logCompleteDOMSensitiveField(element, result);
+ } else {
+ console.warn('โ ๏ธ AI detected sensitive field but element not found:', {
+ selector: result.selector,
+ id: result.id,
+ name: result.name,
+ type: result.type
+ });
+ }
+ } catch (error) {
+ console.error('โ Error processing complete DOM result:', error);
+ }
+ });
+
+ const processingCompleteTime = Date.now();
+ console.log('โฑ๏ธ TIMESTAMP: AI results processing complete', {
+ processingTime: processingCompleteTime - processingStartTime + 'ms',
+ totalAIFieldsFound: aiDetectedFieldsList.length
+ });
+
+ // Send SEPARATE AI-detected list to browser API
+ sendAIDetectedFieldsToBrowser();
+}
+
+function logCompleteDOMSensitiveField(element, result) {
+ try {
+ // Enhanced marking for complete DOM analysis
+ element.setAttribute('data-sensitive-field', 'true');
+ element.setAttribute('data-detection-method', 'AI-Complete-DOM');
+ element.setAttribute('data-sensitivity-level', result.sensitivity);
+ element.setAttribute('data-sensitivity-category', result.category);
+ element.setAttribute('data-ai-confidence', result.confidence);
+ element.setAttribute('data-ai-analysis', result.aiAnalysis || '');
+
+ // Comprehensive logging
+ console.log('๐ COMPLETE DOM AI SENSITIVE FIELD:', {
+ element: element,
+ selector: generateSelector(element),
+ aiClassification: {
+ category: result.category,
+ sensitivity: result.sensitivity,
+ confidence: Math.round(result.confidence * 100) + '%',
+ reason: result.reason,
+ aiAnalysis: result.aiAnalysis,
+ context: result.context
+ },
+ fieldDetails: {
+ type: element.type,
+ name: element.name,
+ id: element.id,
+ placeholder: element.placeholder,
+ value: element.value ? '[REDACTED]' : '[EMPTY]',
+ visible: isElementVisible(element),
+ disabled: element.disabled,
+ hidden: element.hidden
+ },
+ pageContext: {
+ url: window.location.href,
+ title: document.title,
+ pageType: detectPageType()
+ },
+ timestamp: new Date().toISOString()
+ });
+
+ // Enhanced browser-level messaging
+ window.postMessage({
+ type: 'COMPLETE_DOM_SENSITIVE_FIELD_DETECTED',
+ source: 'extension-complete-dom',
+ data: {
+ selector: generateSelector(element),
+ aiClassification: result,
+ fieldDetails: {
+ type: element.type,
+ name: element.name,
+ id: element.id,
+ placeholder: element.placeholder
+ },
+ detectionMethod: 'AI-Complete-DOM',
+ url: window.location.href,
+ timestamp: new Date().toISOString()
+ }
+ }, '*');
+
+ } catch (error) {
+ console.error('โ Failed to log complete DOM sensitive field detection:', error);
+ }
+}
+// // Get page characteristics
+// const pageStats = analyzePageCharacteristics();
+
+// console.log('๐ Page Analysis (SYNC):', pageStats);
+
+// // Decision logic for choosing strategy
+// if (pageStats.shouldUseBatch) {
+// console.log('๐ฆ Using SYNCHRONOUS BATCH DOM analysis strategy');
+// return await sendBatchDOMAnalysisSync(candidates, pageStats);
+// } else {
+// console.log('๐๏ธ Using SYNCHRONOUS VIEWPORT analysis strategy');
+// return await sendVisibleViewportDOMSync(candidates);
+// }
+// }
+
+async function chooseAnalysisStrategySync(candidates) {
+ // This function is now simplified since we're using complete DOM approach
+ console.log('๐ฏ Using COMPLETE DOM analysis approach for all cases');
+ return await sendCompleteDOMToAI();
+}
+
+async function sendBatchDOMAnalysisSync(candidates, pageStats) {
+ try {
+ console.log('๐ฆ Capturing complete DOM for SYNCHRONOUS batch analysis...');
+
+ const fullDOM = createCleanDOMForBatch();
+
+ console.log('๐ค Sending complete DOM SYNCHRONOUSLY to AI agent...');
+ console.log('๐ Full DOM size:', Math.round(fullDOM.length / 1024), 'KB');
+
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({
+ action: 'analyzeBatchDOM',
+ fullDOM: fullDOM,
+ pageURL: window.location.href,
+ pageTitle: document.title,
+ pageStats: pageStats,
+ timestamp: Date.now(),
+ synchronous: true
+ }, (response) => {
+ if (response?.success) {
+ console.log('โ
SYNCHRONOUS Batch DOM Analysis complete:', {
+ sensitiveFieldsFound: response.results.length,
+ batchesProcessed: response.batchCount,
+ processingTime: response.processingTime
+ });
+
+ if (response.results.length > 0) {
+ handleSensitiveFieldResults(response.results, candidates);
+ }
+ resolve(response);
+ } else {
+ console.error('โ SYNCHRONOUS Batch DOM Analysis failed:', response?.error);
+ reject(new Error(response?.error || 'Analysis failed'));
+ }
+ });
+ });
+
+ } catch (error) {
+ console.error('โ Failed to send SYNCHRONOUS batch DOM to AI:', error);
+ throw error;
+ }
+}
+
+async function sendVisibleViewportDOMSync(candidates) {
+ try {
+ console.log('๐๏ธ Capturing visible viewport DOM for SYNCHRONOUS AI analysis...');
+
+ const viewport = {
+ width: window.innerWidth,
+ height: window.innerHeight,
+ scrollX: window.scrollX,
+ scrollY: window.scrollY
+ };
+
+ const visibleDOM = captureVisibleDOM(viewport);
+
+ console.log('๐ค Sending visible viewport DOM SYNCHRONOUSLY to AI agent...');
+
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({
+ action: 'analyzeViewportDOM',
+ visibleDOM: visibleDOM,
+ viewport: viewport,
+ pageURL: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+ synchronous: true
+ }, (response) => {
+ if (response?.success) {
+ console.log('โ
SYNCHRONOUS Viewport DOM Analysis complete:', {
+ sensitiveFieldsFound: response.results.length,
+ processingTime: response.processingTime
+ });
+
+ if (response.results.length > 0) {
+ handleSensitiveFieldResults(response.results, candidates);
+ }
+ resolve(response);
+ } else {
+ console.error('โ SYNCHRONOUS Viewport DOM Analysis failed:', response?.error);
+ reject(new Error(response?.error || 'Analysis failed'));
+ }
+ });
+ });
+
+ } catch (error) {
+ console.error('โ Failed to send SYNCHRONOUS viewport DOM to AI:', error);
+ throw error;
+ }
+}
+
+async function waitForPendingAIAnalysis() {
+ if (pendingAIAnalysis.length === 0) {
+ console.log('โ
No pending AI analysis to wait for');
+ return;
+ }
+
+ console.log('โณ Waiting for', pendingAIAnalysis.length, 'pending AI analyses to complete...');
+
+ try {
+ await Promise.all(pendingAIAnalysis);
+ console.log('โ
All AI analyses completed before page visibility');
+ } catch (error) {
+ console.error('โ Some AI analyses failed:', error);
+ } finally {
+ pendingAIAnalysis = [];
+ }
+}
+
+// Rest of the smart DOM monitoring functions...
+function startSmartDOMMonitoring() {
+ console.log('๐ง Starting SMART DOM monitoring (post early detection)...');
+
+ // COMMENTED OUT: Set up user activity tracking
+ // setupUserActivityTracking();
+
+ // COMMENTED OUT: Set up smart DOM observer for dynamic changes
+ // setupSmartDOMObserver();
+
+ console.log('โ
Smart DOM monitoring started (DISABLED - only raw DOM analysis active)');
+}
+
+function setupUserActivityTracking() {
+ const userEvents = ['click', 'keydown', 'input', 'focus', 'scroll', 'mousemove'];
+
+ userEvents.forEach(event => {
+ document.addEventListener(event, () => {
+ isUserActive = true;
+
+ // Reset user activity timeout
+ clearTimeout(userActivityTimeout);
+ userActivityTimeout = setTimeout(() => {
+ isUserActive = false;
+ console.log('๐ด User inactive - reducing monitoring');
+ }, CONFIG.USER_ACTIVITY_TIMEOUT);
+ });
+ });
+}
+
+function setupSmartDOMObserver() {
+ if (window.domObserver) {
+ window.domObserver.disconnect();
+ }
+
+ window.domObserver = new MutationObserver((mutations) => {
+ // Filter mutations that might contain form elements
+ const relevantMutations = mutations.filter(mutation => {
+ if (mutation.type === 'childList') {
+ return Array.from(mutation.addedNodes).some(node =>
+ node.nodeType === Node.ELEMENT_NODE &&
+ (node.matches('input, textarea, select, form') ||
+ node.querySelector('input, textarea, select'))
+ );
+ }
+
+ if (mutation.type === 'attributes') {
+ return mutation.target.matches('input, textarea, select') ||
+ ['type', 'name', 'id', 'placeholder'].includes(mutation.attributeName);
+ }
+
+ return false;
+ });
+
+ if (relevantMutations.length > 0) {
+ console.log('๐ Relevant DOM changes detected:', relevantMutations.length, 'mutations');
+ scheduleIntelligentScan('dom-change');
+ }
+ });
+
+ window.domObserver.observe(document.body || document.documentElement, {
+ childList: true,
+ subtree: true,
+ attributes: true,
+ attributeFilter: ['type', 'name', 'id', 'placeholder', 'class', 'data-*']
+ });
+}
+
+function scheduleIntelligentScan(trigger) {
+ clearTimeout(scanTimeout);
+
+ if (!isUserActive && trigger !== 'initial') {
+ console.log('โธ๏ธ Skipping scan - user inactive');
+ return;
+ }
+
+ scanTimeout = setTimeout(() => {
+ performScan(trigger);
+ }, CONFIG.SCAN_DEBOUNCE_DELAY);
+}
+
+function performScan(trigger) {
+ if (!isExtensionEnabled) return;
+
+ console.log(`๐ Performing intelligent scan (${trigger})...`);
+
+ const inputs = document.querySelectorAll('input, textarea, select');
+ const currentHash = generateElementsHash(inputs);
+
+ if (currentHash === lastScanHash && trigger !== 'initial') {
+ console.log('โญ๏ธ Skipping scan - no changes detected');
+ return;
+ }
+
+ lastScanHash = currentHash;
+ console.log(`๐ Found ${inputs.length} input elements`);
+
+ let newElementsCount = 0;
+
+ inputs.forEach((element, index) => {
+ const elementKey = generateElementKey(element);
+
+ if (detectedElements.has(elementKey)) {
+ return;
+ }
+
+ newElementsCount++;
+ const elementInfo = extractElementInfo(element, index + 1);
+ console.log(`๐ NEW Element ${index + 1}:`, elementInfo);
+
+ detectedElements.set(elementKey, {
+ element: element,
+ info: elementInfo,
+ timestamp: Date.now(),
+ scannedAt: trigger
+ });
+ });
+
+ if (newElementsCount > 0 || trigger === 'initial') {
+ console.log(`โ
Scan complete. New elements: ${newElementsCount}, Total tracked: ${detectedElements.size}`);
+
+ // COMMENTED OUT: AI analysis trigger
+ // if (newElementsCount >= CONFIG.SIGNIFICANT_CHANGE_THRESHOLD) {
+ // console.log('๐ค Significant changes detected - ready for AI analysis');
+ // prepareForAIDetection(Array.from(detectedElements.values()).slice(-newElementsCount));
+ // }
+ }
+}
+
+function generateElementsHash(elements) {
+ const summary = Array.from(elements).map(el =>
+ `${el.tagName}_${el.type}_${el.name}_${el.id}`
+ ).join('|');
+
+ return btoa(summary).slice(0, 10);
+}
+
+function extractElementInfo(element, index) {
+ return {
+ index: index,
+ tag: element.tagName.toLowerCase(),
+ type: element.type || 'text',
+ id: element.id || '(no id)',
+ name: element.name || '(no name)',
+ placeholder: element.placeholder || '(no placeholder)',
+ className: element.className || '(no class)',
+ visible: isElementVisible(element),
+ value: element.value ? '[HAS_VALUE]' : '[EMPTY]',
+ autocomplete: element.autocomplete || '(none)',
+ required: element.required || false,
+ disabled: element.disabled || false,
+ hidden: element.hidden || false,
+ ariaHidden: element.getAttribute('aria-hidden') || 'false'
+ };
+}
+
+function generateElementKey(element) {
+ try {
+ const rect = element.getBoundingClientRect();
+
+ return `${element.tagName}_${element.type || 'text'}_${element.id || ''}_${element.name || ''}_${Math.round(rect.top)}_${Math.round(rect.left)}`;
+ } catch (error) {
+ // Fallback for elements that might not have bounding rect
+ return `${element.tagName}_${element.type || 'text'}_${element.id || ''}_${element.name || ''}_${Date.now()}`;
+ }
+}
+
+function isElementVisible(element) {
+ try {
+ const rect = element.getBoundingClientRect();
+ const style = window.getComputedStyle(element);
+
+ return rect.width > 0 &&
+ rect.height > 0 &&
+ style.display !== 'none' &&
+ style.visibility !== 'hidden' &&
+ style.opacity !== '0';
+ } catch (e) {
+ return false;
+ }
+}
+
+function prepareForAIDetection(newElements) {
+ console.log('๐ฏ Preparing elements for AI detection:', newElements.length);
+
+ const aiCandidates = newElements.filter(item =>
+ item.info.visible &&
+ (item.info.name !== '(no name)' ||
+ item.info.id !== '(no id)' ||
+ item.info.placeholder !== '(no placeholder)') &&
+ item.info.type !== 'hidden' &&
+ item.info.type !== 'submit'
+ );
+
+ if (aiCandidates.length > 0) {
+ console.log('๐ค AI candidates ready:', aiCandidates.map(item => ({
+ type: item.info.type,
+ name: item.info.name,
+ placeholder: item.info.placeholder
+ })));
+
+ // Choose analysis strategy based on page characteristics
+ chooseAnalysisStrategy(aiCandidates);
+ }
+}
+
+function chooseAnalysisStrategy(candidates) {
+ // Get page characteristics
+ const pageStats = analyzePageCharacteristics();
+
+ console.log('๐ Page Analysis:', pageStats);
+
+ // Decision logic for choosing strategy
+ if (pageStats.shouldUseBatch) {
+ console.log('๐ฆ Using BATCH DOM analysis strategy');
+ sendBatchDOMAnalysis(candidates, pageStats);
+ } else {
+ console.log('๐๏ธ Using VIEWPORT analysis strategy');
+ sendVisibleViewportDOM(candidates);
+ }
+}
+
+function analyzePageCharacteristics() {
+ const allInputs = document.querySelectorAll('input, textarea, select');
+ const domSize = document.documentElement.outerHTML.length;
+ const pageHeight = document.documentElement.scrollHeight;
+ const viewportHeight = window.innerHeight;
+ const formsCount = document.querySelectorAll('form').length;
+
+ // Decision criteria
+ const isSmallPage = domSize < 100000; // Less than 100KB
+ const hasLimitedFields = allInputs.length < 30; // Less than 30 form fields
+ const isShortPage = pageHeight < (viewportHeight * 3); // Less than 3 viewport heights
+ const hasSimpleStructure = formsCount <= 2; // 2 or fewer forms
+
+ const shouldUseBatch = isSmallPage && hasLimitedFields && isShortPage && hasSimpleStructure;
+
+ return {
+ domSize: Math.round(domSize / 1024), // KB
+ totalFields: allInputs.length,
+ pageHeight: pageHeight,
+ viewportHeight: viewportHeight,
+ scrollRatio: Math.round(pageHeight / viewportHeight * 10) / 10,
+ formsCount: formsCount,
+ shouldUseBatch: shouldUseBatch,
+ strategy: shouldUseBatch ? 'batch' : 'viewport'
+ };
+}
+
+async function sendBatchDOMAnalysis(candidates, pageStats) {
+ try {
+ console.log('๐ฆ Capturing complete DOM for batch analysis...');
+
+ // Get clean DOM for batch processing
+ const fullDOM = createCleanDOMForBatch();
+
+ console.log('๐ค Sending complete DOM in batches to AI agent...');
+ console.log('๐ Full DOM size:', Math.round(fullDOM.length / 1024), 'KB');
+
+ const response = await chrome.runtime.sendMessage({
+ action: 'analyzeBatchDOM',
+ fullDOM: fullDOM,
+ pageURL: window.location.href,
+ pageTitle: document.title,
+ pageStats: pageStats,
+ timestamp: Date.now()
+ });
+
+ if (response?.success) {
+ console.log('โ
Batch DOM Analysis complete:', {
+ sensitiveFieldsFound: response.results.length,
+ batchesProcessed: response.batchCount,
+ processingTime: response.processingTime,
+ method: response.method
+ });
+
+ if (response.results.length > 0) {
+ handleSensitiveFieldResults(response.results, candidates);
+ }
+ } else {
+ console.error('โ Batch DOM Analysis failed:', response?.error);
+ // Fallback to viewport analysis
+ console.log('๐ Falling back to viewport analysis...');
+ sendVisibleViewportDOM(candidates);
+ }
+
+ } catch (error) {
+ console.error('โ Failed to send batch DOM to AI:', error);
+ // Fallback to viewport analysis
+ sendVisibleViewportDOM(candidates);
+ }
+}
+
+function createCleanDOMForBatch() {
+ // Clone the document
+ const clonedDoc = document.cloneNode(true);
+ const clonedHTML = clonedDoc.documentElement;
+
+ // Remove unnecessary elements
+ const elementsToRemove = clonedHTML.querySelectorAll(
+ 'script, style, noscript, iframe, embed, object, video, audio, canvas'
+ );
+ elementsToRemove.forEach(el => el.remove());
+
+ // Clean all elements
+ const allElements = clonedHTML.querySelectorAll('*');
+ allElements.forEach(el => {
+ // Remove event handlers
+ const attributes = Array.from(el.attributes || []);
+ attributes.forEach(attr => {
+ if (attr.name.startsWith('on') || attr.name.includes('javascript:')) {
+ el.removeAttribute(attr.name);
+ }
+ });
+
+ // Clear sensitive values but keep structure
+ if (el.tagName === 'INPUT') {
+ if (['password', 'text', 'email', 'tel'].includes(el.type)) {
+ el.removeAttribute('value');
+ if (el.value) el.value = '';
+ }
+ }
+
+ // Remove large text content but keep structure
+ if (['P', 'DIV', 'SPAN'].includes(el.tagName) && el.textContent.length > 200) {
+ el.textContent = el.textContent.substring(0, 100) + '...';
+ }
+ });
+
+ // Add batch processing markers
+ const batchInfo = clonedHTML.ownerDocument.createElement('div');
+ batchInfo.setAttribute('data-batch-info', 'true');
+ batchInfo.innerHTML = `
+
+
+
+
+
+
+ `;
+ clonedHTML.insertBefore(batchInfo, clonedHTML.firstChild);
+
+ return clonedHTML.outerHTML;
+}
+
+async function sendVisibleViewportDOM(candidates) {
+ try {
+ console.log('๐๏ธ Capturing visible viewport DOM for AI analysis...');
+
+ // Get viewport dimensions
+ const viewport = {
+ width: window.innerWidth,
+ height: window.innerHeight,
+ scrollX: window.scrollX,
+ scrollY: window.scrollY
+ };
+
+ // Get only the visible part of the DOM
+ const visibleDOM = captureVisibleDOM(viewport);
+
+ // Track viewport for scroll detection
+ trackViewportChanges();
+
+ console.log('๐ค Sending visible viewport DOM to AI agent...');
+ console.log('๐ Viewport:', `${viewport.width}x${viewport.height} at (${viewport.scrollX}, ${viewport.scrollY})`);
+ console.log('๏ฟฝ Visible DOM size:', Math.round(visibleDOM.length / 1024), 'KB');
+
+ const response = await chrome.runtime.sendMessage({
+ action: 'analyzeViewportDOM',
+ visibleDOM: visibleDOM,
+ viewport: viewport,
+ pageURL: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now()
+ });
+
+ if (response?.success) {
+ console.log('โ
Viewport DOM Analysis complete:', {
+ sensitiveFieldsFound: response.results.length,
+ processingTime: response.processingTime,
+ viewportSize: `${viewport.width}x${viewport.height}`
+ });
+
+ if (response.results.length > 0) {
+ handleSensitiveFieldResults(response.results, candidates);
+ }
+ } else {
+ console.error('โ Viewport DOM Analysis failed:', response?.error);
+ }
+
+ } catch (error) {
+ console.error('โ Failed to send viewport DOM to AI:', error);
+ }
+}
+
+function captureVisibleDOM(viewport) {
+ // Get all elements that are at least partially visible in the viewport
+ const visibleElements = [];
+ const allElements = document.querySelectorAll('*');
+
+ allElements.forEach(element => {
+ if (isElementInViewport(element, viewport)) {
+ visibleElements.push(element);
+ }
+ });
+
+ console.log('๐ Found', visibleElements.length, 'elements in viewport');
+
+ // Create a minimal DOM structure with only visible elements
+ const visibleContainer = document.createElement('div');
+ visibleContainer.setAttribute('data-viewport-analysis', 'true');
+
+ // Add page context
+ const contextInfo = document.createElement('div');
+ contextInfo.innerHTML = `
+
+
+
+
+
+
+ `;
+ visibleContainer.appendChild(contextInfo);
+
+ // Group elements by their form containers
+ const formGroups = new Map();
+
+ visibleElements.forEach(element => {
+ // Find the closest form or create a general group
+ const form = element.closest('form') || document.body;
+ const formId = form.id || form.className || 'general';
+
+ if (!formGroups.has(formId)) {
+ formGroups.set(formId, []);
+ }
+
+ // Clone element and clean it
+ const clonedElement = element.cloneNode(true);
+ cleanElementForAI(clonedElement);
+ formGroups.get(formId).push(clonedElement);
+ });
+
+ // Add grouped elements to container
+ formGroups.forEach((elements, formId) => {
+ const formSection = document.createElement('div');
+ formSection.setAttribute('data-form-group', formId);
+
+ elements.forEach(element => {
+ formSection.appendChild(element);
+ });
+
+ visibleContainer.appendChild(formSection);
+ });
+
+ // Limit size and return
+ let domString = visibleContainer.outerHTML;
+ if (domString.length > 30000) {
+ console.log('โ ๏ธ Visible DOM too large, truncating to 30KB');
+ domString = domString.substring(0, 30000) + '... [VIEWPORT_TRUNCATED]';
+ }
+
+ return domString;
+}
+
+function isElementInViewport(element, viewport) {
+ try {
+ const rect = element.getBoundingClientRect();
+
+ // Check if element is at least partially visible
+ return (
+ rect.bottom > 0 &&
+ rect.right > 0 &&
+ rect.top < viewport.height &&
+ rect.left < viewport.width &&
+ rect.width > 0 &&
+ rect.height > 0
+ );
+ } catch (error) {
+ return false;
+ }
+}
+
+function cleanElementForAI(element) {
+ // Remove scripts and event handlers
+ if (element.tagName === 'SCRIPT' || element.tagName === 'STYLE') {
+ element.remove();
+ return;
+ }
+
+ // Remove event handler attributes
+ const attributes = Array.from(element.attributes || []);
+ attributes.forEach(attr => {
+ if (attr.name.startsWith('on') || attr.name.includes('javascript:')) {
+ element.removeAttribute(attr.name);
+ }
+ });
+
+ // Clear sensitive values but keep structure
+ if (element.tagName === 'INPUT') {
+ if (element.type === 'password' || element.type === 'text' || element.type === 'email') {
+ element.removeAttribute('value');
+ if (element.value) element.value = '';
+ }
+ }
+
+ // Recursively clean child elements
+ Array.from(element.children || []).forEach(child => {
+ cleanElementForAI(child);
+ });
+}
+
+let scrollTimeout;
+let lastViewportHash = '';
+
+function trackViewportChanges() {
+ // COMMENTED OUT: Viewport change tracking
+ // const handleScroll = () => {
+ // if (isUserActive) {
+ // clearTimeout(viewportChangeTimeout);
+ // viewportChangeTimeout = setTimeout(() => {
+ // analyzeNewViewport();
+ // }, 500);
+ // }
+ // };
+
+ // COMMENTED OUT: Remove existing scroll listener
+ // document.removeEventListener('scroll', handleScroll);
+
+ // COMMENTED OUT: Add scroll listener
+ // document.addEventListener('scroll', handleScroll, { passive: true });
+
+ console.log('๐ Viewport change tracking DISABLED - only raw DOM analysis active');
+}
+
+function analyzeNewViewport() {
+ try {
+ const currentViewport = {
+ width: window.innerWidth,
+ height: window.innerHeight,
+ scrollX: window.scrollX,
+ scrollY: window.scrollY
+ };
+
+ // Create viewport hash to detect significant changes
+ const viewportHash = `${Math.round(currentViewport.scrollX / 100)}_${Math.round(currentViewport.scrollY / 100)}`;
+
+ if (viewportHash === lastViewportHash) {
+ console.log('๐๏ธ Viewport change too small, skipping analysis');
+ return;
+ }
+
+ lastViewportHash = viewportHash;
+
+ console.log('๐ New viewport detected, analyzing...', currentViewport);
+
+ // Find new visible form elements
+ const newVisibleElements = findNewVisibleElements(currentViewport);
+
+ if (newVisibleElements.length > 0) {
+ console.log('๐ Found', newVisibleElements.length, 'new visible elements');
+
+ // COMMENTED OUT: Send new viewport for analysis
+ // sendNewViewportAnalysis(currentViewport, newVisibleElements);
+ } else {
+ console.log('๐๏ธ No new form elements in viewport');
+ }
+
+ } catch (error) {
+ console.error('โ Error analyzing new viewport:', error);
+ }
+}
+
+function findNewVisibleElements(viewport) {
+ const currentlyVisible = document.querySelectorAll('input, textarea, select');
+ const newElements = [];
+
+ currentlyVisible.forEach(element => {
+ const elementKey = generateElementKey(element);
+
+ // Check if element is in viewport and not already detected
+ if (isElementInViewport(element, viewport) && !detectedElements.has(elementKey)) {
+ const elementInfo = extractElementInfo(element, newElements.length + 1);
+
+ // Only include meaningful form elements
+ if (elementInfo.type !== 'hidden' && elementInfo.type !== 'submit' &&
+ (elementInfo.name !== '(no name)' || elementInfo.id !== '(no id)' ||
+ elementInfo.placeholder !== '(no placeholder)')) {
+
+ newElements.push({
+ element: element,
+ info: elementInfo,
+ timestamp: Date.now(),
+ scannedAt: 'viewport-scroll'
+ });
+
+ // Add to detected elements
+ detectedElements.set(elementKey, {
+ element: element,
+ info: elementInfo,
+ timestamp: Date.now(),
+ scannedAt: 'viewport-scroll'
+ });
+ }
+ }
+ });
+
+ return newElements;
+}
+
+async function sendNewViewportAnalysis(viewport, newElements) {
+ try {
+ // Get visible DOM for this viewport
+ const visibleDOM = captureVisibleDOM(viewport);
+
+ console.log('๐ค Sending new viewport analysis...');
+
+ const response = await chrome.runtime.sendMessage({
+ action: 'analyzeViewportDOM',
+ visibleDOM: visibleDOM,
+ viewport: viewport,
+ pageURL: window.location.href,
+ pageTitle: document.title,
+ timestamp: Date.now(),
+ triggerReason: 'scroll-change'
+ });
+
+ if (response?.success && response.results.length > 0) {
+ console.log('โ
New viewport analysis complete:', response.results.length, 'sensitive fields found');
+ handleSensitiveFieldResults(response.results, newElements);
+ }
+
+ } catch (error) {
+ console.error('โ Failed to analyze new viewport:', error);
+ }
+}
+
+async function sendToAIAgent(candidates) {
+ try {
+ console.log('๐ Sending', candidates.length, 'elements to AI agent...');
+
+ // Prepare minimal element data for AI
+ const elementsForAI = candidates.map(item => ({
+ type: item.info.type,
+ name: item.info.name,
+ id: item.info.id,
+ placeholder: item.info.placeholder,
+ autocomplete: item.info.autocomplete,
+ className: item.info.className,
+ originalIndex: item.info.index
+ }));
+
+ const response = await chrome.runtime.sendMessage({
+ action: 'analyzeElements',
+ elements: elementsForAI
+ });
+
+ if (response?.success) {
+ console.log('โ
AI Analysis complete:', {
+ newElementsAnalyzed: response.newElementsCount,
+ totalProcessed: response.totalProcessed,
+ sensitiveFieldsFound: response.results.length
+ });
+
+ if (response.results.length > 0) {
+ handleSensitiveFieldResults(response.results, candidates);
+ }
+ } else {
+ console.error('โ AI Analysis failed:', response?.error);
+ }
+
+ } catch (error) {
+ console.error('โ Failed to send elements to AI:', error);
+ }
+}
+
+function handleSensitiveFieldResults(aiResults, candidates) {
+ console.log('๐ Processing', aiResults.length, 'sensitive field results from DOM analysis...');
+
+ aiResults.forEach(result => {
+ try {
+ // Find element using the CSS selector provided by AI
+ const element = document.querySelector(result.selector);
+
+ if (element) {
+ console.log('๐จ SENSITIVE FIELD DETECTED:', {
+ selector: result.selector,
+ type: result.type,
+ category: result.category,
+ sensitivity: result.sensitivity,
+ reason: result.reason,
+ confidence: result.confidence
+ });
+
+ // Log the sensitive field detection
+ logAISensitiveFieldDetection(element, result);
+ } else {
+ console.warn('โ ๏ธ Could not find element with selector:', result.selector);
+ }
+ } catch (error) {
+ console.error('โ Error processing sensitive field result:', error);
+ }
+ });
+}
+
+function logAISensitiveFieldDetection(element, result) {
+ try {
+ // Just mark with data attributes for tracking (no visual changes)
+ element.setAttribute('data-sensitive-field', 'true');
+ element.setAttribute('data-sensitivity-level', result.sensitivity);
+ element.setAttribute('data-sensitivity-category', result.category);
+ element.setAttribute('data-ai-confidence', result.confidence);
+ element.setAttribute('data-detection-method', 'AI');
+
+ // Log comprehensive details
+ console.log('๐ AI SENSITIVE FIELD DETECTED:', {
+ element: element,
+ selector: generateSelector(element),
+ category: result.category,
+ sensitivity: result.sensitivity,
+ confidence: Math.round(result.confidence * 100) + '%',
+ reason: result.reason,
+ url: window.location.href,
+ timestamp: new Date().toISOString(),
+ fieldDetails: {
+ type: element.type,
+ name: element.name,
+ id: element.id,
+ placeholder: element.placeholder,
+ value: element.value ? '[REDACTED]' : '[EMPTY]'
+ },
+ context: getFieldContext(element)
+ });
+
+ // Structured logging for browser-level processing
+ window.postMessage({
+ type: 'SENSITIVE_FIELD_DETECTED',
+ source: 'extension',
+ data: {
+ selector: generateSelector(element),
+ category: result.category,
+ sensitivity: result.sensitivity,
+ confidence: result.confidence,
+ method: 'AI',
+ url: window.location.href,
+ timestamp: new Date().toISOString()
+ }
+ }, '*');
+
+ } catch (error) {
+ console.error('โ Failed to log AI sensitive field detection:', error);
+ }
+}
+
+function stopDOMMonitoring() {
+ console.log('โน๏ธ Stopping DOM monitoring...');
+
+ clearTimeout(scanTimeout);
+ clearTimeout(userActivityTimeout);
+
+ // Stop observer
+ if (window.domObserver) {
+ window.domObserver.disconnect();
+ window.domObserver = null;
+ }
+
+ // Clear all state
+ detectedElements.clear();
+ lastScanHash = '';
+ isUserActive = false;
+
+ console.log('โ
DOM monitoring stopped and state cleared');
+}
+
+// URL change detection for SPA navigation
+// currentURL is declared at the top of the file
+
+function setupURLChangeDetection() {
+ // Detect URL changes in SPAs
+ const originalPushState = history.pushState;
+ const originalReplaceState = history.replaceState;
+
+ history.pushState = function() {
+ originalPushState.apply(history, arguments);
+ handleURLChange();
+ };
+
+ history.replaceState = function() {
+ originalReplaceState.apply(history, arguments);
+ handleURLChange();
+ };
+
+ window.addEventListener('popstate', handleURLChange);
+
+ console.log('๐ URL change detection enabled for SPAs');
+}
+
+function handleURLChange() {
+ const newURL = window.location.href;
+
+ if (newURL !== currentURL) {
+ console.log('๐ URL CHANGED:', currentURL, '->', newURL);
+ currentURL = newURL;
+
+ // Clear previous sensitive fields list for new page
+ clearSensitiveFieldsList();
+
+ // Trigger simple rescan for new page
+ setTimeout(() => {
+ if (isExtensionEnabled) {
+ console.log('๐ URL changed - restarting scan...');
+ performEarlyScan('url-change');
+ }
+ }, 1000); // Wait for page to stabilize
+ }
+}
+
+console.log('๐ฏ Pre-visibility content script setup complete');
+
+function setupUserActivityTracking() {
+ const userEvents = ['click', 'keydown', 'input', 'focus', 'scroll', 'mousemove'];
+
+ userEvents.forEach(event => {
+ document.addEventListener(event, () => {
+ isUserActive = true;
+
+ // Reset user activity timeout
+ clearTimeout(userActivityTimeout);
+ userActivityTimeout = setTimeout(() => {
+ isUserActive = false;
+ console.log('๐ด User inactive - reducing monitoring');
+ }, CONFIG.USER_ACTIVITY_TIMEOUT);
+ });
+ });
+}
+
+function setupSmartDOMObserver() {
+ if (window.domObserver) {
+ window.domObserver.disconnect();
+ }
+
+ window.domObserver = new MutationObserver((mutations) => {
+ // Filter mutations that might contain form elements
+ const relevantMutations = mutations.filter(mutation => {
+ // Check if mutation affects form elements
+ if (mutation.type === 'childList') {
+ return Array.from(mutation.addedNodes).some(node =>
+ node.nodeType === Node.ELEMENT_NODE &&
+ (node.matches('input, textarea, select, form') ||
+ node.querySelector('input, textarea, select'))
+ );
+ }
+
+ // Check attribute changes on form elements
+ if (mutation.type === 'attributes') {
+ return mutation.target.matches('input, textarea, select') ||
+ ['type', 'name', 'id', 'placeholder'].includes(mutation.attributeName);
+ }
+
+ return false;
+ });
+
+ if (relevantMutations.length > 0) {
+ console.log('๐ Relevant DOM changes detected:', relevantMutations.length, 'mutations');
+ scheduleIntelligentScan('dom-change');
+ }
+ });
+
+ window.domObserver.observe(document.body || document.documentElement, {
+ childList: true,
+ subtree: true,
+ attributes: true,
+ attributeFilter: ['type', 'name', 'id', 'placeholder', 'class', 'data-*']
+ });
+}
+
+function scheduleIntelligentScan(trigger) {
+ // Clear existing timeout
+ clearTimeout(scanTimeout);
+
+ // Only scan if user is active or it's initial scan
+ if (!isUserActive && trigger !== 'initial') {
+ console.log('โธ๏ธ Skipping scan - user inactive');
+ return;
+ }
+
+ // Debounce rapid DOM changes
+ scanTimeout = setTimeout(() => {
+ performScan(trigger);
+ }, CONFIG.SCAN_DEBOUNCE_DELAY);
+}
+
+function performScan(trigger) {
+ if (!isExtensionEnabled) return;
+
+ console.log(`๐ Performing intelligent scan (${trigger})...`);
+
+ // Find all input elements
+ const inputs = document.querySelectorAll('input, textarea, select');
+
+ // Create hash of current elements to detect changes
+ const currentHash = generateElementsHash(inputs);
+
+ // Skip scan if nothing changed
+ if (currentHash === lastScanHash && trigger !== 'initial') {
+ console.log('โญ๏ธ Skipping scan - no changes detected');
+ return;
+ }
+
+ lastScanHash = currentHash;
+ console.log(`๐ Found ${inputs.length} input elements`);
+
+ // Process only new/changed elements
+ let newElementsCount = 0;
+ const previousSize = detectedElements.size;
+
+ inputs.forEach((element, index) => {
+ const elementKey = generateElementKey(element);
+
+ // Skip if element already processed and hasn't changed
+ if (detectedElements.has(elementKey)) {
+ return;
+ }
+
+ newElementsCount++;
+
+ const elementInfo = extractElementInfo(element, index + 1);
+ console.log(`๐ NEW Element ${index + 1}:`, elementInfo);
+
+ // Store element info with unique detection
+ detectedElements.set(elementKey, {
+ element: element,
+ info: elementInfo,
+ timestamp: Date.now(),
+ scannedAt: trigger
+ });
+ });
+
+ // Only log summary if significant changes
+ if (newElementsCount > 0 || trigger === 'initial') {
+ console.log(`โ
Scan complete. New elements: ${newElementsCount}, Total tracked: ${detectedElements.size}`);
+
+ // COMMENTED OUT: AI analysis trigger
+ // if (newElementsCount >= CONFIG.SIGNIFICANT_CHANGE_THRESHOLD) {
+ // console.log('๐ค Significant changes detected - ready for AI analysis');
+ // prepareForAIDetection(Array.from(detectedElements.values()).slice(-newElementsCount));
+ // }
+ }
+}
+
+function generateElementsHash(elements) {
+ // Create simple hash of element count and types
+ const summary = Array.from(elements).map(el =>
+ `${el.tagName}_${el.type}_${el.name}_${el.id}`
+ ).join('|');
+
+ return btoa(summary).slice(0, 10); // Simple hash
+}
+
+function extractElementInfo(element, index) {
+ return {
+ index: index,
+ tag: element.tagName.toLowerCase(),
+ type: element.type || 'text',
+ id: element.id || '(no id)',
+ name: element.name || '(no name)',
+ placeholder: element.placeholder || '(no placeholder)',
+ className: element.className || '(no class)',
+ visible: isElementVisible(element),
+ value: element.value ? '[HAS_VALUE]' : '[EMPTY]', // Don't log actual values
+ autocomplete: element.autocomplete || '(none)',
+ required: element.required || false
+ };
+}
+
+function generateElementKey(element) {
+ // Create unique key for element including position
+ const rect = element.getBoundingClientRect();
+ return `${element.tagName}_${element.type}_${element.id}_${element.name}_${Math.round(rect.top)}_${Math.round(rect.left)}`;
+}
+
+function isElementVisible(element) {
+ try {
+ const rect = element.getBoundingClientRect();
+ const style = window.getComputedStyle(element);
+
+ return rect.width > 0 &&
+ rect.height > 0 &&
+ style.display !== 'none' &&
+ style.visibility !== 'hidden' &&
+ style.opacity !== '0';
+ } catch (e) {
+ return false;
+ }
+}
+
+function prepareForAIDetection(newElements) {
+ // This will be used in Step 2 for AI detection
+ console.log('๐ฏ Preparing elements for AI detection:', newElements.length);
+
+ // Filter visible, named elements for AI analysis
+ const aiCandidates = newElements.filter(item =>
+ item.info.visible &&
+ (item.info.name !== '(no name)' ||
+ item.info.id !== '(no id)' ||
+ item.info.placeholder !== '(no placeholder)')
+ );
+
+ if (aiCandidates.length > 0) {
+ console.log('๐ค AI candidates ready:', aiCandidates.map(item => ({
+ type: item.info.type,
+ name: item.info.name,
+ placeholder: item.info.placeholder
+ })));
+ }
+}
+
+function stopDOMMonitoring() {
+ console.log('โน๏ธ Stopping DOM monitoring...');
+
+ // Clear timeouts
+ clearTimeout(scanTimeout);
+ clearTimeout(userActivityTimeout);
+
+ // Disconnect observer
+ if (window.domObserver) {
+ window.domObserver.disconnect();
+ window.domObserver = null;
+ }
+
+ // Clear state
+ detectedElements.clear();
+ lastScanHash = '';
+ isUserActive = false;
+
+ console.log('โ
DOM monitoring stopped');
+}
+
+// Content script is fully loaded and monitoring is already started
+console.log('๐ฏ Pre-visibility content script setup complete');
+
+console.log('๐ฏ Smart content script setup complete');
+
+function logEnhancedSensitiveFieldDetails(element, info, item) {
+ // Enhanced logging for dynamic DOM detection
+ console.log('๐ ENHANCED SENSITIVE FIELD LOGGED:', {
+ element: element,
+ details: info,
+ selector: generateSelector(element),
+ context: getFieldContext(element),
+ dynamicInfo: {
+ detectedAt: item.detectedAt,
+ reanalysis: item.reanalysis || false,
+ incremental: item.incremental || false,
+ changes: item.changes || null,
+ shadowDOM: info.shadowDOM,
+ shadowHost: info.shadowHost,
+ visibilityState: {
+ visible: info.visible,
+ hidden: info.hidden,
+ disabled: info.disabled,
+ ariaHidden: info.ariaHidden
+ }
+ },
+ timestamp: new Date().toISOString(),
+ url: window.location.href
+ });
+
+ // Post enhanced message for browser-level processing
+ window.postMessage({
+ type: 'ENHANCED_SENSITIVE_FIELD_DETECTED',
+ source: 'extension-dynamic',
+ data: {
+ selector: generateSelector(element),
+ fieldInfo: info,
+ detectionContext: {
+ phase: item.detectedAt,
+ reanalysis: item.reanalysis || false,
+ shadowDOM: info.shadowDOM,
+ dynamicChange: true
+ },
+ timestamp: new Date().toISOString(),
+ url: window.location.href
+ }
+ }, '*');
+}
+
+// NEW: Function to show retry status indicator
+function showRetryStatus(message) {
+ // Create or update status indicator
+ let statusIndicator = document.getElementById('sensitive-field-detector-status');
+ if (!statusIndicator) {
+ statusIndicator = document.createElement('div');
+ statusIndicator.id = 'sensitive-field-detector-status';
+ statusIndicator.style.cssText = `
+ position: fixed;
+ top: 10px;
+ right: 10px;
+ background: #2196F3;
+ color: white;
+ padding: 8px 12px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-family: Arial, sans-serif;
+ z-index: 999999;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
+ max-width: 200px;
+ word-wrap: break-word;
+ `;
+ document.body.appendChild(statusIndicator);
+ }
+ statusIndicator.textContent = message;
+
+ // Auto-hide after 5 seconds
+ setTimeout(() => {
+ if (statusIndicator && statusIndicator.parentNode) {
+ statusIndicator.parentNode.removeChild(statusIndicator);
+ }
+ }, 5000);
+}
+
+// NEW: Function to send raw DOM to Gemini AI for analysis
+async function sendRawDOMToGemini() {
+ console.log('๐ NEW: Capturing and sending raw DOM to Gemini AI...');
+
+ try {
+ // Show status indicator
+ showRetryStatus('๐ Analyzing page with AI...');
+
+ // Capture the complete DOM with enhanced strategies for modern web apps
+ let rawDOM;
+
+ // Try to get the most complete DOM possible
+ if (document.readyState === 'loading') {
+ // If still loading, wait a bit for more content
+ console.log('โณ DOM still loading, waiting for more content...');
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+
+ // ENHANCED: Capture DOM with shadow DOM and dynamic content
+ rawDOM = captureEnhancedDOM();
+
+ // Debug: Check if we have form elements in the captured DOM
+ const tempDiv = document.createElement('div');
+ tempDiv.innerHTML = rawDOM;
+ const formElementsInDOM = tempDiv.querySelectorAll('input, textarea, select');
+ console.log('๐ DEBUG: Form elements found in captured DOM:', formElementsInDOM.length);
+ formElementsInDOM.forEach((el, i) => {
+ console.log(` ${i + 1}. ${el.tagName} type="${el.type}" name="${el.name}" id="${el.id}"`);
+ });
+
+ // ENHANCED: Also check for shadow DOM elements
+ const shadowElements = findShadowDOMElements();
+ console.log('๐ DEBUG: Shadow DOM elements found:', shadowElements.length);
+ shadowElements.forEach((el, i) => {
+ console.log(` Shadow ${i + 1}. ${el.tagName} type="${el.type}" name="${el.name}" id="${el.id}"`);
+ });
+
+ const pageInfo = {
+ url: window.location.href,
+ title: document.title,
+ pageType: detectPageType(),
+ timestamp: Date.now(),
+ userAgent: navigator.userAgent,
+ viewport: {
+ width: window.innerWidth,
+ height: window.innerHeight
+ },
+ domState: {
+ readyState: document.readyState,
+ bodyChildren: document.body?.children?.length || 0,
+ formElements: formElementsInDOM.length,
+ shadowElements: shadowElements.length,
+ totalElements: document.querySelectorAll('*').length
+ }
+ };
+
+ console.log('๐ Captured raw DOM:', {
+ size: Math.round(rawDOM.length / 1024) + 'KB',
+ url: pageInfo.url,
+ title: pageInfo.title,
+ pageType: pageInfo.pageType,
+ domState: pageInfo.domState
+ });
+
+ // Debug: Show a snippet of the DOM to verify content
+ const domSnippet = rawDOM.substring(0, 1000);
+ console.log('๐ DEBUG: DOM snippet (first 1000 chars):', domSnippet);
+
+ // Send to background script for AI analysis
+ const response = await new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({
+ action: 'analyzeRawDOM',
+ rawDOM: rawDOM,
+ pageInfo: pageInfo
+ }, (response) => {
+ if (chrome.runtime.lastError) {
+ console.error('โ Chrome runtime error:', chrome.runtime.lastError);
+ reject(chrome.runtime.lastError);
+ return;
+ }
+ resolve(response);
+ });
+ });
+
+ if (response?.success) {
+ console.log('โ
Raw DOM analysis completed successfully');
+ console.log('๐ฏ Detected sensitive fields:', response.sensitiveFields.length);
+ console.log('โฑ๏ธ Processing time:', response.processingTime + 'ms');
+
+ if (response.fallbackUsed) {
+ console.log('๐ Fallback pattern detection was used');
+ showRetryStatus('โ
Analysis complete (fallback mode)');
+ } else if (response.attempts > 1) {
+ console.log(`๐ Completed after ${response.attempts} attempts`);
+ showRetryStatus(`โ
Analysis complete (${response.attempts} attempts)`);
+ } else {
+ showRetryStatus('โ
Analysis complete');
+ }
+
+ // Process the detected fields
+ await processRawDOMResults(response.sensitiveFields, pageInfo);
+
+ return response;
+ } else {
+ console.error('โ Raw DOM analysis failed:', response?.error);
+
+ // Enhanced error reporting
+ if (response?.attempts) {
+ console.log(`๐ Failed after ${response.attempts} attempts`);
+ showRetryStatus(`โ Failed after ${response.attempts} attempts`);
+ }
+
+ if (response?.fallbackUsed) {
+ console.log('๐ Fallback pattern detection was attempted');
+ showRetryStatus('โ Analysis failed (fallback used)');
+ }
+
+ throw new Error(response?.error || 'Unknown error');
+ }
+
+ } catch (error) {
+ console.error('โ Failed to send raw DOM to Gemini:', error);
+
+ // Enhanced error context
+ if (error.message.includes('503')) {
+ console.log('๐ก Tip: Gemini API is overloaded. The extension will retry automatically.');
+ } else if (error.message.includes('429')) {
+ console.log('๐ก Tip: Rate limit exceeded. The extension will retry with backoff.');
+ } else if (error.message.includes('timeout')) {
+ console.log('๐ก Tip: Request timed out. The extension will retry automatically.');
+ }
+
+ throw error;
+ }
+}
+
+// NEW: Enhanced DOM capture function for modern web apps
+function captureEnhancedDOM() {
+ console.log('๐ ENHANCED: Capturing DOM with shadow DOM and dynamic content...');
+
+ // Start with the main document
+ let enhancedDOM = document.documentElement.outerHTML;
+
+ // ENHANCED: Wait for dynamic content to load
+ if (document.readyState !== 'complete') {
+ console.log('โณ Waiting for dynamic content to load...');
+ // This will be handled by the async nature of the function
+ }
+
+ // ENHANCED: Capture shadow DOM content
+ const shadowContent = captureShadowDOMContent();
+ if (shadowContent) {
+ enhancedDOM += '\n\n' + shadowContent;
+ }
+
+ // ENHANCED: Capture dynamically loaded content
+ const dynamicContent = captureDynamicContent();
+ if (dynamicContent) {
+ enhancedDOM += '\n\n' + dynamicContent;
+ }
+
+ // ENHANCED: Capture form-like elements that might be missed
+ const formLikeElements = captureFormLikeElements();
+ if (formLikeElements) {
+ enhancedDOM += '\n\n' + formLikeElements;
+ }
+
+ console.log('โ
Enhanced DOM capture completed');
+ return enhancedDOM;
+}
+
+// NEW: Function to capture shadow DOM content
+function captureShadowDOMContent() {
+ console.log('๐ ENHANCED: Searching for shadow DOM elements...');
+
+ let shadowContent = '';
+ const shadowElements = [];
+
+ // Find all elements that might have shadow roots
+ const allElements = document.querySelectorAll('*');
+
+ allElements.forEach((element, index) => {
+ try {
+ // Check if element has shadow root
+ if (element.shadowRoot) {
+ console.log(`๐ Found shadow root in element: ${element.tagName} (${index})`);
+
+ // Capture shadow DOM content
+ const shadowHTML = element.shadowRoot.innerHTML;
+ shadowContent += `\n\n
${shadowHTML}
`;
+
+ // Find form elements in shadow DOM
+ const shadowFormElements = element.shadowRoot.querySelectorAll('input, textarea, select');
+ shadowFormElements.forEach((formEl, formIndex) => {
+ shadowElements.push({
+ element: formEl,
+ parent: element.tagName,
+ shadowIndex: index,
+ formIndex: formIndex
+ });
+ });
+ }
+ } catch (error) {
+ // Some shadow roots might not be accessible
+ console.log(`โ ๏ธ Could not access shadow root in element: ${element.tagName}`);
+ }
+ });
+
+ console.log(`๐ Found ${shadowElements.length} form elements in shadow DOM`);
+ shadowElements.forEach((item, i) => {
+ console.log(` Shadow ${i + 1}. ${item.element.tagName} type="${item.element.type}" name="${item.element.name}" id="${item.element.id}" (in ${item.parent})`);
+ });
+
+ return shadowContent;
+}
+
+// NEW: Function to capture dynamically loaded content
+function captureDynamicContent() {
+ console.log('๐ ENHANCED: Capturing dynamically loaded content...');
+
+ let dynamicContent = '';
+
+ // Look for common patterns in modern web apps
+ const dynamicSelectors = [
+ '[data-testid]', // React testing attributes
+ '[data-cy]', // Cypress testing attributes
+ '[role]', // ARIA roles
+ '[aria-label]', // ARIA labels
+ '.form-control', // Bootstrap forms
+ '.input-group', // Bootstrap input groups
+ '.field', // Generic field classes
+ '.form-field', // Form field classes
+ '[class*="form"]', // Any class containing "form"
+ '[class*="input"]', // Any class containing "input"
+ '[class*="field"]' // Any class containing "field"
+ ];
+
+ dynamicSelectors.forEach(selector => {
+ const elements = document.querySelectorAll(selector);
+ if (elements.length > 0) {
+ console.log(`๐ Found ${elements.length} elements with selector: ${selector}`);
+ elements.forEach((el, index) => {
+ if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') {
+ dynamicContent += `\n\n${el.outerHTML}`;
+ }
+ });
+ }
+ });
+
+ return dynamicContent;
+}
+
+// NEW: Function to capture form-like elements
+function captureFormLikeElements() {
+ console.log('๐ ENHANCED: Capturing form-like elements...');
+
+ let formLikeContent = '';
+
+ // Look for elements that might contain sensitive data but aren't traditional forms
+ const sensitivePatterns = [
+ // Common sensitive field patterns
+ 'input[type="text"]',
+ 'input[type="email"]',
+ 'input[type="tel"]',
+ 'input[type="password"]',
+ 'input[type="number"]',
+ 'input[type="date"]',
+ 'input[type="month"]',
+ 'input[type="week"]',
+ 'input[type="datetime-local"]',
+ 'textarea',
+ 'select',
+ // Contenteditable elements
+ '[contenteditable="true"]',
+ // Custom input elements
+ '[role="textbox"]',
+ '[role="combobox"]',
+ '[role="listbox"]',
+ // Common sensitive field names
+ 'input[name*="email"]',
+ 'input[name*="phone"]',
+ 'input[name*="password"]',
+ 'input[name*="username"]',
+ 'input[name*="name"]',
+ 'input[name*="address"]',
+ 'input[name*="birth"]',
+ 'input[name*="ssn"]',
+ 'input[name*="credit"]',
+ 'input[name*="card"]',
+ 'input[name*="account"]',
+ 'input[id*="email"]',
+ 'input[id*="phone"]',
+ 'input[id*="password"]',
+ 'input[id*="username"]',
+ 'input[id*="name"]',
+ 'input[id*="address"]',
+ 'input[id*="birth"]',
+ 'input[id*="ssn"]',
+ 'input[id*="credit"]',
+ 'input[id*="card"]',
+ 'input[id*="account"]'
+ ];
+
+ sensitivePatterns.forEach(pattern => {
+ try {
+ const elements = document.querySelectorAll(pattern);
+ if (elements.length > 0) {
+ console.log(`๐ Found ${elements.length} elements with pattern: ${pattern}`);
+ elements.forEach((el, index) => {
+ formLikeContent += `\n\n${el.outerHTML}`;
+ });
+ }
+ } catch (error) {
+ // Some selectors might be invalid
+ console.log(`โ ๏ธ Invalid selector: ${pattern}`);
+ }
+ });
+
+ return formLikeContent;
+}
+
+// NEW: Function to find shadow DOM elements
+function findShadowDOMElements() {
+ const shadowElements = [];
+
+ const allElements = document.querySelectorAll('*');
+
+ allElements.forEach(element => {
+ try {
+ if (element.shadowRoot) {
+ const formElements = element.shadowRoot.querySelectorAll('input, textarea, select');
+ formElements.forEach(formEl => {
+ shadowElements.push(formEl);
+ });
+ }
+ } catch (error) {
+ // Shadow root not accessible
+ }
+ });
+
+ return shadowElements;
+}
+
+async function processRawDOMResults(sensitiveFields, pageInfo) {
+ console.log('๐ Processing raw DOM analysis results...');
+
+ if (!sensitiveFields || sensitiveFields.length === 0) {
+ console.log('โ
No sensitive fields detected in raw DOM analysis');
+ return;
+ }
+
+ // Process each detected field
+ sensitiveFields.forEach((field, index) => {
+ console.log(`\n๐ Processing field ${index + 1}:`);
+ console.log(` Type: ${field.type}`);
+ console.log(` Selector: ${field.selector}`);
+ console.log(` Confidence: ${Math.round(field.confidence * 100)}%`);
+ console.log(` Risk Level: ${field.risk_level}`);
+
+ // Try to find the actual element in the DOM
+ try {
+ const element = document.querySelector(field.selector);
+ if (element) {
+ console.log(` โ
Element found in DOM`);
+
+ // Mark the element for tracking
+ element.setAttribute('data-sensitive-field', 'true');
+ element.setAttribute('data-ai-detection', 'true');
+ element.setAttribute('data-field-type', field.type);
+ element.setAttribute('data-confidence', field.confidence);
+ element.setAttribute('data-risk-level', field.risk_level);
+
+ // Log detailed information
+ logRawDOMFieldDetails(element, field, pageInfo);
+
+ // Add to AI detected fields list
+ addToAIDetectedFieldsList(element, {
+ type: field.type,
+ name: element.name || '',
+ id: element.id || '',
+ placeholder: element.placeholder || '',
+ autocomplete: element.autocomplete || ''
+ }, {
+ detectionMethod: 'raw-dom-ai',
+ confidence: field.confidence,
+ category: field.type,
+ sensitivity: field.risk_level,
+ reasoning: field.reasoning,
+ context: field.context,
+ recommendation: field.recommendation
+ });
+
+ } else {
+ console.log(` โ ๏ธ Element not found in current DOM (may be dynamic)`);
+ }
+ } catch (error) {
+ console.error(` โ Error processing field:`, error);
+ }
+ });
+
+ // Send AI detected fields to browser API
+ sendAIDetectedFieldsToBrowser();
+
+ console.log('โ
Raw DOM results processing completed');
+}
+
+function logRawDOMFieldDetails(element, field, pageInfo) {
+ console.log('๐ RAW DOM FIELD DETAILS:');
+ console.log('=' .repeat(50));
+ console.log(`๐ Page: ${pageInfo.url}`);
+ console.log(`๐ Title: ${pageInfo.title}`);
+ console.log(`๐ Field Type: ${field.type.toUpperCase()}`);
+ console.log(`๐ฏ Selector: ${field.selector}`);
+ console.log(`๐ Confidence: ${Math.round(field.confidence * 100)}%`);
+ console.log(`โ ๏ธ Risk Level: ${field.risk_level.toUpperCase()}`);
+ console.log('');
+ console.log(`๐ Element Details:`);
+ console.log(` Tag: ${element.tagName}`);
+ console.log(` Type: ${element.type || 'N/A'}`);
+ console.log(` Name: ${element.name || 'N/A'}`);
+ console.log(` ID: ${element.id || 'N/A'}`);
+ console.log(` Placeholder: ${element.placeholder || 'N/A'}`);
+ console.log(` Autocomplete: ${element.autocomplete || 'N/A'}`);
+ console.log(` Required: ${element.required || false}`);
+ console.log(` Disabled: ${element.disabled || false}`);
+ console.log('');
+ console.log(`๐ง AI Reasoning: ${field.reasoning}`);
+ console.log(`๐ Context: ${field.context}`);
+ console.log(`๐ก Recommendation: ${field.recommendation}`);
+ console.log('=' .repeat(50));
+}
+
+// Function to manually trigger raw DOM analysis (for testing)
+function triggerRawDOMAnalysis() {
+ console.log('๐ Manually triggering raw DOM analysis...');
+ sendRawDOMToGemini()
+ .then(result => {
+ console.log('โ
Manual raw DOM analysis completed:', result);
+ })
+ .catch(error => {
+ console.error('โ Manual raw DOM analysis failed:', error);
+ });
+}
+
+// Make the function available globally for testing
+window.triggerRawDOMAnalysis = triggerRawDOMAnalysis;
+
+
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/content.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/content.js
new file mode 100644
index 000000000..d7607c93d
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/content.js
@@ -0,0 +1,746 @@
+// Main content script - Hybrid sensitive field detection
+// Combines pattern-based detection with AI classification
+
+console.log('๐ Content script loading...', window.location.href);
+console.log('๐ IMMEDIATE CONTENT SCRIPT TEST - IF YOU SEE THIS, CONTENT SCRIPTS ARE WORKING!');
+
+class SensitiveFieldDetector {
+ constructor() {
+ console.log('๐๏ธ SensitiveFieldDetector constructor called');
+ this.processedElements = new WeakSet();
+ this.detectionQueue = [];
+ this.isProcessing = false;
+ this.currentDomain = window.location.hostname;
+ this.detectionRules = null;
+ this.observers = [];
+
+ this.settings = {
+ aiConfidenceThreshold: 0.7,
+ patternConfidenceThreshold: 0.9,
+ maxQueueSize: 20,
+ batchProcessDelay: 2000,
+ enableAI: true,
+ enablePatterns: true
+ };
+
+ console.log('โ๏ธ Initial settings:', this.settings);
+
+ this.initialize();
+ }
+
+ async initialize() {
+ console.log('๐ Initializing SensitiveFieldDetector...');
+ try {
+ // Wait for all utility scripts to load
+ console.log('โณ Waiting for dependencies...');
+ await this.waitForDependencies();
+ console.log('โ
Dependencies loaded');
+
+ // Load user settings
+ console.log('โ๏ธ Loading settings...');
+ await this.loadSettings();
+ console.log('โ
Settings loaded:', this.settings);
+
+ // Initialize detection rules
+ console.log('๐ Initializing detection rules...');
+ this.initializeDetectionRules();
+ console.log('โ
Detection rules initialized');
+
+ // Start detection process
+ console.log('๐ Starting detection process...');
+ this.startDetection();
+ console.log('โ
Detection process started');
+
+ console.log('๐ Sensitive Field Detector fully initialized');
+ } catch (error) {
+ console.error('โ Failed to initialize detector:', error);
+ }
+ }
+
+ async waitForDependencies() {
+ console.log('โณ Checking dependencies...');
+ const dependencies = [
+ 'SENSITIVE_PATTERNS',
+ 'FIELD_DICTIONARIES',
+ 'cacheManager',
+ 'privacyManager',
+ 'performanceManager'
+ ];
+
+ for (const dep of dependencies) {
+ console.log(`๐ Checking dependency: ${dep}`);
+ let attempts = 0;
+ while (!window[dep] && attempts < 50) {
+ await new Promise(resolve => setTimeout(resolve, 100));
+ attempts++;
+ }
+
+ if (!window[dep]) {
+ console.warn(`โ ๏ธ Dependency ${dep} not loaded after 5 seconds, using fallback`);
+ } else {
+ console.log(`โ
Dependency ${dep} loaded successfully`);
+ }
+ }
+ console.log('๐ฏ Dependency check complete');
+ }
+
+ async loadSettings() {
+ try {
+ const stored = await chrome.storage.sync.get('detectorSettings');
+ if (stored.detectorSettings) {
+ this.settings = { ...this.settings, ...stored.detectorSettings };
+ }
+ } catch (error) {
+ console.warn('Failed to load settings, using defaults');
+ }
+ }
+
+ initializeDetectionRules() {
+ this.detectionRules = {
+ patterns: window.SENSITIVE_PATTERNS || {},
+ dictionaries: window.FIELD_DICTIONARIES || {},
+ regional: window.REGIONAL_PATTERNS || {}
+ };
+ }
+
+ startDetection() {
+ console.log('๐ Starting detection...');
+ // Initial scan
+ console.log('๐ Performing initial scan...');
+ this.performInitialScan();
+
+ // Set up dynamic detection
+ console.log('๐ Setting up dynamic detection...');
+ this.setupDynamicDetection();
+
+ // Start batch processing
+ console.log('โก Starting batch processing...');
+ this.startBatchProcessing();
+ console.log('โ
Detection started successfully');
+ }
+
+ performInitialScan() {
+ console.log('๐ Starting initial scan of page elements...');
+ window.performanceManager?.requestIdleExecution(() => {
+ console.log('โฐ Idle execution started - collecting elements...');
+ const elements = this.collectElements();
+ console.log(`๐ Found ${elements.length} elements to process:`, elements);
+ this.processElementsHybrid(elements);
+ });
+ }
+
+ collectElements() {
+ console.log('๐งญ Collecting form elements...');
+ const selectors = [
+ 'input[type="email"]',
+ 'input[type="password"]',
+ 'input[type="tel"]',
+ 'input[type="text"]',
+ 'input[type="search"]',
+ 'input[type="url"]',
+ 'input:not([type])',
+ 'textarea',
+ 'select',
+ 'div[contenteditable="true"]',
+ 'span[contenteditable="true"]',
+ '*[role="textbox"]',
+ '*[aria-label*="email" i]',
+ '*[aria-label*="password" i]',
+ '*[aria-label*="phone" i]'
+ ];
+
+ console.log('๐ฏ Using selectors:', selectors);
+
+ const elements = window.performanceManager?.efficientQuerySelector
+ ? window.performanceManager.efficientQuerySelector(selectors, 1000)
+ : document.querySelectorAll(selectors.join(', '));
+
+ console.log(`๐ Raw query found ${elements.length} elements`);
+ const elementsArray = Array.from(elements);
+ console.log('๐ Element types found:', elementsArray.map(el => `${el.tagName}[type=${el.type || 'none'}]`));
+
+ return elementsArray;
+ }
+
+ async processElementsHybrid(elements) {
+ console.log(`๐ Processing ${elements.length} elements with hybrid detection...`);
+ if (!elements.length) {
+ console.log('โ No elements to process');
+ return;
+ }
+
+ const startTime = performance.now();
+ const results = {
+ highConfidence: [],
+ mediumConfidence: [],
+ lowConfidence: []
+ };
+
+ console.log('โก Starting element processing loop...');
+ // Process each element
+ for (const element of elements) {
+ if (this.processedElements.has(element)) {
+ console.log('โญ๏ธ Element already processed, skipping');
+ continue;
+ }
+
+ console.log('๐ Processing element:', element);
+ const metadata = this.extractElementMetadata(element);
+ console.log('๐ Element metadata:', metadata);
+
+ const cached = await window.cacheManager?.get(metadata, this.currentDomain);
+
+ if (cached) {
+ console.log('๐พ Found cached result:', cached);
+ this.handleDetectionResult(element, cached, 'cache');
+ window.performanceManager?.incrementMetric('cacheHits');
+ continue;
+ }
+
+ window.performanceManager?.incrementMetric('cacheMisses');
+
+ // Pattern-based detection
+ const patternResult = this.detectWithPatterns(metadata);
+
+ if (patternResult.confidence >= this.settings.patternConfidenceThreshold) {
+ // High confidence pattern match
+ results.highConfidence.push({ element, metadata, result: patternResult });
+ this.handleDetectionResult(element, patternResult, 'pattern');
+
+ // Cache the result
+ await window.cacheManager?.set(metadata, patternResult, this.currentDomain, 'domain');
+ } else if (patternResult.confidence >= this.settings.aiConfidenceThreshold) {
+ // Medium confidence - cache locally but don't send to AI immediately
+ results.mediumConfidence.push({ element, metadata, result: patternResult });
+ this.handleDetectionResult(element, patternResult, 'pattern');
+
+ await window.cacheManager?.set(metadata, patternResult, this.currentDomain, 'session');
+ } else if (this.settings.enableAI) {
+ // Low confidence - queue for AI analysis
+ results.lowConfidence.push({ element, metadata, result: patternResult });
+ this.queueForAI(element, metadata);
+ }
+
+ this.processedElements.add(element);
+ }
+
+ // Log results
+ this.logDetectionResults(results);
+
+ window.performanceManager?.recordMetric('hybridProcessingTime', performance.now() - startTime);
+ }
+
+ extractElementMetadata(element) {
+ const metadata = {
+ tag: element.tagName?.toLowerCase() || '',
+ type: element.type || '',
+ name: element.name || '',
+ id: element.id || '',
+ placeholder: element.placeholder || '',
+ ariaLabel: element.getAttribute('aria-label') || '',
+ autocomplete: element.autocomplete || '',
+ required: element.required || false,
+ maxLength: element.maxLength || null,
+ minLength: element.minLength || null,
+ pattern: element.pattern || '',
+ className: element.className || '',
+ labelText: this.getLabelText(element),
+ formContext: this.getFormContext(element),
+ positionInfo: this.getPositionInfo(element)
+ };
+
+ return metadata;
+ }
+
+ getLabelText(element) {
+ // Try multiple methods to find associated label
+ let labelText = '';
+
+ // Method 1: for attribute
+ if (element.id) {
+ const label = document.querySelector(`label[for="${element.id}"]`);
+ if (label) labelText = label.textContent?.trim() || '';
+ }
+
+ // Method 2: parent label
+ if (!labelText) {
+ const parentLabel = element.closest('label');
+ if (parentLabel) labelText = parentLabel.textContent?.trim() || '';
+ }
+
+ // Method 3: previous sibling
+ if (!labelText) {
+ let sibling = element.previousElementSibling;
+ while (sibling && !labelText) {
+ if (sibling.tagName === 'LABEL' || sibling.textContent?.trim()) {
+ labelText = sibling.textContent?.trim() || '';
+ break;
+ }
+ sibling = sibling.previousElementSibling;
+ }
+ }
+
+ return labelText;
+ }
+
+ getFormContext(element) {
+ const form = element.closest('form');
+ if (!form) return null;
+
+ return {
+ elementCount: form.elements?.length || 0,
+ method: form.method || '',
+ action: form.action || '',
+ id: form.id || '',
+ className: form.className || ''
+ };
+ }
+
+ getPositionInfo(element) {
+ try {
+ const rect = element.getBoundingClientRect();
+ return {
+ visible: rect.width > 0 && rect.height > 0,
+ top: Math.round(rect.top),
+ left: Math.round(rect.left),
+ width: Math.round(rect.width),
+ height: Math.round(rect.height)
+ };
+ } catch (error) {
+ return { visible: false };
+ }
+ }
+
+ detectWithPatterns(metadata) {
+ let confidence = 0;
+ let fieldType = 'unknown';
+ let detectionMethods = [];
+ const reasons = [];
+
+ // Input type-based detection (highest priority)
+ if (metadata.type && this.detectionRules.patterns.INPUT_TYPES?.[metadata.type]) {
+ const typeInfo = this.detectionRules.patterns.INPUT_TYPES[metadata.type];
+ confidence = Math.max(confidence, typeInfo.sensitivity);
+ fieldType = typeInfo.type;
+ detectionMethods.push('input_type');
+ reasons.push(`Input type: ${metadata.type}`);
+ }
+
+ // Autocomplete attribute detection
+ if (metadata.autocomplete && this.detectionRules.patterns.AUTOCOMPLETE_PATTERNS?.[metadata.autocomplete]) {
+ const autoInfo = this.detectionRules.patterns.AUTOCOMPLETE_PATTERNS[metadata.autocomplete];
+ confidence = Math.max(confidence, autoInfo.sensitivity);
+ fieldType = autoInfo.type;
+ detectionMethods.push('autocomplete');
+ reasons.push(`Autocomplete: ${metadata.autocomplete}`);
+ }
+
+ // Name/ID pattern matching
+ const namePatterns = this.detectionRules.patterns.NAME_PATTERNS || {};
+ for (const [pattern, regex] of Object.entries(namePatterns)) {
+ if (this.testAttribute(metadata, 'name', regex) || this.testAttribute(metadata, 'id', regex)) {
+ confidence = Math.max(confidence, 0.85);
+ fieldType = pattern;
+ detectionMethods.push('name_pattern');
+ reasons.push(`Name/ID pattern: ${pattern}`);
+ break;
+ }
+ }
+
+ // Placeholder pattern matching
+ const placeholderPatterns = this.detectionRules.patterns.PLACEHOLDER_PATTERNS || {};
+ for (const [pattern, regex] of Object.entries(placeholderPatterns)) {
+ if (this.testAttribute(metadata, 'placeholder', regex)) {
+ confidence = Math.max(confidence, 0.80);
+ fieldType = pattern;
+ detectionMethods.push('placeholder_pattern');
+ reasons.push(`Placeholder pattern: ${pattern}`);
+ break;
+ }
+ }
+
+ // Label pattern matching
+ const labelPatterns = this.detectionRules.patterns.LABEL_PATTERNS || {};
+ for (const [pattern, regex] of Object.entries(labelPatterns)) {
+ if (this.testAttribute(metadata, 'labelText', regex) || this.testAttribute(metadata, 'ariaLabel', regex)) {
+ confidence = Math.max(confidence, 0.75);
+ fieldType = pattern;
+ detectionMethods.push('label_pattern');
+ reasons.push(`Label pattern: ${pattern}`);
+ break;
+ }
+ }
+
+ // Context-based scoring
+ const contextScore = this.calculateContextScore(metadata);
+ confidence = Math.min(confidence + contextScore, 1.0);
+
+ // Multi-language support
+ const languageScore = this.checkMultiLanguagePatterns(metadata);
+ confidence = Math.min(confidence + languageScore, 1.0);
+
+ return {
+ confidence,
+ fieldType,
+ detectionMethods,
+ reasons,
+ timestamp: Date.now()
+ };
+ }
+
+ testAttribute(metadata, attribute, regex) {
+ const value = metadata[attribute];
+ return value && regex.test(value);
+ }
+
+ calculateContextScore(metadata) {
+ let score = 0;
+ const context = [metadata.className, metadata.labelText, metadata.placeholder].join(' ').toLowerCase();
+
+ const contextKeywords = this.detectionRules.patterns.CONTEXT_KEYWORDS || {};
+
+ if (contextKeywords.high?.some(keyword => context.includes(keyword))) {
+ score += 0.15;
+ }
+ if (contextKeywords.medium?.some(keyword => context.includes(keyword))) {
+ score += 0.10;
+ }
+
+ return score;
+ }
+
+ checkMultiLanguagePatterns(metadata) {
+ let score = 0;
+ const dictionaries = this.detectionRules.dictionaries;
+
+ for (const [lang, patterns] of Object.entries(dictionaries)) {
+ for (const [fieldType, keywords] of Object.entries(patterns)) {
+ const testText = [metadata.labelText, metadata.placeholder, metadata.name].join(' ').toLowerCase();
+
+ if (keywords.some(keyword => testText.includes(keyword.toLowerCase()))) {
+ score += 0.10;
+ break;
+ }
+ }
+ }
+
+ return Math.min(score, 0.20); // Cap at 0.20
+ }
+
+ queueForAI(element, metadata) {
+ if (!this.settings.enableAI) return;
+
+ this.detectionQueue.push({ element, metadata, timestamp: Date.now() });
+
+ // Limit queue size
+ if (this.detectionQueue.length > this.settings.maxQueueSize) {
+ this.detectionQueue.shift(); // Remove oldest
+ }
+ }
+
+ startBatchProcessing() {
+ setInterval(() => {
+ if (this.detectionQueue.length > 0 && !this.isProcessing) {
+ this.processBatchAI();
+ }
+ }, this.settings.batchProcessDelay);
+ }
+
+ async processBatchAI() {
+ if (this.isProcessing || !this.settings.enableAI) return;
+
+ this.isProcessing = true;
+ const batch = this.detectionQueue.splice(0, Math.min(10, this.detectionQueue.length));
+
+ try {
+ const anonymizedBatch = window.privacyManager?.createSafeBatch(
+ batch.map(item => item.metadata)
+ );
+
+ if (!anonymizedBatch) {
+ console.warn('Privacy manager not available, skipping AI analysis');
+ return;
+ }
+
+ // Check for cached AI responses
+ const promptKey = this.generateBatchPromptKey(anonymizedBatch);
+ const cachedResponse = window.cacheManager?.getCachedAIResponse(promptKey);
+
+ if (cachedResponse) {
+ this.handleAIResponse(batch, cachedResponse);
+ window.performanceManager?.incrementMetric('cacheHits');
+ return;
+ }
+
+ // Send to background script for AI analysis
+ const response = await chrome.runtime.sendMessage({
+ action: 'analyzeWithAI',
+ data: anonymizedBatch
+ });
+
+ if (response?.success) {
+ // Cache the AI response
+ await window.cacheManager?.cacheAIResponse(promptKey, response.result);
+ this.handleAIResponse(batch, response.result);
+ window.performanceManager?.incrementMetric('aiRequests');
+ }
+
+ } catch (error) {
+ console.error('AI batch processing failed:', error);
+ } finally {
+ this.isProcessing = false;
+ }
+ }
+
+ generateBatchPromptKey(anonymizedBatch) {
+ const essentialData = anonymizedBatch.elements.map(el => ({
+ tag: el.tag,
+ type: el.type,
+ patterns: el.patterns
+ }));
+
+ return JSON.stringify(essentialData);
+ }
+
+ handleAIResponse(batch, aiResult) {
+ if (!aiResult || !Array.isArray(aiResult.sensitiveIndexes)) return;
+
+ aiResult.sensitiveIndexes.forEach(index => {
+ if (batch[index]) {
+ const { element, metadata } = batch[index];
+ const result = {
+ confidence: aiResult.confidence || 0.85,
+ fieldType: aiResult.fieldTypes?.[index] || 'sensitive',
+ detectionMethods: ['ai'],
+ reasons: ['AI classification'],
+ timestamp: Date.now()
+ };
+
+ this.handleDetectionResult(element, result, 'ai');
+
+ // Cache AI results at global level
+ window.cacheManager?.set(metadata, result, this.currentDomain, 'global');
+ }
+ });
+ }
+
+ handleDetectionResult(element, result, source) {
+ // Create unique identifier for element
+ const elementId = this.generateElementId(element);
+
+ // Prepare result for logging and browser integration
+ const detectionResult = {
+ elementId,
+ element: {
+ tag: element.tagName?.toLowerCase(),
+ type: element.type,
+ name: element.name,
+ id: element.id
+ },
+ sensitivity: {
+ score: result.confidence,
+ type: result.fieldType,
+ methods: result.detectionMethods,
+ reasons: result.reasons
+ },
+ source,
+ timestamp: result.timestamp,
+ domain: this.currentDomain
+ };
+
+ // Log to console
+ this.logSensitiveField(detectionResult);
+
+ // Send to browser's masking system (future integration)
+ this.sendToBrowserAPI(detectionResult);
+
+ // Add visual indicator for testing (optional)
+ if (this.settings.enableVisualIndicators) {
+ this.addVisualIndicator(element, result);
+ }
+ }
+
+ generateElementId(element) {
+ // Create a unique identifier for the element
+ const parts = [
+ element.tagName?.toLowerCase() || '',
+ element.type || '',
+ element.name || '',
+ element.id || '',
+ element.className || ''
+ ].filter(Boolean);
+
+ return window.performanceManager?.generateTaskId() ||
+ Date.now().toString(36) + Math.random().toString(36).substr(2);
+ }
+
+ logSensitiveField(result) {
+ console.group(`๐ Sensitive Field Detected (${result.source})`);
+ console.log('Element:', result.element);
+ console.log('Sensitivity:', result.sensitivity);
+ console.log('Confidence:', `${Math.round(result.sensitivity.score * 100)}%`);
+ console.log('Detection Methods:', result.sensitivity.methods);
+ console.log('Reasons:', result.sensitivity.reasons);
+ console.groupEnd();
+ }
+
+ logDetectionResults(results) {
+ const total = results.highConfidence.length + results.mediumConfidence.length + results.lowConfidence.length;
+
+ if (total === 0) return;
+
+ console.group(`๐ Detection Summary - ${total} sensitive fields found`);
+ console.log(`High Confidence (Pattern): ${results.highConfidence.length}`);
+ console.log(`Medium Confidence (Pattern): ${results.mediumConfidence.length}`);
+ console.log(`Queued for AI Analysis: ${results.lowConfidence.length}`);
+
+ // Create table for detailed view
+ const tableData = [
+ ...results.highConfidence.map(item => ({
+ Element: `${item.element.tagName}[${item.element.type || 'text'}]`,
+ Name: item.element.name || item.element.id || '(none)',
+ Type: item.result.fieldType,
+ Confidence: `${Math.round(item.result.confidence * 100)}%`,
+ Method: item.result.detectionMethods.join(', ')
+ })),
+ ...results.mediumConfidence.map(item => ({
+ Element: `${item.element.tagName}[${item.element.type || 'text'}]`,
+ Name: item.element.name || item.element.id || '(none)',
+ Type: item.result.fieldType,
+ Confidence: `${Math.round(item.result.confidence * 100)}%`,
+ Method: item.result.detectionMethods.join(', ')
+ }))
+ ];
+
+ if (tableData.length > 0) {
+ console.table(tableData);
+ }
+
+ console.groupEnd();
+ }
+
+ sendToBrowserAPI(result) {
+ // Future integration with custom Chromium browser
+ // For now, store in a global array that the browser can access
+ if (!window.detectedSensitiveFields) {
+ window.detectedSensitiveFields = [];
+ }
+
+ window.detectedSensitiveFields.push(result);
+
+ // Dispatch custom event for browser integration
+ window.dispatchEvent(new CustomEvent('sensitiveFieldDetected', {
+ detail: result
+ }));
+ }
+
+ addVisualIndicator(element, result) {
+ // Optional visual indicator for testing
+ try {
+ element.style.outline = `2px solid ${this.getConfidenceColor(result.confidence)}`;
+ element.title = `Sensitive Field (${Math.round(result.confidence * 100)}% confidence)`;
+ } catch (error) {
+ // Ignore styling errors
+ }
+ }
+
+ getConfidenceColor(confidence) {
+ if (confidence >= 0.9) return '#ff4444'; // High confidence - red
+ if (confidence >= 0.7) return '#ffaa00'; // Medium confidence - orange
+ return '#ffdd00'; // Low confidence - yellow
+ }
+
+ setupDynamicDetection() {
+ // Mutation Observer for dynamic content
+ const { observer } = window.performanceManager?.createOptimizedObserver(
+ (mutations) => this.handleDynamicChanges(mutations)
+ ) || { observer: null };
+
+ if (observer) {
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true
+ });
+ this.observers.push(observer);
+ }
+
+ // Scroll detection for lazy-loaded content
+ window.performanceManager?.throttle('scroll-detection', () => {
+ const newElements = this.collectElements().filter(el => !this.processedElements.has(el));
+ if (newElements.length > 0) {
+ this.processElementsHybrid(newElements);
+ }
+ }, 3000);
+
+ window.addEventListener('scroll', () => {
+ window.performanceManager?.throttle('scroll-detection', () => {
+ const newElements = this.collectElements().filter(el => !this.processedElements.has(el));
+ if (newElements.length > 0) {
+ this.processElementsHybrid(newElements);
+ }
+ }, 3000);
+ });
+ }
+
+ handleDynamicChanges(mutations) {
+ const newElements = [];
+
+ mutations.forEach(mutation => {
+ mutation.addedNodes.forEach(node => {
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ // Check if the node itself is a form element
+ if (this.isFormElement(node)) {
+ newElements.push(node);
+ }
+
+ // Check for form elements within the node
+ const formElements = node.querySelectorAll?.('input, textarea, select, [contenteditable="true"]');
+ if (formElements) {
+ newElements.push(...Array.from(formElements));
+ }
+ }
+ });
+ });
+
+ if (newElements.length > 0) {
+ window.performanceManager?.debounce('dynamic-processing', () => {
+ this.processElementsHybrid(newElements);
+ }, 500);
+ }
+ }
+
+ isFormElement(element) {
+ const formTags = ['INPUT', 'TEXTAREA', 'SELECT'];
+ return formTags.includes(element.tagName) ||
+ element.contentEditable === 'true' ||
+ element.getAttribute('role') === 'textbox';
+ }
+
+ // Cleanup method
+ destroy() {
+ this.observers.forEach(observer => observer.disconnect());
+ this.observers = [];
+ this.detectionQueue = [];
+ this.processedElements = new WeakSet();
+ }
+}
+
+// Initialize detector when DOM is ready
+console.log('๐ Content script: DOM ready check...', document.readyState);
+if (document.readyState === 'loading') {
+ console.log('๐
DOM still loading, adding DOMContentLoaded listener...');
+ document.addEventListener('DOMContentLoaded', () => {
+ console.log('๐ DOMContentLoaded fired, creating detector...');
+ window.sensitiveFieldDetector = new SensitiveFieldDetector();
+ });
+} else {
+ console.log('โ
DOM already ready, creating detector immediately...');
+ window.sensitiveFieldDetector = new SensitiveFieldDetector();
+}
+
+// Handle browser extension lifecycle
+window.addEventListener('beforeunload', () => {
+ if (window.sensitiveFieldDetector) {
+ window.sensitiveFieldDetector.destroy();
+ }
+});
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/icons/icon128.svg b/sensitive_fields_ai_agent/sensitive-field-detector_1/icons/icon128.svg
new file mode 100644
index 000000000..e69de29bb
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/icons/icon16.svg b/sensitive_fields_ai_agent/sensitive-field-detector_1/icons/icon16.svg
new file mode 100644
index 000000000..e69de29bb
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/icons/icon48.svg b/sensitive_fields_ai_agent/sensitive-field-detector_1/icons/icon48.svg
new file mode 100644
index 000000000..e69de29bb
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/manifest-simple.json b/sensitive_fields_ai_agent/sensitive-field-detector_1/manifest-simple.json
new file mode 100644
index 000000000..b109269a2
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/manifest-simple.json
@@ -0,0 +1,31 @@
+{
+ "manifest_version": 3,
+ "name": "Simple Sensitive Field Detector",
+ "version": "1.0.0",
+ "description": "Simple step-by-step sensitive field detection",
+
+ "permissions": [
+ "scripting",
+ "activeTab",
+ "storage"
+ ],
+
+ "host_permissions": [
+ ""
+ ],
+
+ "background": {
+ "service_worker": "background-simple.js"
+ },
+
+ "content_scripts": [{
+ "matches": [""],
+ "js": ["content-step1.js"],
+ "run_at": "document_start",
+ "all_frames": true
+ }],
+
+ "action": {
+ "default_popup": "popup-simple.html"
+ }
+}
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/manifest.json b/sensitive_fields_ai_agent/sensitive-field-detector_1/manifest.json
new file mode 100644
index 000000000..143c99311
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/manifest.json
@@ -0,0 +1,32 @@
+{
+ "manifest_version": 3,
+ "name": "Simple Sensitive Field Detector",
+ "version": "1.0.0",
+ "description": "Simple step-by-step sensitive field detection",
+
+ "permissions": [
+ "scripting",
+ "activeTab",
+ "storage"
+ ],
+
+ "host_permissions": [
+ "",
+ "https://generativelanguage.googleapis.com/*"
+ ],
+
+ "background": {
+ "service_worker": "background-simple.js"
+ },
+
+ "content_scripts": [{
+ "matches": [""],
+ "js": ["content-step1.js"],
+ "run_at": "document_start",
+ "all_frames": true
+ }],
+
+ "action": {
+ "default_popup": "popup-simple.html"
+ }
+}
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/package-lock.json b/sensitive_fields_ai_agent/sensitive-field-detector_1/package-lock.json
new file mode 100644
index 000000000..2674d462c
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "sensitive-field-detector",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/popup-simple.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup-simple.html
new file mode 100644
index 000000000..8081fc169
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup-simple.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Extension Disabled
+
+
+
+ How to test:
+ 1. Enable the toggle above
+ 2. Navigate to any webpage
+ 3. Open DevTools Console (F12)
+ 4. Look for logs showing detected elements
+ 5. Try typing in form fields to see DOM changes
+
+
+
+
+
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/popup-simple.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup-simple.js
new file mode 100644
index 000000000..108210166
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup-simple.js
@@ -0,0 +1,170 @@
+// Simple Popup Script
+console.log('๐๏ธ Popup script loading...');
+console.log('๐ Extension ID:', chrome.runtime.id);
+console.log('๐ Chrome runtime available:', !!chrome.runtime);
+
+let isEnabled = false;
+let isToggling = false; // Prevent multiple rapid clicks
+
+// DOM elements
+let toggleSwitch, statusDiv;
+
+// Initialize popup when DOM is ready
+document.addEventListener('DOMContentLoaded', () => {
+ console.log('๐ DOM loaded, getting elements...');
+ toggleSwitch = document.getElementById('toggleSwitch');
+ statusDiv = document.getElementById('status');
+
+ console.log('๐ฏ Elements found:', {
+ toggleSwitch: !!toggleSwitch,
+ statusDiv: !!statusDiv
+ });
+
+ if (toggleSwitch && statusDiv) {
+ init();
+ } else {
+ console.error('โ Could not find required DOM elements');
+ }
+});
+
+async function init() {
+ console.log('๐ง Initializing popup...');
+
+ // Get current state
+ try {
+ console.log('๐ค Sending getState message to background...');
+
+ // Check if chrome.runtime is available
+ if (!chrome.runtime) {
+ console.error('โ chrome.runtime not available');
+ return;
+ }
+
+ const response = await new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({ action: 'getState' }, (response) => {
+ if (chrome.runtime.lastError) {
+ console.error('โ Chrome runtime error:', chrome.runtime.lastError);
+ reject(chrome.runtime.lastError);
+ return;
+ }
+ console.log('๐ฅ Raw response received:', response);
+ resolve(response);
+ });
+ });
+
+ console.log('๐ฅ Processed response:', response);
+
+ if (response && typeof response.enabled !== 'undefined') {
+ isEnabled = response.enabled;
+ console.log('โ
State retrieved successfully:', isEnabled);
+ } else {
+ console.warn('โ ๏ธ Invalid response, defaulting to false');
+ isEnabled = false;
+ }
+
+ updateUI();
+ console.log('๐ฏ Current state set to:', isEnabled);
+ } catch (error) {
+ console.error('โ Error getting state:', error);
+ isEnabled = false;
+ updateUI();
+ }
+
+ // Add event listeners
+ console.log('๐ Adding event listeners to toggle switch...');
+
+ // Single click listener with proper event handling
+ toggleSwitch.addEventListener('click', (event) => {
+ console.log('๐ฑ๏ธ Toggle switch clicked');
+ event.preventDefault();
+ event.stopPropagation();
+ toggleExtension();
+ }, { once: false });
+
+ console.log('โ
Popup ready, isEnabled:', isEnabled);
+}
+
+async function toggleExtension() {
+ if (isToggling) {
+ console.log('โธ๏ธ Already toggling, ignoring click');
+ return;
+ }
+
+ isToggling = true;
+ console.log('๐ Toggle clicked! Current state:', isEnabled);
+ console.log('๐ฏ Toggle switch element:', toggleSwitch);
+
+ const newState = !isEnabled;
+ console.log('๐ New state will be:', newState);
+
+ try {
+ console.log('๐ค Sending setState message to background...');
+
+ const response = await new Promise((resolve, reject) => {
+ const timeout = setTimeout(() => {
+ reject(new Error('Message timeout'));
+ }, 5000);
+
+ chrome.runtime.sendMessage({
+ action: 'setState',
+ enabled: newState
+ }, (response) => {
+ clearTimeout(timeout);
+ if (chrome.runtime.lastError) {
+ console.error('โ Chrome runtime error:', chrome.runtime.lastError);
+ reject(chrome.runtime.lastError);
+ return;
+ }
+ console.log('๐ฅ setState response received:', response);
+ resolve(response);
+ });
+ });
+
+ if (response && response.success) {
+ isEnabled = newState;
+ updateUI();
+ console.log('โ
Extension toggled successfully to:', isEnabled);
+ } else {
+ console.error('โ setState failed, response:', response);
+ updateUI(); // Keep current state
+ }
+ } catch (error) {
+ console.error('โ Error toggling extension:', error);
+ updateUI(); // Keep current state
+ } finally {
+ isToggling = false;
+ }
+}
+
+function updateUI() {
+ console.log('๐จ Updating UI, isEnabled:', isEnabled);
+ console.log('๐ฏ Elements check:', {
+ toggleSwitch: !!toggleSwitch,
+ statusDiv: !!statusDiv
+ });
+
+ if (!toggleSwitch || !statusDiv) {
+ console.error('โ Required elements not found for UI update');
+ return;
+ }
+
+ if (isEnabled) {
+ console.log('โ
Setting UI to ENABLED state...');
+ toggleSwitch.classList.add('active');
+ statusDiv.className = 'status enabled';
+ statusDiv.textContent = 'Extension Enabled - Monitoring DOM';
+ console.log('โ
UI updated to ENABLED state');
+ console.log('๐ Toggle classes:', toggleSwitch.className);
+ console.log('๐ Status classes:', statusDiv.className);
+ } else {
+ console.log('โ Setting UI to DISABLED state...');
+ toggleSwitch.classList.remove('active');
+ statusDiv.className = 'status disabled';
+ statusDiv.textContent = 'Extension Disabled';
+ console.log('โ UI updated to DISABLED state');
+ console.log('๐ Toggle classes:', toggleSwitch.className);
+ console.log('๐ Status classes:', statusDiv.className);
+ }
+}
+
+console.log('๐ฏ Popup script ready');
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.css b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.css
new file mode 100644
index 000000000..430006b84
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.css
@@ -0,0 +1,658 @@
+/* Popup Styles - Modern and Professional Design */
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+ font-size: 14px;
+ line-height: 1.5;
+ color: #2c3e50;
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+ min-height: 600px;
+ width: 400px;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+ margin: 8px;
+ overflow: hidden;
+}
+
+/* Header Styles */
+.header {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ padding: 20px;
+ text-align: center;
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ margin-bottom: 12px;
+}
+
+.logo-icon {
+ font-size: 24px;
+ background: rgba(255, 255, 255, 0.2);
+ padding: 8px;
+ border-radius: 8px;
+}
+
+.title {
+ font-size: 18px;
+ font-weight: 600;
+ margin: 0;
+}
+
+.status {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ font-size: 12px;
+}
+
+.status-indicator {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #e74c3c;
+ animation: pulse 2s infinite;
+}
+
+.status-indicator.active {
+ background: #2ecc71;
+}
+
+@keyframes pulse {
+ 0% { opacity: 1; }
+ 50% { opacity: 0.5; }
+ 100% { opacity: 1; }
+}
+
+/* Main Content Styles */
+.main {
+ flex: 1;
+ padding: 20px;
+ overflow-y: auto;
+}
+
+.section {
+ margin-bottom: 24px;
+ padding-bottom: 20px;
+ border-bottom: 1px solid #ecf0f1;
+}
+
+.section:last-child {
+ border-bottom: none;
+ margin-bottom: 0;
+}
+
+.section-title {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 16px;
+ color: #2c3e50;
+}
+
+.icon {
+ font-size: 18px;
+}
+
+/* Form Styles */
+.form-group {
+ margin-bottom: 16px;
+}
+
+.label {
+ display: block;
+ font-weight: 500;
+ margin-bottom: 6px;
+ color: #34495e;
+}
+
+.input {
+ width: 100%;
+ padding: 12px;
+ border: 2px solid #ecf0f1;
+ border-radius: 6px;
+ font-size: 14px;
+ transition: border-color 0.3s ease;
+ background: #fafbfc;
+}
+
+.input:focus {
+ outline: none;
+ border-color: #667eea;
+ background: white;
+}
+
+.input-group {
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+.input-group .input {
+ padding-right: 45px;
+}
+
+.btn-icon {
+ position: absolute;
+ right: 8px;
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 8px;
+ border-radius: 4px;
+ font-size: 16px;
+ transition: background-color 0.2s ease;
+}
+
+.btn-icon:hover {
+ background: rgba(102, 126, 234, 0.1);
+}
+
+/* Checkbox Styles */
+.checkbox-label {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ cursor: pointer;
+ font-weight: 500;
+}
+
+.checkbox {
+ display: none;
+}
+
+.checkbox-custom {
+ width: 20px;
+ height: 20px;
+ border: 2px solid #bdc3c7;
+ border-radius: 4px;
+ position: relative;
+ transition: all 0.3s ease;
+ background: white;
+}
+
+.checkbox:checked + .checkbox-custom {
+ background: #667eea;
+ border-color: #667eea;
+}
+
+.checkbox:checked + .checkbox-custom::after {
+ content: 'โ';
+ position: absolute;
+ color: white;
+ font-size: 12px;
+ font-weight: bold;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
+
+/* Slider Styles */
+.slider {
+ width: 100%;
+ height: 6px;
+ border-radius: 3px;
+ background: #ecf0f1;
+ outline: none;
+ -webkit-appearance: none;
+ appearance: none;
+}
+
+.slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background: #667eea;
+ cursor: pointer;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+}
+
+.slider::-moz-range-thumb {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background: #667eea;
+ cursor: pointer;
+ border: none;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+}
+
+/* Button Styles */
+.btn-primary, .btn-secondary {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 12px 20px;
+ border: none;
+ border-radius: 6px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-decoration: none;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+}
+
+.btn-primary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+}
+
+.btn-secondary {
+ background: #ecf0f1;
+ color: #2c3e50;
+ border: 1px solid #bdc3c7;
+}
+
+.btn-secondary:hover {
+ background: #d5dbdb;
+ transform: translateY(-1px);
+}
+
+/* Help Text */
+.help-text {
+ font-size: 12px;
+ color: #7f8c8d;
+ margin-top: 4px;
+ line-height: 1.4;
+}
+
+.help-text a {
+ color: #667eea;
+ text-decoration: none;
+}
+
+.help-text a:hover {
+ text-decoration: underline;
+}
+
+/* API Status Card */
+.api-status-card {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+ padding: 16px;
+ border-radius: 8px;
+ border: 1px solid #dee2e6;
+ margin-bottom: 8px;
+}
+
+.api-status-icon {
+ font-size: 24px;
+ background: rgba(102, 126, 234, 0.1);
+ padding: 12px;
+ border-radius: 8px;
+ color: #667eea;
+}
+
+.api-status-content {
+ flex: 1;
+}
+
+.api-status-content h3 {
+ margin: 0 0 4px 0;
+ font-size: 14px;
+ font-weight: 600;
+ color: #2c3e50;
+}
+
+.api-status-text {
+ margin: 0 0 6px 0;
+ font-size: 12px;
+ color: #7f8c8d;
+}
+
+.api-key-display {
+ font-family: 'Courier New', monospace;
+ font-size: 11px;
+ background: rgba(102, 126, 234, 0.1);
+ color: #667eea;
+ padding: 4px 8px;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+.api-status-indicator {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ font-size: 10px;
+ text-align: center;
+}
+
+.status-dot {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ background: #ffc107;
+ animation: pulse 2s infinite;
+}
+
+.status-dot.success {
+ background: #28a745;
+}
+
+.status-dot.error {
+ background: #dc3545;
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+}
+
+/* Status Messages */
+.status-message {
+ padding: 8px 12px;
+ border-radius: 4px;
+ font-size: 12px;
+ margin-top: 8px;
+ display: none;
+}
+
+.status-message.success {
+ background: #d5f4e6;
+ color: #27ae60;
+ border: 1px solid #a8e6cf;
+}
+
+.status-message.error {
+ background: #fdf2f2;
+ color: #e74c3c;
+ border: 1px solid #f5b7b1;
+}
+
+/* Privacy Info */
+.privacy-info {
+ background: #f8f9fa;
+ padding: 16px;
+ border-radius: 6px;
+ margin-bottom: 16px;
+}
+
+.privacy-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+ font-size: 13px;
+}
+
+.privacy-item:last-child {
+ margin-bottom: 0;
+}
+
+.privacy-icon {
+ color: #27ae60;
+ font-weight: bold;
+}
+
+/* Statistics Grid */
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 12px;
+ margin-top: 12px;
+}
+
+.stat-item {
+ background: #f8f9fa;
+ padding: 16px;
+ border-radius: 6px;
+ text-align: center;
+ border: 1px solid #ecf0f1;
+}
+
+.stat-value {
+ font-size: 24px;
+ font-weight: bold;
+ color: #667eea;
+ margin-bottom: 4px;
+}
+
+.stat-label {
+ font-size: 12px;
+ color: #7f8c8d;
+ font-weight: 500;
+}
+
+/* Footer */
+.footer {
+ padding: 20px;
+ background: #f8f9fa;
+ border-top: 1px solid #ecf0f1;
+}
+
+.actions {
+ display: flex;
+ gap: 12px;
+ margin-bottom: 16px;
+}
+
+.actions .btn-primary {
+ flex: 1;
+}
+
+.footer-info {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ font-size: 12px;
+ color: #7f8c8d;
+}
+
+.separator {
+ color: #bdc3c7;
+}
+
+.link {
+ color: #667eea;
+ text-decoration: none;
+}
+
+.link:hover {
+ text-decoration: underline;
+}
+
+.version {
+ font-weight: 500;
+}
+
+/* Toast Notifications */
+.toast {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ padding: 12px 20px;
+ border-radius: 6px;
+ font-size: 14px;
+ font-weight: 500;
+ z-index: 1000;
+ transform: translateX(400px);
+ transition: transform 0.3s ease;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.toast.show {
+ transform: translateX(0);
+}
+
+.toast.success {
+ background: #27ae60;
+ color: white;
+}
+
+.toast.error {
+ background: #e74c3c;
+ color: white;
+}
+
+.toast.info {
+ background: #3498db;
+ color: white;
+}
+
+/* Loading Overlay */
+.loading-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ display: none;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ z-index: 2000;
+}
+
+.loading-overlay.show {
+ display: flex;
+}
+
+.loading-spinner {
+ width: 40px;
+ height: 40px;
+ border: 4px solid rgba(255, 255, 255, 0.3);
+ border-top: 4px solid white;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin-bottom: 16px;
+}
+
+.loading-text {
+ color: white;
+ font-size: 16px;
+ font-weight: 500;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* Responsive Design */
+@media (max-width: 400px) {
+ body {
+ width: 100vw;
+ }
+
+ .container {
+ margin: 0;
+ border-radius: 0;
+ min-height: 100vh;
+ }
+
+ .stats-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .actions {
+ flex-direction: column;
+ }
+}
+
+/* Dark Mode Support */
+@media (prefers-color-scheme: dark) {
+ body {
+ background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
+ }
+
+ .container {
+ background: #2c3e50;
+ color: #ecf0f1;
+ }
+
+ .section {
+ border-bottom-color: #34495e;
+ }
+
+ .section-title {
+ color: #ecf0f1;
+ }
+
+ .label {
+ color: #bdc3c7;
+ }
+
+ .input {
+ background: #34495e;
+ border-color: #4a5f7a;
+ color: #ecf0f1;
+ }
+
+ .input:focus {
+ background: #34495e;
+ border-color: #667eea;
+ }
+
+ .btn-secondary {
+ background: #34495e;
+ color: #ecf0f1;
+ border-color: #4a5f7a;
+ }
+
+ .btn-secondary:hover {
+ background: #4a5f7a;
+ }
+
+ .help-text {
+ color: #95a5a6;
+ }
+
+ .privacy-info, .stat-item {
+ background: #34495e;
+ border-color: #4a5f7a;
+ }
+
+ .footer {
+ background: #34495e;
+ border-top-color: #4a5f7a;
+ }
+}
+
+/* Animation for better UX */
+.section {
+ animation: fadeInUp 0.6s ease-out;
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.html
new file mode 100644
index 000000000..ddd14440a
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.html
@@ -0,0 +1,208 @@
+
+
+
+
+
+ Sensitive Field Detector Settings
+
+
+
+
+
+
+
+
+
+
+
+
+ ๐ค
+ AI Configuration
+
+
+
+
+
+
+
+
+
+
+ โ๏ธ
+ Detection Settings
+
+
+
+
+
+
+
+
+
+
+
+
+ ๐ก๏ธ
+ Privacy & Security
+
+
+
+
+ โ
+ No user input values are transmitted
+
+
+ โ
+ Only metadata and patterns are analyzed
+
+
+ โ
+ Data is anonymized before AI analysis
+
+
+ โ
+ Local caching minimizes external requests
+
+
+
+
+
+
+
+
+
+ ๐
+ Statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.js
new file mode 100644
index 000000000..6d65d7ce0
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/popup/popup.js
@@ -0,0 +1,400 @@
+// Popup JavaScript - Settings and UI management
+// Handles user interactions, settings management, and API testing
+
+class PopupManager {
+ constructor() {
+ // Default settings - API key is embedded in background script
+ this.settings = {
+ enableAI: true,
+ enablePatterns: true,
+ aiConfidenceThreshold: 0.7,
+ enableVisualIndicators: false
+ };
+
+ this.elements = {};
+ this.isApiKeyVisible = false;
+
+ this.initialize();
+ }
+
+ async initialize() {
+ console.log('๐๏ธ Popup initializing...');
+
+ // Get DOM elements
+ this.cacheElements();
+
+ // Set up event listeners
+ this.setupEventListeners();
+
+ // Load current settings
+ await this.loadSettings();
+
+ // Update UI
+ this.updateUI();
+
+ // Load statistics
+ await this.loadStatistics();
+
+ console.log('โ
Popup ready');
+ }
+
+ cacheElements() {
+ this.elements = {
+ // Status elements
+ statusIndicator: document.getElementById('statusIndicator'),
+ statusText: document.getElementById('statusText'),
+
+ // API status elements
+ apiStatusIndicator: document.getElementById('apiStatusIndicator'),
+ apiStatusText: document.getElementById('apiStatusText'),
+
+ // Form elements
+ enableAI: document.getElementById('enableAI'),
+ enablePatterns: document.getElementById('enablePatterns'),
+ confidenceThreshold: document.getElementById('confidenceThreshold'),
+ confidenceValue: document.getElementById('confidenceValue'),
+ enableVisualIndicators: document.getElementById('enableVisualIndicators'),
+
+ // Action buttons
+ saveSettings: document.getElementById('saveSettings'),
+ testDetection: document.getElementById('testDetection'),
+ clearCache: document.getElementById('clearCache'),
+
+ // Statistics
+ totalRequests: document.getElementById('totalRequests'),
+ successRate: document.getElementById('successRate'),
+ avgResponseTime: document.getElementById('avgResponseTime'),
+ cacheHits: document.getElementById('cacheHits'),
+
+ // UI elements
+ toast: document.getElementById('toast'),
+ loadingOverlay: document.getElementById('loadingOverlay'),
+
+ // Links
+ helpLink: document.getElementById('helpLink'),
+ reportIssue: document.getElementById('reportIssue')
+ };
+ }
+
+ setupEventListeners() {
+ // Confidence threshold slider
+ this.elements.confidenceThreshold.addEventListener('input', (e) => {
+ this.elements.confidenceValue.textContent = `${e.target.value}%`;
+ this.settings.aiConfidenceThreshold = parseInt(e.target.value) / 100;
+ });
+
+ // Save settings button
+ this.elements.saveSettings.addEventListener('click', () => {
+ this.saveSettings();
+ });
+
+ // Test detection button
+ this.elements.testDetection.addEventListener('click', () => {
+ this.testDetection();
+ });
+
+ // Clear cache button
+ this.elements.clearCache.addEventListener('click', () => {
+ this.clearCache();
+ });
+
+ // Checkbox listeners
+ this.elements.enableAI.addEventListener('change', (e) => {
+ this.settings.enableAI = e.target.checked;
+ this.updateAISection();
+ });
+
+ this.elements.enablePatterns.addEventListener('change', (e) => {
+ this.settings.enablePatterns = e.target.checked;
+ });
+
+ this.elements.enableVisualIndicators.addEventListener('change', (e) => {
+ this.settings.enableVisualIndicators = e.target.checked;
+ });
+
+ // Help and support links
+ this.elements.helpLink.addEventListener('click', (e) => {
+ e.preventDefault();
+ this.openHelp();
+ });
+
+ this.elements.reportIssue.addEventListener('click', (e) => {
+ e.preventDefault();
+ this.reportIssue();
+ });
+
+ // Real-time form validation - no longer needed with embedded API key
+ }
+
+ async loadSettings() {
+ try {
+ const response = await chrome.runtime.sendMessage({ action: 'getSettings' });
+
+ if (response?.success) {
+ this.settings = { ...this.settings, ...response.settings };
+ this.populateForm();
+ }
+ } catch (error) {
+ console.error('Failed to load settings:', error);
+ this.showToast('Failed to load settings', 'error');
+ }
+ }
+
+ populateForm() {
+ this.elements.enableAI.checked = this.settings.enableAI;
+ this.elements.enablePatterns.checked = this.settings.enablePatterns;
+ this.elements.confidenceThreshold.value = Math.round(this.settings.aiConfidenceThreshold * 100);
+ this.elements.confidenceValue.textContent = `${Math.round(this.settings.aiConfidenceThreshold * 100)}%`;
+ this.elements.enableVisualIndicators.checked = this.settings.enableVisualIndicators;
+
+ // Test the embedded API key
+ this.testEmbeddedApiKey();
+ }
+
+ updateUI() {
+ this.updateStatus();
+ this.updateAISection();
+ }
+
+ updateStatus() {
+ const hasApiKey = Boolean(this.settings.geminiApiKey);
+ const isEnabled = this.settings.enableAI && hasApiKey;
+
+ if (isEnabled) {
+ this.elements.statusIndicator.classList.add('active');
+ this.elements.statusText.textContent = 'Active';
+ } else {
+ this.elements.statusIndicator.classList.remove('active');
+ this.elements.statusText.textContent = 'Inactive';
+ }
+ }
+
+ async testEmbeddedApiKey() {
+ this.updateApiKeyStatus('Testing...', 'testing');
+
+ try {
+ const response = await chrome.runtime.sendMessage({
+ action: 'testAPIKey'
+ });
+
+ if (response?.success) {
+ if (response.valid) {
+ this.updateApiKeyStatus('Connected', 'success');
+ } else {
+ this.updateApiKeyStatus('Failed', 'error');
+ }
+ } else {
+ this.updateApiKeyStatus('Error', 'error');
+ }
+ } catch (error) {
+ console.error('API key test failed:', error);
+ this.updateApiKeyStatus('Error', 'error');
+ }
+ }
+
+ updateApiKeyStatus(text, status) {
+ this.elements.apiStatusText.textContent = text;
+ const statusDot = this.elements.apiStatusIndicator.querySelector('.status-dot');
+
+ statusDot.className = 'status-dot';
+ if (status === 'success') {
+ statusDot.classList.add('success');
+ } else if (status === 'error') {
+ statusDot.classList.add('error');
+ }
+ // 'testing' uses default yellow with pulse animation
+ }
+
+ updateAISection() {
+ const aiElements = document.querySelectorAll('[data-ai-dependent]');
+ const isEnabled = this.settings.enableAI;
+
+ aiElements.forEach(element => {
+ element.style.opacity = isEnabled ? '1' : '0.5';
+ element.style.pointerEvents = isEnabled ? 'auto' : 'none';
+ });
+ }
+
+ isFormValid() {
+ // Basic validation - API key is embedded, so always valid
+ return true;
+ }
+
+ async saveSettings() {
+ try {
+ // Collect current form values (no API key needed since it's embedded)
+ const formSettings = {
+ enableAI: this.elements.enableAI.checked,
+ enablePatterns: this.elements.enablePatterns.checked,
+ aiConfidenceThreshold: parseInt(this.elements.confidenceThreshold.value) / 100,
+ enableVisualIndicators: this.elements.enableVisualIndicators.checked
+ };
+
+ const response = await chrome.runtime.sendMessage({
+ action: 'updateSettings',
+ settings: formSettings
+ });
+
+ if (response?.success) {
+ this.settings = { ...this.settings, ...formSettings };
+ this.updateUI();
+ this.showToast('Settings saved successfully', 'success');
+ } else {
+ this.showToast('Failed to save settings', 'error');
+ }
+ } catch (error) {
+ console.error('Failed to save settings:', error);
+ this.showToast('Failed to save settings', 'error');
+ }
+ }
+
+ async testDetection() {
+ try {
+ this.showToast('Opening test page...', 'info');
+
+ // Create a test page with various form fields
+ const testPageUrl = chrome.runtime.getURL('test/test-page.html');
+ await chrome.tabs.create({ url: testPageUrl });
+
+ // Close popup after opening test page
+ window.close();
+ } catch (error) {
+ console.error('Failed to open test page:', error);
+ this.showToast('Failed to open test page', 'error');
+ }
+ }
+
+ async clearCache() {
+ if (!confirm('Are you sure you want to clear all cached data? This action cannot be undone.')) {
+ return;
+ }
+
+ try {
+ const response = await chrome.runtime.sendMessage({ action: 'clearCache' });
+
+ if (response?.success) {
+ this.showToast('Cache cleared successfully', 'success');
+ await this.loadStatistics(); // Refresh stats
+ } else {
+ this.showToast('Failed to clear cache', 'error');
+ }
+ } catch (error) {
+ console.error('Failed to clear cache:', error);
+ this.showToast('Failed to clear cache', 'error');
+ }
+ }
+
+ async loadStatistics() {
+ try {
+ const response = await chrome.runtime.sendMessage({ action: 'getStatistics' });
+
+ if (response?.success) {
+ this.updateStatistics(response.statistics);
+ }
+ } catch (error) {
+ console.error('Failed to load statistics:', error);
+ }
+ }
+
+ updateStatistics(stats) {
+ this.elements.totalRequests.textContent = stats.totalRequests || 0;
+ this.elements.successRate.textContent = `${Math.round((stats.successRate || 0) * 100)}%`;
+ this.elements.avgResponseTime.textContent = `${Math.round(stats.averageResponseTime || 0)}ms`;
+
+ // Calculate cache efficiency (this would come from content script)
+ const cacheEfficiency = stats.cacheHits ?
+ Math.round((stats.cacheHits / (stats.cacheHits + stats.cacheMisses)) * 100) : 0;
+ this.elements.cacheHits.textContent = `${cacheEfficiency}%`;
+ }
+
+ showToast(message, type = 'info') {
+ this.elements.toast.textContent = message;
+ this.elements.toast.className = `toast ${type}`;
+ this.elements.toast.classList.add('show');
+
+ setTimeout(() => {
+ this.elements.toast.classList.remove('show');
+ }, 3000);
+ }
+
+ showLoading(message = 'Loading...') {
+ this.elements.loadingOverlay.querySelector('.loading-text').textContent = message;
+ this.elements.loadingOverlay.classList.add('show');
+ }
+
+ hideLoading() {
+ this.elements.loadingOverlay.classList.remove('show');
+ }
+
+ openHelp() {
+ const helpUrl = 'https://github.com/your-repo/sensitive-field-detector/wiki';
+ chrome.tabs.create({ url: helpUrl });
+ }
+
+ reportIssue() {
+ const issueUrl = 'https://github.com/your-repo/sensitive-field-detector/issues/new';
+ chrome.tabs.create({ url: issueUrl });
+ }
+}
+
+// Utility functions
+function formatUptime(milliseconds) {
+ const seconds = Math.floor(milliseconds / 1000);
+ const minutes = Math.floor(seconds / 60);
+ const hours = Math.floor(minutes / 60);
+
+ if (hours > 0) {
+ return `${hours}h ${minutes % 60}m`;
+ } else if (minutes > 0) {
+ return `${minutes}m ${seconds % 60}s`;
+ } else {
+ return `${seconds}s`;
+ }
+}
+
+function formatFileSize(bytes) {
+ if (bytes === 0) return '0 B';
+
+ const sizes = ['B', 'KB', 'MB', 'GB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
+
+ return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
+}
+
+// Initialize popup when DOM is ready
+document.addEventListener('DOMContentLoaded', () => {
+ new PopupManager();
+});
+
+// Handle keyboard shortcuts
+document.addEventListener('keydown', (e) => {
+ // Ctrl/Cmd + S to save settings
+ if ((e.ctrlKey || e.metaKey) && e.key === 's') {
+ e.preventDefault();
+ document.getElementById('saveSettings')?.click();
+ }
+
+ // Escape to close
+ if (e.key === 'Escape') {
+ window.close();
+ }
+});
+
+// Auto-save on form changes (debounced)
+let autoSaveTimeout;
+document.addEventListener('input', () => {
+ clearTimeout(autoSaveTimeout);
+ autoSaveTimeout = setTimeout(() => {
+ // Auto-save logic could go here
+ // For now, just validate the form
+ document.querySelector('.popup-manager')?.validateForm();
+ }, 1000);
+});
+
+// Handle extension messages
+chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
+ if (message.action === 'settingsUpdated') {
+ // Reload settings when updated from another source
+ window.location.reload();
+ }
+});
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/retry-test.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/retry-test.html
new file mode 100644
index 000000000..7a93109ac
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/retry-test.html
@@ -0,0 +1,304 @@
+
+
+
+
+
+ Retry Mechanism Test - Sensitive Field Detector
+
+
+
+
+
๐ Retry Mechanism Test
+
+
+
๐ Test Instructions:
+
+ - This page contains various sensitive fields to test the extension
+ - The extension should automatically detect these fields using AI
+ - If the Gemini API is overloaded (503 error), the extension will retry automatically
+ - Watch the console for retry attempts and fallback detection
+ - A status indicator should appear in the top-right corner
+ - Check that the extension gracefully handles API failures
+
+
+
+
+
+
+
+
+
+
+
๐ Address Information
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
๐ Hidden Sensitive Fields
+
+
+
+
These hidden fields contain sensitive tokens and should be detected by the extension.
+
+
+
+
๐ Additional Information
+
+
+
+
+
+
+
+
๐ฏ Test Controls
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-automatic-trigger.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-automatic-trigger.html
new file mode 100644
index 000000000..1de32f0f3
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-automatic-trigger.html
@@ -0,0 +1,248 @@
+
+
+
+
+
+ Automatic Raw DOM Analysis Test
+
+
+
+
+
๐ Automatic Raw DOM Analysis Test
+
+
+
๐ Test Instructions:
+
+ - Make sure the sensitive-field-detector extension is loaded and enabled
+ - Refresh this page
+ - Watch the console for automatic raw DOM analysis logs
+ - Check the status below for confirmation
+
+
+
+
+ โณ Waiting for automatic raw DOM analysis...
+
+
+
+
๐ Test Form Fields
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-extension.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-extension.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-extension.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-extension.js
new file mode 100644
index 000000000..9a24d3885
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-extension.js
@@ -0,0 +1,376 @@
+// Test Script for Sensitive Field Detector Extension
+// Run this in the browser console to test various scenarios
+
+console.log('๐งช Sensitive Field Detector Test Script Loaded');
+
+// Test 1: Check if extension is loaded
+function testExtensionPresence() {
+ console.log('๐ Test 1: Checking extension presence...');
+
+ if (typeof chrome !== 'undefined' && chrome.runtime) {
+ console.log('โ
Chrome extension API detected');
+
+ // Try to communicate with extension
+ chrome.runtime.sendMessage({ action: 'getState' }, (response) => {
+ if (chrome.runtime.lastError) {
+ console.log('โ ๏ธ Extension not responding:', chrome.runtime.lastError.message);
+ } else {
+ console.log('โ
Extension responding:', response);
+ }
+ });
+ } else {
+ console.log('โ Chrome extension API not detected');
+ }
+}
+
+// Test 2: Check for sensitive fields in DOM
+function testSensitiveFieldDetection() {
+ console.log('๐ Test 2: Checking for sensitive fields in DOM...');
+
+ const sensitiveSelectors = [
+ 'input[type="password"]',
+ 'input[type="email"]',
+ 'input[name*="password"]',
+ 'input[name*="email"]',
+ 'input[name*="ssn"]',
+ 'input[name*="credit"]',
+ 'input[name*="card"]',
+ 'input[name*="cvv"]',
+ 'input[name*="token"]',
+ 'input[name*="secret"]',
+ 'input[name*="auth"]',
+ 'input[name*="pin"]',
+ 'input[name*="otp"]',
+ 'input[name*="api"]',
+ 'input[name*="key"]'
+ ];
+
+ let foundFields = [];
+
+ sensitiveSelectors.forEach(selector => {
+ const elements = document.querySelectorAll(selector);
+ if (elements.length > 0) {
+ foundFields.push({
+ selector: selector,
+ count: elements.length,
+ elements: Array.from(elements).map(el => ({
+ id: el.id,
+ name: el.name,
+ type: el.type,
+ placeholder: el.placeholder
+ }))
+ });
+ }
+ });
+
+ console.log('๐ Found sensitive fields:', foundFields);
+ return foundFields;
+}
+
+// Test 3: Simulate user interactions
+function testUserInteractions() {
+ console.log('๐ Test 3: Simulating user interactions...');
+
+ const testFields = [
+ { selector: '#username', value: 'testuser' },
+ { selector: '#email', value: 'test@example.com' },
+ { selector: '#password', value: 'testpassword123' },
+ { selector: '#phone', value: '555-123-4567' },
+ { selector: '#ssn', value: '123-45-6789' },
+ { selector: '#credit-card', value: '4111-1111-1111-1111' },
+ { selector: '#cvv', value: '123' }
+ ];
+
+ testFields.forEach(field => {
+ const element = document.querySelector(field.selector);
+ if (element) {
+ console.log(`๐ฑ๏ธ Interacting with ${field.selector}...`);
+
+ // Focus the field
+ element.focus();
+
+ // Simulate typing
+ element.value = field.value;
+
+ // Trigger input event
+ element.dispatchEvent(new Event('input', { bubbles: true }));
+
+ // Trigger change event
+ element.dispatchEvent(new Event('change', { bubbles: true }));
+
+ // Blur the field
+ element.blur();
+
+ console.log(`โ
Completed interaction with ${field.selector}`);
+ } else {
+ console.log(`โ ๏ธ Field not found: ${field.selector}`);
+ }
+ });
+}
+
+// Test 4: Check for extension detection markers
+function testDetectionMarkers() {
+ console.log('๐ Test 4: Checking for extension detection markers...');
+
+ const markers = [
+ '[data-sensitive-field="true"]',
+ '[data-ai-detection="true"]',
+ '[data-field-type]',
+ '[data-confidence]',
+ '[data-risk-level]'
+ ];
+
+ let foundMarkers = [];
+
+ markers.forEach(marker => {
+ const elements = document.querySelectorAll(marker);
+ if (elements.length > 0) {
+ foundMarkers.push({
+ marker: marker,
+ count: elements.length,
+ elements: Array.from(elements).map(el => ({
+ id: el.id,
+ name: el.name,
+ type: el.type,
+ attributes: {
+ 'data-sensitive-field': el.getAttribute('data-sensitive-field'),
+ 'data-ai-detection': el.getAttribute('data-ai-detection'),
+ 'data-field-type': el.getAttribute('data-field-type'),
+ 'data-confidence': el.getAttribute('data-confidence'),
+ 'data-risk-level': el.getAttribute('data-risk-level')
+ }
+ }))
+ });
+ }
+ });
+
+ console.log('๐ Found detection markers:', foundMarkers);
+ return foundMarkers;
+}
+
+// Test 5: Monitor for extension logs
+function monitorExtensionLogs() {
+ console.log('๐ Test 5: Monitoring for extension logs...');
+
+ // Override console.log to capture extension logs
+ const originalLog = console.log;
+ const extensionLogs = [];
+
+ console.log = function(...args) {
+ const message = args.join(' ');
+
+ // Check if this looks like an extension log
+ if (message.includes('๐') ||
+ message.includes('๐ฏ') ||
+ message.includes('๐ง ') ||
+ message.includes('๐ญ') ||
+ message.includes('โ
') ||
+ message.includes('Sensitive Field') ||
+ message.includes('AI detected') ||
+ message.includes('masking')) {
+
+ extensionLogs.push({
+ timestamp: new Date().toISOString(),
+ message: message
+ });
+
+ console.log('๐ Extension Log Captured:', message);
+ }
+
+ originalLog.apply(console, args);
+ };
+
+ console.log('๐ฏ Extension log monitoring started');
+
+ // Return function to stop monitoring
+ return function() {
+ console.log = originalLog;
+ console.log('๐ Extension log monitoring stopped');
+ console.log('๐ Total extension logs captured:', extensionLogs.length);
+ return extensionLogs;
+ };
+}
+
+// Test 6: Test dynamic field addition
+function testDynamicFields() {
+ console.log('๐ Test 6: Testing dynamic field addition...');
+
+ const dynamicContainer = document.getElementById('dynamic-fields-container');
+ if (dynamicContainer) {
+ console.log('โ
Dynamic container found, adding test fields...');
+
+ // Add some dynamic fields
+ const timestamp = Date.now();
+ const dynamicFields = `
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ dynamicContainer.innerHTML += dynamicFields;
+ console.log('โ
Dynamic fields added successfully');
+
+ // Wait a bit and check if they were detected
+ setTimeout(() => {
+ const newFields = document.querySelectorAll(`[id^="test-dynamic-"]`);
+ console.log('๐ New dynamic fields found:', newFields.length);
+
+ newFields.forEach(field => {
+ if (field.hasAttribute('data-sensitive-field')) {
+ console.log(`โ
Dynamic field detected: ${field.id}`);
+ } else {
+ console.log(`โ ๏ธ Dynamic field not detected: ${field.id}`);
+ }
+ });
+ }, 2000);
+
+ } else {
+ console.log('โ Dynamic container not found');
+ }
+}
+
+// Test 7: Test sensitive text content detection
+function testSensitiveTextContent() {
+ console.log('๐ Test 7: Testing sensitive text content detection...');
+
+ const sensitiveTextElements = document.querySelectorAll('.sensitive-text');
+ console.log(`๐ Found ${sensitiveTextElements.length} sensitive text elements`);
+
+ if (sensitiveTextElements.length === 0) {
+ console.log('โ No sensitive text elements found');
+ return;
+ }
+
+ const textTypes = {};
+ sensitiveTextElements.forEach(element => {
+ const type = element.getAttribute('data-type');
+ if (!textTypes[type]) {
+ textTypes[type] = [];
+ }
+ textTypes[type].push({
+ text: element.textContent,
+ element: element
+ });
+ });
+
+ console.log('๐ Sensitive text types found:', Object.keys(textTypes));
+ console.log('๐ Text content breakdown:', textTypes);
+
+ // Check if any have been marked by extension
+ const markedElements = document.querySelectorAll('.sensitive-text[data-sensitive-field="true"]');
+ console.log(`๐ฏ Elements marked by extension: ${markedElements.length}`);
+
+ // Test interaction with sensitive text
+ console.log('๐ฑ๏ธ Testing interaction with sensitive text elements...');
+ sensitiveTextElements.forEach((element, index) => {
+ if (index < 3) { // Test first 3 elements
+ element.click();
+ console.log(`โ
Clicked sensitive text: ${element.textContent.substring(0, 20)}...`);
+ }
+ });
+
+ return {
+ totalElements: sensitiveTextElements.length,
+ textTypes: Object.keys(textTypes),
+ markedElements: markedElements.length,
+ breakdown: textTypes
+ };
+}
+
+// Test 8: Comprehensive test suite
+function runComprehensiveTest() {
+ console.log('๐ Starting comprehensive extension test suite...');
+ console.log('=' .repeat(60));
+
+ // Test 1: Extension presence
+ testExtensionPresence();
+
+ // Wait a bit
+ setTimeout(() => {
+ // Test 2: Sensitive field detection
+ testSensitiveFieldDetection();
+
+ // Test 3: User interactions
+ testUserInteractions();
+
+ // Test 4: Detection markers
+ testDetectionMarkers();
+
+ // Test 5: Dynamic fields
+ testDynamicFields();
+
+ // Test 6: Sensitive text content
+ testSensitiveTextContent();
+
+ console.log('=' .repeat(60));
+ console.log('โ
Comprehensive test suite completed');
+ console.log('๐ Check the logs above for results');
+
+ }, 1000);
+}
+
+// Test 9: Performance test
+function testPerformance() {
+ console.log('๐ Test 9: Performance testing...');
+
+ const startTime = performance.now();
+
+ // Simulate rapid field interactions
+ const fields = document.querySelectorAll('input, textarea, select');
+ console.log(`๐ Testing performance with ${fields.length} fields`);
+
+ fields.forEach((field, index) => {
+ setTimeout(() => {
+ field.focus();
+ field.dispatchEvent(new Event('input', { bubbles: true }));
+ field.blur();
+ }, index * 50); // 50ms intervals
+ });
+
+ setTimeout(() => {
+ const endTime = performance.now();
+ console.log(`โฑ๏ธ Performance test completed in ${endTime - startTime}ms`);
+ }, fields.length * 50 + 1000);
+}
+
+// Make all test functions globally available
+window.testExtensionPresence = testExtensionPresence;
+window.testSensitiveFieldDetection = testSensitiveFieldDetection;
+window.testUserInteractions = testUserInteractions;
+window.testDetectionMarkers = testDetectionMarkers;
+window.monitorExtensionLogs = monitorExtensionLogs;
+window.testDynamicFields = testDynamicFields;
+window.testSensitiveTextContent = testSensitiveTextContent;
+window.runComprehensiveTest = runComprehensiveTest;
+window.testPerformance = testPerformance;
+
+// Auto-run basic tests when script loads
+document.addEventListener('DOMContentLoaded', () => {
+ console.log('๐งช Auto-running basic extension tests...');
+
+ setTimeout(() => {
+ testExtensionPresence();
+ testSensitiveFieldDetection();
+ testSensitiveTextContent();
+ }, 2000);
+});
+
+console.log('๐งช Test functions available:');
+console.log('- testExtensionPresence()');
+console.log('- testSensitiveFieldDetection()');
+console.log('- testUserInteractions()');
+console.log('- testDetectionMarkers()');
+console.log('- monitorExtensionLogs()');
+console.log('- testDynamicFields()');
+console.log('- testSensitiveTextContent()');
+console.log('- runComprehensiveTest()');
+console.log('- testPerformance()');
\ No newline at end of file
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-modern-web-app.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-modern-web-app.html
new file mode 100644
index 000000000..97562295e
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-modern-web-app.html
@@ -0,0 +1,337 @@
+
+
+
+
+
+ Modern Web App Detection Test
+
+
+
+
+
๐ Modern Web App Detection Test
+
This page simulates modern web app patterns like Twitter, with shadow DOM, dynamic content, and complex structures.
+
+
+
๐ Traditional Form Elements
+
+
+
+
๐ Shadow DOM Elements
+
+
+
+
โก Dynamic Content
+
+
+
+
+
+
+
+
+
+
+
+
+
โ๏ธ Modern Framework Patterns
+
+
+
+
โ๏ธ Contenteditable Elements
+
+
+
+
+
๐ป Hidden Fields
+
+
+
+
+
+
๐งฉ Custom Web Components
+
+
+
+
+
๐ Location Information
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
๐ Detection Status
+
Expected Detection: 15+ sensitive fields across different patterns
+
Patterns Tested: Traditional forms, Shadow DOM, Dynamic content, React patterns, Contenteditable, Hidden fields, Custom components
+
Modern Features: data-testid, aria-label, role attributes, shadow DOM simulation
+
+
+
+ Detection Log:
+ Waiting for extension to load and analyze...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-pre-visibility.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-pre-visibility.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-raw-dom-analysis.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-raw-dom-analysis.html
new file mode 100644
index 000000000..9f6f79d74
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-raw-dom-analysis.html
@@ -0,0 +1,305 @@
+
+
+
+
+
+ Raw DOM Analysis Test - Sensitive Field Detector
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-sensitive-fields.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-sensitive-fields.html
new file mode 100644
index 000000000..709debab7
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-sensitive-fields.html
@@ -0,0 +1,873 @@
+
+
+
+
+
+ ๐ Sensitive Field Detector Test Site
+
+
+
+
+
+
+
+
+
๐งช How to Test the Extension:
+
+ - Load the Extension: Make sure the sensitive-field-detector_1 extension is loaded in Chrome
+ - Enable Detection: Click the extension icon and enable detection
+ - Open DevTools: Press F12 and go to Console tab
+ - Watch for Logs: Look for detection logs starting with ๐, ๐ฏ, ๐ง , etc.
+ - Test Different Scenarios: Scroll through different sections and interact with forms
+ - Check Masking: Verify that sensitive fields are being masked by the browser API
+
+
+
+
+
+
๐ Basic Authentication Fields
+
+
+
+
+
+
+
+
+
๐ค Personal Information Fields
+
+
+
+
+
+
+
+
+
+
๐ฐ Financial Information Fields
+
+
+
+
+
+
+
+
+
+
๐ Address Information Fields
+
+
+
+
+
+
+
+
+
+
๐ Security and Token Fields
+
+
+
+
+
+
+
+
+
๐ป Hidden Fields
+
+
Hidden fields that should be detected:
+
+
+
+
+
Expected: High sensitivity - CSRF token, session ID, auth token
+
+
+
+
+
+
โก Dynamic Content Fields
+
+
Fields that will be added dynamically:
+
+
+
+
+
+
+
+
๐ Textarea Fields
+
+
+
+
+
+
+
๐ Sensitive Text Content (Display Only)
+
These are regular text elements containing sensitive information that should be detected and masked.
+
+
+
๐ค Personal Information Display
+
+
Email Address: john.doe@example.com
+
Phone Number: +1-555-123-4567
+
Mobile Number: (555) 987-6543
+
Full Name: John Michael Doe
+
Date of Birth: March 15, 1985
+
Age: 38 years old
+
+
+
๐ Address Information Display
+
+
Home Address: 123 Main Street, Apt 4B, New York, NY 10001
+
Work Address: 456 Business Ave, Suite 200, Los Angeles, CA 90210
+
ZIP Code: 10001
+
Country: United States
+
+
+
๐ฐ Financial Information Display
+
+
Credit Card Number: 4111-1111-1111-1111
+
Debit Card Number: 5555-4444-3333-2222
+
Bank Account Number: 1234567890123456
+
Routing Number: 021000021
+
IBAN: GB29 NWBK 6016 1331 9268 19
+
Account Balance: $12,345.67
+
Transaction Amount: $1,234.56
+
+
+
๐ Government ID Information Display
+
+
Social Security Number: 123-45-6789
+
Driver's License: DL123456789
+
Passport Number: A12345678
+
Tax ID: 12-3456789
+
National ID: 123456789012345
+
+
+
๐ Security Information Display
+
+
API Key: sk-1234567890abcdef1234567890abcdef1234567890abcdef
+
Access Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
+
Session ID: sess_1234567890abcdef1234567890abcdef
+
User ID: user_123456789
+
Customer ID: cust_987654321
+
+
+
๐ฅ Medical Information Display
+
+
Patient ID: PAT-123456789
+
Medical Record Number: MRN-987654321
+
Insurance Number: INS-456789123
+
Health Plan ID: HP-789123456
+
+
+
๐ผ Business Information Display
+
+
Employee ID: EMP-123456
+
Payroll Number: PY-789012
+
Department Code: DEPT-IT-001
+
Project Code: PRJ-2024-001
+
+
+
๐ฑ Contact Information Display
+
+
Emergency Contact: Jane Doe - (555) 111-2222
+
Alternate Email: jane.doe@company.com
+
Work Phone: +1-555-999-8888
+
Fax Number: +1-555-777-6666
+
+
+
๐ Network Information Display
+
+
IP Address: 192.168.1.100
+
MAC Address: 00:1B:44:11:3A:B7
+
Domain: internal.company.local
+
Server Name: SRV-PROD-001
+
+
+
๐ Mixed Content Display
+
+
Profile Summary: John Doe (john.doe@example.com) - Employee ID: EMP-123456, Phone: +1-555-123-4567
+
Transaction Details: Payment of $1,234.56 to Account: 1234567890123456, Card: 4111-****-****-1111
+
Login History: Last login: 2024-01-15 14:30:25 from IP: 192.168.1.100, Session: sess_1234567890abcdef
+
+
+
+
+
+
+
๐ Test Results
+
+
+ Status: Ready for testing
+
+
+ Extension: Check if extension is loaded and enabled
+
+
+ Console: Open DevTools Console (F12) to see detection logs
+
+
+
+
+
+
+
๐ Detection Log
+
+
๐ Waiting for extension detection...
+
๐ Open DevTools Console (F12) to see detailed logs
+
๐ฏ Look for logs starting with: ๐, ๐ฏ, ๐ง , ๐ญ, โ
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/test-ultra-early-detection.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-ultra-early-detection.html
new file mode 100644
index 000000000..f99cc3cbe
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/test-ultra-early-detection.html
@@ -0,0 +1,230 @@
+
+
+
+
+
+ Ultra-Early Detection Test
+
+
+
+
+
๐ Ultra-Early Detection Test
+
This page tests the ultra-early raw DOM analysis to ensure sensitive fields are detected before the page becomes visible to users.
+
+
+
+
+
๐ Detection Status
+
Expected Detection: 8+ sensitive fields (email, password, username, SSN, credit card, CVV, phone, address, CSRF token)
+
Timing: Raw DOM analysis should start within 50ms of page load
+
Fallback: Pattern detection should catch obvious fields if AI fails
+
+
+
+ Detection Log:
+ Waiting for extension to load and analyze...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/text-content-test.html b/sensitive_fields_ai_agent/sensitive-field-detector_1/text-content-test.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/cache.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/cache.js
new file mode 100644
index 000000000..ded8a22c8
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/cache.js
@@ -0,0 +1,353 @@
+// Multi-level caching system for performance optimization
+// Implements session, domain, global, and AI response caching
+
+console.log('๐พ cache.js loading...', window.location.href);
+
+class CacheManager {
+ constructor() {
+ this.sessionCache = new Map();
+ this.domainCache = new Map();
+ this.globalCache = new Map();
+ this.aiCache = new Map();
+
+ this.cacheSettings = {
+ session: { maxSize: 1000, ttl: 0 }, // No TTL for session
+ domain: { maxSize: 500, ttl: 7 * 24 * 60 * 60 * 1000 }, // 7 days
+ global: { maxSize: 200, ttl: 30 * 24 * 60 * 60 * 1000 }, // 30 days
+ ai: { maxSize: 100, ttl: 24 * 60 * 60 * 1000 } // 24 hours
+ };
+
+ this.initializeCache();
+ }
+
+ async initializeCache() {
+ try {
+ // Load persistent caches from storage
+ const stored = await chrome.storage.local.get(['domainCache', 'globalCache', 'aiCache']);
+
+ if (stored.domainCache) {
+ this.domainCache = new Map(Object.entries(stored.domainCache));
+ }
+ if (stored.globalCache) {
+ this.globalCache = new Map(Object.entries(stored.globalCache));
+ }
+ if (stored.aiCache) {
+ this.aiCache = new Map(Object.entries(stored.aiCache));
+ }
+
+ // Clean expired entries
+ this.cleanExpiredEntries();
+
+ // Set up periodic cleanup
+ setInterval(() => this.cleanExpiredEntries(), 60 * 60 * 1000); // Every hour
+ } catch (error) {
+ console.warn('Cache initialization failed:', error);
+ }
+ }
+
+ // Generate cache key from element metadata
+ generateElementKey(element) {
+ const keyParts = [
+ element.tag,
+ element.type,
+ element.name,
+ element.id,
+ element.placeholder,
+ element.labelText
+ ].filter(Boolean);
+
+ return this.hashString(keyParts.join('|'));
+ }
+
+ // Generate domain-specific key
+ generateDomainKey(domain, elementKey) {
+ return `${domain}:${elementKey}`;
+ }
+
+ // Simple hash function for cache keys
+ hashString(str) {
+ let hash = 0;
+ for (let i = 0; i < str.length; i++) {
+ const char = str.charCodeAt(i);
+ hash = ((hash << 5) - hash) + char;
+ hash = hash & hash; // Convert to 32-bit integer
+ }
+ return Math.abs(hash).toString(36);
+ }
+
+ // Session cache operations (non-persistent)
+ setSession(key, value) {
+ if (this.sessionCache.size >= this.cacheSettings.session.maxSize) {
+ const firstKey = this.sessionCache.keys().next().value;
+ this.sessionCache.delete(firstKey);
+ }
+
+ this.sessionCache.set(key, {
+ value,
+ timestamp: Date.now()
+ });
+ }
+
+ getSession(key) {
+ const entry = this.sessionCache.get(key);
+ return entry ? entry.value : null;
+ }
+
+ // Domain cache operations (persistent)
+ async setDomain(domain, key, value) {
+ const domainKey = this.generateDomainKey(domain, key);
+
+ if (this.domainCache.size >= this.cacheSettings.domain.maxSize) {
+ await this.evictOldestEntries(this.domainCache, 1);
+ }
+
+ this.domainCache.set(domainKey, {
+ value,
+ timestamp: Date.now(),
+ domain
+ });
+
+ await this.persistCache('domainCache');
+ }
+
+ getDomain(domain, key) {
+ const domainKey = this.generateDomainKey(domain, key);
+ const entry = this.domainCache.get(domainKey);
+
+ if (entry && this.isEntryValid(entry, this.cacheSettings.domain.ttl)) {
+ return entry.value;
+ }
+
+ if (entry) {
+ this.domainCache.delete(domainKey);
+ }
+
+ return null;
+ }
+
+ // Global cache operations (persistent)
+ async setGlobal(key, value) {
+ if (this.globalCache.size >= this.cacheSettings.global.maxSize) {
+ await this.evictOldestEntries(this.globalCache, 1);
+ }
+
+ this.globalCache.set(key, {
+ value,
+ timestamp: Date.now()
+ });
+
+ await this.persistCache('globalCache');
+ }
+
+ getGlobal(key) {
+ const entry = this.globalCache.get(key);
+
+ if (entry && this.isEntryValid(entry, this.cacheSettings.global.ttl)) {
+ return entry.value;
+ }
+
+ if (entry) {
+ this.globalCache.delete(key);
+ }
+
+ return null;
+ }
+
+ // AI response cache operations (persistent)
+ async setAI(key, value) {
+ if (this.aiCache.size >= this.cacheSettings.ai.maxSize) {
+ await this.evictOldestEntries(this.aiCache, 1);
+ }
+
+ this.aiCache.set(key, {
+ value,
+ timestamp: Date.now()
+ });
+
+ await this.persistCache('aiCache');
+ }
+
+ getAI(key) {
+ const entry = this.aiCache.get(key);
+
+ if (entry && this.isEntryValid(entry, this.cacheSettings.ai.ttl)) {
+ return entry.value;
+ }
+
+ if (entry) {
+ this.aiCache.delete(key);
+ }
+
+ return null;
+ }
+
+ // Hierarchical cache lookup
+ async get(element, domain = null) {
+ const elementKey = this.generateElementKey(element);
+
+ // 1. Check session cache first (fastest)
+ let result = this.getSession(elementKey);
+ if (result) {
+ return result;
+ }
+
+ // 2. Check domain cache if domain provided
+ if (domain) {
+ result = this.getDomain(domain, elementKey);
+ if (result) {
+ // Cache in session for faster future access
+ this.setSession(elementKey, result);
+ return result;
+ }
+ }
+
+ // 3. Check global cache
+ result = this.getGlobal(elementKey);
+ if (result) {
+ // Cache in session and domain for faster future access
+ this.setSession(elementKey, result);
+ if (domain) {
+ await this.setDomain(domain, elementKey, result);
+ }
+ return result;
+ }
+
+ return null;
+ }
+
+ // Set result in appropriate cache levels
+ async set(element, value, domain = null, level = 'session') {
+ const elementKey = this.generateElementKey(element);
+
+ // Always cache in session
+ this.setSession(elementKey, value);
+
+ // Cache in higher levels based on confidence and level parameter
+ if (level === 'domain' && domain) {
+ await this.setDomain(domain, elementKey, value);
+ } else if (level === 'global') {
+ await this.setGlobal(elementKey, value);
+ if (domain) {
+ await this.setDomain(domain, elementKey, value);
+ }
+ }
+ }
+
+ // Cache AI responses
+ async cacheAIResponse(prompt, response) {
+ const promptKey = this.hashString(prompt);
+ await this.setAI(promptKey, response);
+ }
+
+ // Get cached AI response
+ getCachedAIResponse(prompt) {
+ const promptKey = this.hashString(prompt);
+ return this.getAI(promptKey);
+ }
+
+ // Utility methods
+ isEntryValid(entry, ttl) {
+ if (ttl === 0) return true; // No expiration
+ return (Date.now() - entry.timestamp) < ttl;
+ }
+
+ async evictOldestEntries(cache, count) {
+ const entries = Array.from(cache.entries());
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
+
+ for (let i = 0; i < count && i < entries.length; i++) {
+ cache.delete(entries[i][0]);
+ }
+ }
+
+ cleanExpiredEntries() {
+ const now = Date.now();
+
+ // Clean domain cache
+ for (const [key, entry] of this.domainCache.entries()) {
+ if (!this.isEntryValid(entry, this.cacheSettings.domain.ttl)) {
+ this.domainCache.delete(key);
+ }
+ }
+
+ // Clean global cache
+ for (const [key, entry] of this.globalCache.entries()) {
+ if (!this.isEntryValid(entry, this.cacheSettings.global.ttl)) {
+ this.globalCache.delete(key);
+ }
+ }
+
+ // Clean AI cache
+ for (const [key, entry] of this.aiCache.entries()) {
+ if (!this.isEntryValid(entry, this.cacheSettings.ai.ttl)) {
+ this.aiCache.delete(key);
+ }
+ }
+ }
+
+ async persistCache(cacheType) {
+ try {
+ const cacheMap = this[cacheType];
+ const cacheObject = Object.fromEntries(cacheMap);
+ await chrome.storage.local.set({ [cacheType]: cacheObject });
+ } catch (error) {
+ console.warn(`Failed to persist ${cacheType}:`, error);
+ }
+ }
+
+ // Get cache statistics
+ getStats() {
+ return {
+ session: {
+ size: this.sessionCache.size,
+ maxSize: this.cacheSettings.session.maxSize
+ },
+ domain: {
+ size: this.domainCache.size,
+ maxSize: this.cacheSettings.domain.maxSize
+ },
+ global: {
+ size: this.globalCache.size,
+ maxSize: this.cacheSettings.global.maxSize
+ },
+ ai: {
+ size: this.aiCache.size,
+ maxSize: this.cacheSettings.ai.maxSize
+ }
+ };
+ }
+
+ // Clear all caches
+ async clearAll() {
+ this.sessionCache.clear();
+ this.domainCache.clear();
+ this.globalCache.clear();
+ this.aiCache.clear();
+
+ await chrome.storage.local.remove(['domainCache', 'globalCache', 'aiCache']);
+ }
+
+ // Clear specific cache type
+ async clear(type) {
+ switch (type) {
+ case 'session':
+ this.sessionCache.clear();
+ break;
+ case 'domain':
+ this.domainCache.clear();
+ await chrome.storage.local.remove(['domainCache']);
+ break;
+ case 'global':
+ this.globalCache.clear();
+ await chrome.storage.local.remove(['globalCache']);
+ break;
+ case 'ai':
+ this.aiCache.clear();
+ await chrome.storage.local.remove(['aiCache']);
+ break;
+ }
+ }
+}
+
+// Initialize global cache manager
+window.cacheManager = new CacheManager();
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/performance.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/performance.js
new file mode 100644
index 000000000..e02a939af
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/performance.js
@@ -0,0 +1,515 @@
+// Performance optimization utilities
+// Manages resource usage, debouncing, and efficient DOM operations
+
+class PerformanceManager {
+ constructor() {
+ this.debounceTimers = new Map();
+ this.throttleTimers = new Map();
+ this.observerPool = new Map();
+ this.workerPool = [];
+ this.metrics = {
+ domScans: 0,
+ aiRequests: 0,
+ cacheHits: 0,
+ cacheMisses: 0,
+ averageProcessingTime: 0,
+ totalProcessingTime: 0
+ };
+
+ this.settings = {
+ maxDomElements: 10000,
+ debounceDelay: 300,
+ throttleDelay: 1000,
+ maxConcurrentAI: 3,
+ maxWorkers: 2,
+ idleTimeout: 5000
+ };
+
+ this.initializeWorkers();
+ }
+
+ // Initialize Web Workers for heavy computation
+ initializeWorkers() {
+ if (typeof Worker === 'undefined') return;
+
+ try {
+ for (let i = 0; i < this.settings.maxWorkers; i++) {
+ const workerCode = this.generateWorkerCode();
+ const blob = new Blob([workerCode], { type: 'application/javascript' });
+ const worker = new Worker(URL.createObjectURL(blob));
+
+ worker.onmessage = (e) => this.handleWorkerMessage(e);
+ worker.onerror = (e) => console.warn('Worker error:', e);
+
+ this.workerPool.push({
+ worker,
+ busy: false,
+ id: i
+ });
+ }
+ } catch (error) {
+ console.warn('Failed to initialize workers:', error);
+ }
+ }
+
+ // Generate Web Worker code for DOM analysis
+ generateWorkerCode() {
+ return `
+ self.onmessage = function(e) {
+ const { type, data, id } = e.data;
+
+ try {
+ let result;
+
+ switch (type) {
+ case 'analyzePatterns':
+ result = analyzeElementPatterns(data);
+ break;
+ case 'processElements':
+ result = processElementBatch(data);
+ break;
+ default:
+ result = { error: 'Unknown task type' };
+ }
+
+ self.postMessage({ id, result, success: true });
+ } catch (error) {
+ self.postMessage({ id, error: error.message, success: false });
+ }
+ };
+
+ function analyzeElementPatterns(elements) {
+ return elements.map(element => ({
+ ...element,
+ patternScore: calculatePatternScore(element),
+ confidence: calculateConfidence(element)
+ }));
+ }
+
+ function processElementBatch(elements) {
+ return elements.filter(element =>
+ element.confidence > 0.7 || element.patternScore > 0.5
+ );
+ }
+
+ function calculatePatternScore(element) {
+ let score = 0;
+ const attributes = [element.name, element.id, element.placeholder].filter(Boolean);
+
+ const patterns = {
+ email: /email|mail/i,
+ password: /password|pwd|pass/i,
+ phone: /phone|tel|mobile/i,
+ sensitive: /ssn|credit|card|secret/i
+ };
+
+ attributes.forEach(attr => {
+ Object.values(patterns).forEach(pattern => {
+ if (pattern.test(attr)) score += 0.2;
+ });
+ });
+
+ return Math.min(score, 1);
+ }
+
+ function calculateConfidence(element) {
+ let confidence = 0;
+
+ if (element.type === 'email') confidence += 0.9;
+ if (element.type === 'password') confidence += 0.95;
+ if (element.type === 'tel') confidence += 0.8;
+
+ return confidence;
+ }
+ `;
+ }
+
+ // Handle Web Worker responses
+ handleWorkerMessage(e) {
+ const { id, result, success, error } = e.data;
+
+ if (success && this.workerCallbacks.has(id)) {
+ const callback = this.workerCallbacks.get(id);
+ callback(result);
+ this.workerCallbacks.delete(id);
+ } else if (error) {
+ console.warn('Worker task failed:', error);
+ }
+
+ // Mark worker as available
+ const worker = this.workerPool.find(w => w.id === id);
+ if (worker) worker.busy = false;
+ }
+
+ // Execute task in Web Worker
+ async executeInWorker(type, data) {
+ const availableWorker = this.workerPool.find(w => !w.busy);
+
+ if (!availableWorker) {
+ // Fallback to main thread if no workers available
+ return this.executeInMainThread(type, data);
+ }
+
+ return new Promise((resolve) => {
+ const taskId = this.generateTaskId();
+ availableWorker.busy = true;
+
+ if (!this.workerCallbacks) {
+ this.workerCallbacks = new Map();
+ }
+
+ this.workerCallbacks.set(taskId, resolve);
+
+ availableWorker.worker.postMessage({
+ type,
+ data,
+ id: taskId
+ });
+
+ // Timeout fallback
+ setTimeout(() => {
+ if (this.workerCallbacks.has(taskId)) {
+ this.workerCallbacks.delete(taskId);
+ availableWorker.busy = false;
+ resolve(this.executeInMainThread(type, data));
+ }
+ }, 5000);
+ });
+ }
+
+ // Fallback execution in main thread
+ executeInMainThread(type, data) {
+ // Simplified version of worker functions
+ switch (type) {
+ case 'analyzePatterns':
+ return data.map(element => ({
+ ...element,
+ patternScore: this.calculatePatternScore(element),
+ confidence: this.calculateConfidence(element)
+ }));
+ case 'processElements':
+ return data.filter(element =>
+ element.confidence > 0.7 || element.patternScore > 0.5
+ );
+ default:
+ return data;
+ }
+ }
+
+ // Debounced function execution
+ debounce(key, func, delay = this.settings.debounceDelay) {
+ if (this.debounceTimers.has(key)) {
+ clearTimeout(this.debounceTimers.get(key));
+ }
+
+ const timer = setTimeout(() => {
+ func();
+ this.debounceTimers.delete(key);
+ }, delay);
+
+ this.debounceTimers.set(key, timer);
+ }
+
+ // Throttled function execution
+ throttle(key, func, delay = this.settings.throttleDelay) {
+ if (this.throttleTimers.has(key)) {
+ return false; // Function is throttled
+ }
+
+ const timer = setTimeout(() => {
+ this.throttleTimers.delete(key);
+ }, delay);
+
+ this.throttleTimers.set(key, timer);
+ func();
+ return true;
+ }
+
+ // Efficient DOM querying with limits
+ efficientQuerySelector(selectors, maxResults = 1000) {
+ const startTime = performance.now();
+ const results = [];
+
+ try {
+ // Use more specific selectors first
+ const prioritizedSelectors = this.prioritizeSelectors(selectors);
+
+ for (const selector of prioritizedSelectors) {
+ if (results.length >= maxResults) break;
+
+ const elements = document.querySelectorAll(selector);
+ const remaining = maxResults - results.length;
+
+ for (let i = 0; i < Math.min(elements.length, remaining); i++) {
+ if (!results.includes(elements[i])) {
+ results.push(elements[i]);
+ }
+ }
+ }
+
+ this.recordMetric('domScanTime', performance.now() - startTime);
+ this.metrics.domScans++;
+
+ return results;
+ } catch (error) {
+ console.warn('DOM query failed:', error);
+ return [];
+ }
+ }
+
+ // Prioritize selectors by specificity and performance
+ prioritizeSelectors(selectors) {
+ const selectorMap = new Map();
+
+ selectors.forEach(selector => {
+ let priority = 0;
+
+ // Higher priority for more specific selectors
+ if (selector.includes('#')) priority += 10; // ID selector
+ if (selector.includes('[type=')) priority += 8; // Type attribute
+ if (selector.includes('[name')) priority += 6; // Name attribute
+ if (selector.includes('.')) priority += 4; // Class selector
+
+ // Lower priority for broad selectors
+ if (selector === 'div' || selector === 'span') priority -= 5;
+
+ selectorMap.set(selector, priority);
+ });
+
+ return Array.from(selectorMap.entries())
+ .sort((a, b) => b[1] - a[1])
+ .map(entry => entry[0]);
+ }
+
+ // Optimized mutation observer
+ createOptimizedObserver(callback, options = {}) {
+ const observerId = this.generateTaskId();
+
+ const defaultOptions = {
+ childList: true,
+ subtree: true,
+ attributes: false,
+ attributeOldValue: false,
+ characterData: false,
+ characterDataOldValue: false
+ };
+
+ const observerOptions = { ...defaultOptions, ...options };
+
+ // Debounced callback to prevent excessive triggering
+ const debouncedCallback = (mutations) => {
+ this.debounce(`observer_${observerId}`, () => {
+ // Filter mutations to only relevant changes
+ const relevantMutations = mutations.filter(mutation =>
+ this.isRelevantMutation(mutation)
+ );
+
+ if (relevantMutations.length > 0) {
+ callback(relevantMutations);
+ }
+ }, 200);
+ };
+
+ const observer = new MutationObserver(debouncedCallback);
+ this.observerPool.set(observerId, observer);
+
+ return { observer, observerId };
+ }
+
+ // Check if mutation is relevant for sensitive field detection
+ isRelevantMutation(mutation) {
+ if (mutation.type !== 'childList') return false;
+
+ const relevantTags = ['INPUT', 'TEXTAREA', 'SELECT', 'FORM'];
+
+ // Check added nodes
+ for (const node of mutation.addedNodes) {
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ if (relevantTags.includes(node.tagName)) return true;
+ if (node.querySelector && node.querySelector('input, textarea, select')) return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Request idle callback with fallback
+ requestIdleExecution(callback, timeout = this.settings.idleTimeout) {
+ if (typeof requestIdleCallback !== 'undefined') {
+ requestIdleCallback(callback, { timeout });
+ } else {
+ // Fallback for browsers without requestIdleCallback
+ setTimeout(callback, 16); // ~60fps
+ }
+ }
+
+ // Batch processing with size limits
+ processBatch(items, processor, batchSize = 50) {
+ const results = [];
+
+ for (let i = 0; i < items.length; i += batchSize) {
+ const batch = items.slice(i, i + batchSize);
+ const batchResults = processor(batch);
+ results.push(...batchResults);
+
+ // Yield control between batches
+ if (i + batchSize < items.length) {
+ return new Promise(resolve => {
+ this.requestIdleExecution(() => {
+ const remainingResults = this.processBatch(
+ items.slice(i + batchSize),
+ processor,
+ batchSize
+ );
+ resolve([...results, ...remainingResults]);
+ });
+ });
+ }
+ }
+
+ return results;
+ }
+
+ // Memory-efficient element processing
+ processElementsEfficiently(elements) {
+ const startTime = performance.now();
+
+ // Pre-filter elements to reduce processing load
+ const relevantElements = elements.filter(el => this.isRelevantElement(el));
+
+ if (relevantElements.length > this.settings.maxDomElements) {
+ console.warn(`Too many elements (${relevantElements.length}), limiting to ${this.settings.maxDomElements}`);
+ relevantElements.splice(this.settings.maxDomElements);
+ }
+
+ // Process in batches using workers when possible
+ const result = this.executeInWorker('processElements', relevantElements);
+
+ this.recordMetric('processingTime', performance.now() - startTime);
+
+ return result;
+ }
+
+ // Check if element is relevant for processing
+ isRelevantElement(element) {
+ const relevantTypes = ['input', 'textarea', 'select'];
+ const relevantClasses = ['form-control', 'input', 'field'];
+
+ // Tag-based relevance
+ if (relevantTypes.includes(element.tagName?.toLowerCase())) {
+ return true;
+ }
+
+ // Class-based relevance
+ if (element.className && relevantClasses.some(cls =>
+ element.className.toLowerCase().includes(cls)
+ )) {
+ return true;
+ }
+
+ // Content-based relevance for divs/spans
+ if (['div', 'span'].includes(element.tagName?.toLowerCase())) {
+ const text = element.textContent?.toLowerCase() || '';
+ const sensitiveKeywords = ['email', 'password', 'phone', 'address'];
+ return sensitiveKeywords.some(keyword => text.includes(keyword));
+ }
+
+ return false;
+ }
+
+ // Record performance metrics
+ recordMetric(metric, value) {
+ if (metric === 'processingTime') {
+ this.metrics.totalProcessingTime += value;
+ this.metrics.averageProcessingTime =
+ this.metrics.totalProcessingTime / (this.metrics.domScans + this.metrics.aiRequests);
+ }
+
+ this.metrics[metric] = value;
+ }
+
+ // Increment counter metrics
+ incrementMetric(metric) {
+ this.metrics[metric] = (this.metrics[metric] || 0) + 1;
+ }
+
+ // Get performance statistics
+ getPerformanceStats() {
+ return {
+ ...this.metrics,
+ activeDebounceTimers: this.debounceTimers.size,
+ activeThrottleTimers: this.throttleTimers.size,
+ activeObservers: this.observerPool.size,
+ availableWorkers: this.workerPool.filter(w => !w.busy).length,
+ memoryUsage: this.getMemoryUsage()
+ };
+ }
+
+ // Estimate memory usage
+ getMemoryUsage() {
+ if (performance.memory) {
+ return {
+ used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024),
+ total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024),
+ limit: Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024)
+ };
+ }
+ return null;
+ }
+
+ // Clean up resources
+ cleanup() {
+ // Clear all timers
+ this.debounceTimers.forEach(timer => clearTimeout(timer));
+ this.throttleTimers.forEach(timer => clearTimeout(timer));
+ this.debounceTimers.clear();
+ this.throttleTimers.clear();
+
+ // Disconnect observers
+ this.observerPool.forEach(observer => observer.disconnect());
+ this.observerPool.clear();
+
+ // Terminate workers
+ this.workerPool.forEach(({ worker }) => worker.terminate());
+ this.workerPool.length = 0;
+ }
+
+ // Generate unique task ID
+ generateTaskId() {
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
+ }
+
+ // Pattern scoring functions
+ calculatePatternScore(element) {
+ let score = 0;
+ const attributes = [element.name, element.id, element.placeholder].filter(Boolean);
+
+ const patterns = {
+ email: /email|mail/i,
+ password: /password|pwd|pass/i,
+ phone: /phone|tel|mobile/i,
+ sensitive: /ssn|credit|card|secret/i
+ };
+
+ attributes.forEach(attr => {
+ Object.values(patterns).forEach(pattern => {
+ if (pattern.test(attr)) score += 0.2;
+ });
+ });
+
+ return Math.min(score, 1);
+ }
+
+ calculateConfidence(element) {
+ let confidence = 0;
+
+ if (element.type === 'email') confidence += 0.9;
+ if (element.type === 'password') confidence += 0.95;
+ if (element.type === 'tel') confidence += 0.8;
+
+ return confidence;
+ }
+}
+
+// Initialize global performance manager
+window.performanceManager = new PerformanceManager();
diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/privacy.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/privacy.js
new file mode 100644
index 000000000..fc99e3c9a
--- /dev/null
+++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/utils/privacy.js
@@ -0,0 +1,351 @@
+// Privacy and data anonymization utilities
+// Ensures no user data is transmitted to external services
+
+class PrivacyManager {
+ constructor() {
+ this.sensitiveValueRegex = {
+ email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
+ phone: /^[\+]?[(]?[\d\s\-\(\)\.]{10,}$/,
+ creditCard: /^\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}$/,
+ ssn: /^\d{3}[-\s]?\d{2}[-\s]?\d{4}$/,
+ zipCode: /^\d{5}(-\d{4})?$/
+ };
+
+ this.tokenMap = new Map();
+ this.hashSalt = this.generateSalt();
+ }
+
+ // Generate a random salt for hashing
+ generateSalt() {
+ const array = new Uint8Array(16);
+ crypto.getRandomValues(array);
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
+ }
+
+ // Anonymize element metadata before sending to AI
+ anonymizeElement(element) {
+ const anonymized = {
+ index: element.index,
+ tag: element.tag,
+ type: element.type || '',
+ structure: this.generateStructureFingerprint(element),
+ context: this.extractSafeContext(element),
+ patterns: this.extractPatterns(element),
+ visual: this.extractVisualCues(element)
+ };
+
+ // Add anonymized attribute information
+ if (element.name) {
+ anonymized.namePattern = this.anonymizeAttribute('name', element.name);
+ }
+ if (element.id) {
+ anonymized.idPattern = this.anonymizeAttribute('id', element.id);
+ }
+ if (element.placeholder) {
+ anonymized.placeholderPattern = this.anonymizeAttribute('placeholder', element.placeholder);
+ }
+ if (element.labelText) {
+ anonymized.labelPattern = this.anonymizeAttribute('label', element.labelText);
+ }
+ if (element.ariaLabel) {
+ anonymized.ariaPattern = this.anonymizeAttribute('aria', element.ariaLabel);
+ }
+
+ return anonymized;
+ }
+
+ // Anonymize attribute values while preserving meaningful patterns
+ anonymizeAttribute(type, value) {
+ if (!value) return '';
+
+ // Convert to lowercase for pattern matching
+ const lowerValue = value.toLowerCase();
+
+ // Check for sensitive patterns and replace with tokens
+ const patterns = {
+ email: ['email', 'mail', 'e-mail', '@'],
+ password: ['password', 'pwd', 'pass', 'secret', 'pin'],
+ phone: ['phone', 'tel', 'mobile', 'cell'],
+ address: ['address', 'street', 'addr', 'location'],
+ name: ['name', 'first', 'last', 'given', 'family'],
+ date: ['date', 'birth', 'dob', 'birthday'],
+ credit: ['credit', 'card', 'cc', 'payment'],
+ ssn: ['ssn', 'social', 'security', 'tax'],
+ number: /\d+/g
+ };
+
+ let anonymizedValue = lowerValue;
+
+ // Replace sensitive keywords with tokens
+ for (const [category, keywords] of Object.entries(patterns)) {
+ if (Array.isArray(keywords)) {
+ keywords.forEach(keyword => {
+ if (anonymizedValue.includes(keyword)) {
+ anonymizedValue = anonymizedValue.replace(
+ new RegExp(keyword, 'gi'),
+ `${category.toUpperCase()}_TOKEN`
+ );
+ }
+ });
+ } else if (keywords instanceof RegExp) {
+ anonymizedValue = anonymizedValue.replace(keywords, 'NUMBER_TOKEN');
+ }
+ }
+
+ // Preserve structure indicators
+ anonymizedValue = this.preserveStructure(anonymizedValue);
+
+ return anonymizedValue;
+ }
+
+ // Preserve structural information while anonymizing
+ preserveStructure(value) {
+ // Preserve common separators and patterns
+ const structurePreservation = {
+ '_': 'UNDERSCORE',
+ '-': 'DASH',
+ '.': 'DOT',
+ '@': 'AT_SYMBOL',
+ '#': 'HASH',
+ '$': 'DOLLAR',
+ '%': 'PERCENT'
+ };
+
+ let preserved = value;
+ for (const [char, token] of Object.entries(structurePreservation)) {
+ preserved = preserved.replace(new RegExp(`\\${char}`, 'g'), token);
+ }
+
+ return preserved;
+ }
+
+ // Generate a structural fingerprint without exposing sensitive data
+ generateStructureFingerprint(element) {
+ const features = {
+ hasName: !!element.name,
+ hasId: !!element.id,
+ hasPlaceholder: !!element.placeholder,
+ hasLabel: !!element.labelText,
+ hasAriaLabel: !!element.ariaLabel,
+ hasClass: !!element.className,
+ hasAutocomplete: !!element.autocomplete,
+ isRequired: !!element.required,
+ isDisabled: !!element.disabled,
+ isReadonly: !!element.readonly,
+ tagType: element.tag?.toLowerCase(),
+ inputType: element.type?.toLowerCase()
+ };
+
+ // Create a hash of the feature set
+ return this.hashObject(features);
+ }
+
+ // Extract safe contextual information
+ extractSafeContext(element) {
+ const context = {
+ formContext: this.getFormContext(element),
+ positionContext: this.getPositionContext(element),
+ semanticContext: this.getSemanticContext(element)
+ };
+
+ return context;
+ }
+
+ // Get form-level context without sensitive data
+ getFormContext(element) {
+ const form = element.form;
+ if (!form) return null;
+
+ return {
+ formElements: form.elements?.length || 0,
+ formMethod: form.method || '',
+ formAction: this.anonymizeUrl(form.action || ''),
+ formId: form.id ? this.hashString(form.id) : null
+ };
+ }
+
+ // Get positional context
+ getPositionContext(element) {
+ // This would be populated by the content script with DOM position info
+ return element.positionInfo || null;
+ }
+
+ // Extract semantic context from surrounding elements
+ getSemanticContext(element) {
+ // This would analyze nearby text, headings, etc. for context
+ return element.semanticInfo || null;
+ }
+
+ // Extract visual patterns and cues
+ extractPatterns(element) {
+ const patterns = {
+ hasEmailPattern: this.testPattern(element, 'email'),
+ hasPhonePattern: this.testPattern(element, 'phone'),
+ hasPasswordPattern: this.testPattern(element, 'password'),
+ hasAddressPattern: this.testPattern(element, 'address'),
+ hasNamePattern: this.testPattern(element, 'name'),
+ hasCreditCardPattern: this.testPattern(element, 'creditCard'),
+ hasSSNPattern: this.testPattern(element, 'ssn')
+ };
+
+ return patterns;
+ }
+
+ // Test for specific patterns in element attributes
+ testPattern(element, patternType) {
+ const patterns = window.SENSITIVE_PATTERNS;
+ if (!patterns) return false;
+
+ const testFields = [
+ element.name,
+ element.id,
+ element.placeholder,
+ element.labelText,
+ element.ariaLabel
+ ].filter(Boolean);
+
+ const patternRegex = patterns.NAME_PATTERNS[patternType] ||
+ patterns.PLACEHOLDER_PATTERNS[patternType] ||
+ patterns.LABEL_PATTERNS[patternType];
+
+ if (!patternRegex) return false;
+
+ return testFields.some(field => patternRegex.test(field));
+ }
+
+ // Extract visual cues without sensitive data
+ extractVisualCues(element) {
+ return {
+ hasPasswordToggle: this.hasVisualIndicator(element, 'passwordToggle'),
+ hasRequiredIndicator: this.hasVisualIndicator(element, 'requiredField'),
+ hasSecurityIcon: this.hasVisualIndicator(element, 'securityIcon'),
+ hasEmailIcon: this.hasVisualIndicator(element, 'emailIcon'),
+ inputMasking: !!element.inputMasking,
+ maxLength: element.maxLength || null,
+ minLength: element.minLength || null
+ };
+ }
+
+ // Check for visual indicators
+ hasVisualIndicator(element, indicatorType) {
+ const indicators = window.SENSITIVE_PATTERNS?.VISUAL_INDICATORS?.[indicatorType] || [];
+ const classNames = element.className || '';
+
+ return indicators.some(indicator =>
+ classNames.toLowerCase().includes(indicator.toLowerCase())
+ );
+ }
+
+ // Anonymize URLs while preserving structure
+ anonymizeUrl(url) {
+ if (!url) return '';
+
+ try {
+ const urlObj = new URL(url);
+ return {
+ protocol: urlObj.protocol,
+ hostname: this.hashString(urlObj.hostname),
+ pathname: this.anonymizePath(urlObj.pathname),
+ hasQuery: !!urlObj.search,
+ hasFragment: !!urlObj.hash
+ };
+ } catch {
+ return { invalidUrl: true };
+ }
+ }
+
+ // Anonymize URL paths
+ anonymizePath(path) {
+ if (!path) return '';
+
+ const segments = path.split('/').filter(Boolean);
+ return segments.map(segment => {
+ if (segment.match(/^\d+$/)) return 'ID_SEGMENT';
+ if (segment.length > 10) return 'LONG_SEGMENT';
+ return this.hashString(segment);
+ }).join('/');
+ }
+
+ // Hash any object to create consistent fingerprints
+ hashObject(obj) {
+ const str = JSON.stringify(obj, Object.keys(obj).sort());
+ return this.hashString(str);
+ }
+
+ // Simple hash function
+ hashString(str) {
+ if (!str) return '';
+
+ let hash = 0;
+ const saltedStr = str + this.hashSalt;
+
+ for (let i = 0; i < saltedStr.length; i++) {
+ const char = saltedStr.charCodeAt(i);
+ hash = ((hash << 5) - hash) + char;
+ hash = hash & hash; // Convert to 32-bit integer
+ }
+
+ return Math.abs(hash).toString(36);
+ }
+
+ // Validate that no sensitive data is in the anonymized object
+ validateAnonymization(anonymizedData) {
+ const dataStr = JSON.stringify(anonymizedData).toLowerCase();
+
+ // Check for potential PII patterns
+ const sensitivePatterns = [
+ /\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b/, // Email
+ /\b\d{3}[-.]?\d{2}[-.]?\d{4}\b/, // SSN
+ /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/, // Credit card
+ /\b\d{10,}\b/ // Long numbers
+ ];
+
+ for (const pattern of sensitivePatterns) {
+ if (pattern.test(dataStr)) {
+ console.warn('Potential sensitive data detected in anonymized object');
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Create a privacy-safe batch of elements for AI analysis
+ createSafeBatch(elements) {
+ const anonymizedBatch = elements.map(element => this.anonymizeElement(element));
+
+ // Validate the entire batch
+ const isValid = anonymizedBatch.every(item => this.validateAnonymization(item));
+
+ if (!isValid) {
+ throw new Error('Anonymization validation failed');
+ }
+
+ return {
+ elements: anonymizedBatch,
+ batchId: this.generateBatchId(),
+ timestamp: Date.now(),
+ privacyVersion: '1.0'
+ };
+ }
+
+ // Generate unique batch ID for tracking
+ generateBatchId() {
+ return crypto.randomUUID();
+ }
+
+ // Get privacy compliance report
+ getPrivacyReport() {
+ return {
+ dataMinimization: 'Implemented',
+ anonymization: 'Active',
+ noUserValues: 'Enforced',
+ encryption: 'Local storage only',
+ retention: 'Session-based with configurable TTL',
+ compliance: ['GDPR Article 25', 'CCPA Section 1798.100']
+ };
+ }
+}
+
+// Initialize global privacy manager
+window.privacyManager = new PrivacyManager();