diff --git a/sensitive_fields_ai_agent/.DS_Store b/sensitive_fields_ai_agent/.DS_Store new file mode 100644 index 000000000..d5d886723 Binary files /dev/null and b/sensitive_fields_ai_agent/.DS_Store differ diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1.crx b/sensitive_fields_ai_agent/sensitive-field-detector_1.crx new file mode 100644 index 000000000..d435b1815 Binary files /dev/null and b/sensitive_fields_ai_agent/sensitive-field-detector_1.crx differ diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1.pem b/sensitive_fields_ai_agent/sensitive-field-detector_1.pem new file mode 100644 index 000000000..1f23cf8db --- /dev/null +++ b/sensitive_fields_ai_agent/sensitive-field-detector_1.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCt72F2qUi6vFvQ +U79vcKmSLIBJOtojd0iHyNUMnHg3scg996MWZrNC5dr7iIRkbemY4M5v1aCB5s0z +CGKx+Q9DSoCxmzx9/IddyuEy27e6c37tOYFxB0pCG67ZIS8r9M6LZdemyXmdZ7sz +0MiekkCkQ5iU7J6OYSWnMG3wVqRSBpRCTmUtbWP+wAnytcQh3JUBp/trIxeVxdV4 +G4SXRXcnJnwLvnSZvixOQV5Lq2NwHAmLe5FOmuZ9p37G5OVkwqynaArnx2HH9HLh +UWMF0rRhfgUm8m0vNWxGynqFqGJ8lu1AtW6qEd/vF7v/8VXohLhLVTsLW4rA2AkO +zERqXW7tAgMBAAECggEABEBJGKY/4pefEYTeCrXmNjVHWBES1ZW9pzBjL1EOXyNw +WJNhUfAtGJAloSAzRtZv/WOr4OGkNxbLPueec7Byqbe04ttGq6oQZOp10woruL2P +PmQrMi8gw0eJ7nn4RErD5SDA+61CuB8LlgwtMGvfHevJBgV0RUolOI8YPTsZxXzA +eVig7tBdjGR8kAW3rJBYxtsr2sRrmuYZgOFnp4sKHbp7EC81+QQyZnmKpBpEBRNZ +Pq4kJurdiHpI8/Sd8SxT+lwW2MW5s+cABt7luZI6y7NOzPa1Ft5IzUsorRZfDOwd +GL7chk9yV+G+jRQsmx4rBVQhjJHx6Rc9WelC0gxyAQKBgQDUNDdRauTRXTmR9ZPM +nuLSnykKwtyQ7TxPtY1f6Ek646aTb6OpHLxv0M32NyW2Aj4HSWfO9cww1DXEf2Kx +C84RYvw+k6QNx1vhh1TCaPuQWzZS7DZS6SOX32VVA9tVjrYTXof6WvEdLEgDe+N/ +KaOMI+6NetnxYwwfCRQHQ8ZgJQKBgQDR1TrweZbK1p3BtbxweFFFEb2VWvLTe19W +DBsgsj+Xxb+RqqhxhnZksYbVRypPfOKKCBIrhLhnj3m9HiFMJCvBsTtNrsExOHT9 +8eIt5E5YWdU4ArvGRmIBuAXclrHSuYTlm8qWbixymhiQ7Qe+tTO5qNCx/5xEEZu4 +HaqOBlEVKQKBgQC0ckaL/FrhUeXvoSVyfbWhpugul28Z6jI8EcVLRepnokf23ZsM +5juy1ZsegNyXfiai6Z/VlJxy9TFXi38v0DsF++dFiySY9urBx+OigcVRhs6WCvlS +3Z9/uN6hQWtUwMtBg0Qb9xNLXGlMVSAp9Q120OM8+be0lSYsifq3BsH85QKBgCcP +srEhcVjBKwzsb+/f9gENlGVBUQLdbJnwFvy3Me9m+Ip9Ym3/wfpssdTyMoYpkWJw +rjE7u7hLlTYHbdtmwF6GR6vmZEBtahn79UblwU/hKTgMCjiW0ZfQUgcGZoP71SmJ +HuVyDQQoe3o/JbnysXZWXsND+isRvBNCMoXN1zpBAoGAJeUwB4/lEw22oDxrlx+t +wlVE4TqLSqHL8ZrnBbcuklCm/zEDCMWypu5rRLMNUpOtU+mI+66It/upD4OGH8EI +zozYpsbW+Fl60XyCXRlZV0GJQZYiMA98CUjsPs0UjjyFIoV3+dw1SwNEuRjtWkq9 +wd+No2zCgNUI7SggrxF9CrQ= +-----END PRIVATE KEY----- diff --git a/sensitive_fields_ai_agent/sensitive-field-detector_1/background-simple.js b/sensitive_fields_ai_agent/sensitive-field-detector_1/background-simple.js new file mode 100644 index 000000000..56ce536cc --- /dev/null +++ b/sensitive_fields_ai_agent/sensitive-field-detector_1/background-simple.js @@ -0,0 +1,2560 @@ +// Enhanced Background Script with AI Integration +console.log('๐Ÿ”ง Background script starting...'); +console.log('๐Ÿ†” Extension ID:', chrome.runtime.id); + +// Embedded AI configuration - Updated to latest Gemini API endpoint +const GEMINI_API_KEY = 'AIzaSyCoNFODrVovsQEFa4nseHbv0d56eMqhtDU'; +const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent'; + +// Track processed elements to avoid duplicates +const processedElements = new Set(); + +// Initialize extension state on startup +chrome.runtime.onStartup.addListener(() => { + console.log('๐Ÿš€ Extension startup detected'); + initializeExtensionState(); +}); + +chrome.runtime.onInstalled.addListener(() => { + console.log('๐ŸŽฏ Extension installed'); + initializeExtensionState(); +}); + +function initializeExtensionState() { + // Ensure default state exists + chrome.storage.sync.get(['extensionEnabled'], (result) => { + if (result.extensionEnabled === undefined) { + console.log('๐Ÿ”ง No existing state found, setting default to enabled'); + chrome.storage.sync.set({ extensionEnabled: true }, () => { + console.log('โœ… Default state set to enabled'); + }); + } else { + console.log('โœ… Existing state found:', result.extensionEnabled); + } + }); +} + +// Handle messages from popup and content scripts +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + console.log('๐Ÿ“จ Background received message:', message); + console.log('๐Ÿ‘ค Message sender:', sender); + + if (message.action === 'getState') { + console.log('๐Ÿ” Processing getState request...'); + chrome.storage.sync.get(['extensionEnabled'], (result) => { + const enabled = result.extensionEnabled !== undefined ? result.extensionEnabled : true; + console.log('๐Ÿ“ค Sending state response:', { enabled }); + sendResponse({ enabled }); + }); + return true; // Keep message channel open + } + + if (message.action === 'setState') { + console.log('๐Ÿ”ง Processing setState request, enabled:', message.enabled); + chrome.storage.sync.set({ extensionEnabled: message.enabled }, () => { + if (chrome.runtime.lastError) { + console.error('โŒ Error saving state:', chrome.runtime.lastError); + sendResponse({ success: false, error: chrome.runtime.lastError.message }); + } else { + console.log('โœ… Extension state saved:', message.enabled); + sendResponse({ success: true }); + } + }); + return true; + } + + if (message.action === 'analyzeElements') { + handleAIAnalysis(message.elements, sendResponse); + return true; // Keep message channel open for async response + } + + if (message.action === 'analyzeDOMStructure') { + handleDOMAnalysis(message, sendResponse); + return true; // Keep message channel open for async response + } + + if (message.action === 'analyzeViewportDOM') { + handleViewportDOMAnalysis(message, sendResponse); + return true; // Keep message channel open for async response + } + + if (message.action === 'analyzeBatchDOM') { + handleBatchDOMAnalysis(message, sendResponse); + return true; // Keep message channel open for async response + } + + if (message.action === 'analyzeCompleteDOM') { + handleCompleteDOMAnalysis(message, sendResponse); + return true; // Keep message channel open for async response + } + + if (message.action === 'testAPIKey') { + testGeminiAPI(sendResponse); + return true; + } + + if (message.action === 'analyzeCompletePageContent') { + console.log('๐Ÿค– Complete page content analysis requested'); + handleCompletePageAnalysis(message.data, sendResponse); + return true; + } + + if (message.action === 'analyzeRawDOM') { + console.log('๐Ÿ” Processing analyzeRawDOM request...'); + analyzeRawDOMWithGemini(message.rawDOM, message.pageInfo) + .then(result => { + console.log('โœ… Raw DOM analysis completed, sending response'); + + // Extract selectors from AI response and send to browser for masking + if (result.success && result.sensitiveFields && result.sensitiveFields.length > 0) { + const selectors = result.sensitiveFields.map(field => field.selector); + console.log('๐ŸŽฏ Extracted selectors for masking:', selectors); + console.log('๐Ÿ“Š Summary: Extracted', selectors.length, 'selectors from', result.sensitiveFields.length, 'AI-detected sensitive fields'); + + // Send selectors directly to browser using the maskSensitiveElements API + try { + console.log('๐Ÿ” Debugging tab ID - message.sender:', message.sender); + console.log('๐Ÿ” Debugging tab ID - sender:', sender); + + // Get the tab ID from the sender or use the active tab + const tabId = message.sender?.tab?.id || sender?.tab?.id; + console.log('๐Ÿ” Extracted tab ID:', tabId); + + if (!tabId) { + console.warn('โš ๏ธ No tab ID available, trying to get active tab...'); + // Get the active tab as fallback + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + console.log('๐Ÿ” Found tabs:', tabs); + if (tabs && tabs[0]) { + console.log('โœ… Using active tab ID:', tabs[0].id); + executeMaskingScript(tabs[0].id, selectors, 'legacy-detection'); + } else { + console.error('โŒ Could not find active tab for masking'); + } + }); + } else { + console.log('โœ… Using sender tab ID:', tabId); + executeMaskingScript(tabId, selectors, 'legacy-detection'); + } + } catch (error) { + console.warn('โš ๏ธ Error executing masking script:', error); + } + } else { + console.log('โ„น๏ธ No sensitive fields detected, no masking required'); + } + + sendResponse(result); + }) + .catch(error => { + console.error('โŒ Raw DOM analysis failed:', error); + sendResponse({ + success: false, + error: error.message, + sensitiveFields: [] + }); + }); + return true; // Keep message channel open + } +}); + +async function handleCompletePageAnalysis(analysisPayload, sendResponse) { + const analysisStartTime = Date.now(); + console.log('๐Ÿค– Starting complete page content analysis for sensitive information detection'); + + try { + const { pageInfo, completeDOM, detectedElements } = analysisPayload; + + console.log('๐Ÿ“Š Analysis payload:', { + url: pageInfo.url, + domElements: completeDOM.length, + preDetectedElements: detectedElements.length + }); + + // Create comprehensive prompt for Gemini + const analysisPrompt = createCompletePageAnalysisPrompt(pageInfo, completeDOM, detectedElements); + + console.log('๐Ÿ”ค Sending analysis prompt to Gemini (length:', analysisPrompt.length, 'chars)'); + + // Send to Gemini for analysis + const geminiResponse = await callGeminiAPI(analysisPrompt); + + if (geminiResponse && geminiResponse.success) { + console.log('โœ… Gemini analysis completed successfully'); + + // Parse Gemini response for detected sensitive elements + const aiDetectedElements = parseGeminiSensitiveContentResponse(geminiResponse.data, completeDOM); + + console.log(`๐Ÿง  AI detected ${aiDetectedElements.length} additional sensitive elements`, { + analysisTime: Date.now() - analysisStartTime + 'ms' + }); + + sendResponse({ + success: true, + detectedElements: aiDetectedElements, + newDetections: aiDetectedElements.length, + enhancedClassifications: detectedElements.length, + analysisTime: Date.now() - analysisStartTime + }); + + } else { + console.warn('โš ๏ธ Gemini analysis failed'); + sendResponse({ + success: false, + error: 'Gemini analysis failed', + detectedElements: [] + }); + } + + } catch (error) { + console.error('โŒ Error in complete page analysis:', error); + sendResponse({ + success: false, + error: error.message, + detectedElements: [] + }); + } +} + +function createCompletePageAnalysisPrompt(pageInfo, domElements, detectedElements) { + const prompt = ` +SENSITIVE INFORMATION DETECTION AND CLASSIFICATION + +PAGE CONTEXT: +- URL: ${pageInfo.url} +- Title: ${pageInfo.title} +- Analysis Type: Complete page visible text content classification + +TASK: Analyze the complete page DOM and classify ALL visible text content that contains sensitive information. + +DETECTION CATEGORIES: +1. PERSONAL_INFO: email addresses, phone numbers, names, addresses +2. FINANCIAL: credit card numbers, account numbers, routing numbers, monetary amounts +3. IDENTIFICATION: SSN, tax IDs, passport numbers, license numbers +4. TECHNICAL: IP addresses, API keys, tokens, credentials +5. HEALTHCARE: patient IDs, medical record numbers, insurance numbers +6. BUSINESS: employee IDs, internal codes, proprietary information + +PRE-DETECTED ELEMENTS (already found by pattern matching): +${detectedElements.map(el => `- ${el.tagName}: "${el.textContent.substring(0, 100)}" [${el.sensitiveType}]`).join('\n')} + +COMPLETE DOM ELEMENTS TO ANALYZE: +${domElements.slice(0, 50).map(el => ` +Element: ${el.tagName}${el.id ? '#' + el.id : ''}${el.className ? '.' + el.className.split(' ')[0] : ''} +Text: "${el.textContent}" +Selector: ${el.selector} +`).join('\n')} + +INSTRUCTIONS: +1. Examine ALL visible text content in the DOM elements +2. Identify ANY sensitive information not already detected +3. Classify each finding with confidence level (0.0-1.0) +4. Provide CSS selector for precise element identification +5. Explain reasoning for each classification + +RESPONSE FORMAT (JSON only): +{ + "detectedElements": [ + { + "selector": "css-selector-here", + "type": "email|phone|credit-card|ssn|ip-address|etc", + "category": "PERSONAL_INFO|FINANCIAL|IDENTIFICATION|TECHNICAL|HEALTHCARE|BUSINESS", + "confidence": 0.95, + "reasoning": "Brief explanation of why this is sensitive", + "sensitivity": "high|medium|low", + "textContent": "relevant portion of text content", + "detectedValue": "the actual sensitive value found" + } + ] +} + +Focus on finding sensitive information displayed as text content (not input fields). Look for account details, personal information, financial data, etc. that users might see on banking sites, profile pages, or account dashboards. +`; + + return prompt; +} + +function parseGeminiSensitiveContentResponse(responseData, domElements) { + try { + console.log('๐Ÿ” Parsing Gemini response for sensitive content detections'); + + // Extract JSON from Gemini response + let jsonContent = responseData; + if (typeof responseData === 'string') { + const jsonMatch = responseData.match(/\{[\s\S]*\}/); + if (jsonMatch) { + jsonContent = JSON.parse(jsonMatch[0]); + } else { + console.warn('โš ๏ธ No JSON found in Gemini response'); + return []; + } + } + + const detectedElements = jsonContent.detectedElements || []; + console.log(`๐Ÿง  Parsed ${detectedElements.length} AI-detected sensitive elements`); + + // Validate and enhance each detection + const validDetections = detectedElements.filter(element => { + return element.selector && + element.type && + element.category && + element.confidence && + element.confidence >= 0.6; // Minimum confidence threshold + }).map(element => ({ + ...element, + aiModelUsed: 'gemini-1.5-flash', + detectionMethod: 'ai-complete-page-analysis', + timestamp: new Date().toISOString() + })); + + console.log(`โœ… ${validDetections.length} valid AI detections ready for processing`); + return validDetections; + + } catch (error) { + console.error('โŒ Error parsing Gemini response:', error); + return []; + } +} + +async function handleAIAnalysis(elements, sendResponse) { + try { + console.log('๐Ÿค– AI Analysis requested for', elements.length, 'elements'); + + // Filter out already processed elements + const newElements = elements.filter(element => { + const elementKey = generateElementHash(element); + if (processedElements.has(elementKey)) { + return false; + } + processedElements.add(elementKey); + return true; + }); + + if (newElements.length === 0) { + console.log('โญ๏ธ No new elements to analyze'); + sendResponse({ success: true, results: [], newElementsCount: 0 }); + return; + } + + console.log('๐ŸŽฏ Analyzing', newElements.length, 'new elements'); + + // Prepare minimal data for AI + const elementsForAI = newElements.map(element => ({ + type: element.type, + name: element.name || '', + id: element.id || '', + placeholder: element.placeholder || '', + autocomplete: element.autocomplete || '', + className: element.className || '' + })); + + const aiResults = await callGeminiAPI(elementsForAI); + + sendResponse({ + success: true, + results: aiResults, + newElementsCount: newElements.length, + totalProcessed: processedElements.size + }); + + } catch (error) { + console.error('โŒ AI Analysis failed:', error); + sendResponse({ success: false, error: error.message }); + } +} + +async function handleViewportDOMAnalysis(message, sendResponse) { + try { + const startTime = Date.now(); + console.log('๐Ÿ‘๏ธ Viewport DOM Analysis requested'); + console.log('๐Ÿ“ Viewport:', `${message.viewport.width}x${message.viewport.height} at (${message.viewport.scrollX}, ${message.viewport.scrollY})`); + console.log('๐Ÿ“„ Visible DOM size:', Math.round(message.visibleDOM.length / 1024), 'KB'); + + // Create viewport hash for caching + const viewportHash = generateViewportHash(message.viewport, message.visibleDOM); + + if (processedElements.has(viewportHash)) { + console.log('โญ๏ธ Similar viewport already analyzed, skipping'); + sendResponse({ success: true, results: [], processingTime: 0, cached: true }); + return; + } + + // Mark this viewport as processed + processedElements.add(viewportHash); + + const aiResults = await callGeminiForViewport(message); + const processingTime = Date.now() - startTime; + + sendResponse({ + success: true, + results: aiResults, + processingTime: processingTime, + viewportSize: `${message.viewport.width}x${message.viewport.height}`, + domSize: message.visibleDOM.length, + scrollPosition: `${message.viewport.scrollX}, ${message.viewport.scrollY}` + }); + + } catch (error) { + console.error('โŒ Viewport DOM Analysis failed:', error); + sendResponse({ success: false, error: error.message }); + } +} + +async function callGeminiForViewport(message) { + const prompt = createViewportAnalysisPrompt(message); + + const requestBody = { + contents: [{ + parts: [{ + text: prompt + }] + }], + generationConfig: { + temperature: 0.1, + topK: 1, + topP: 0.8, + maxOutputTokens: 2048, + }, + 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" + } + ] + }; + + console.log('๐Ÿš€ Sending viewport DOM to Gemini AI for classification...'); + + try { + const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }); + + if (!response.ok) { + const errorText = await response.text(); + console.error('โŒ Gemini API Error:', response.status, errorText); + + // Try alternative API endpoint if main fails + if (response.status === 404) { + console.log('๐Ÿ”„ Trying alternative Gemini endpoint...'); + return await callGeminiAlternativeEndpoint(requestBody, 'viewport'); + } + + throw new Error(`API request failed: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + console.log('โœ… Gemini AI viewport classification complete'); + + return parseViewportAnalysisResponse(data); + + } catch (error) { + console.error('โŒ AI Classification failed:', error); + + // Fallback to pattern-based classification + console.log('๐Ÿ”„ Falling back to enhanced pattern-based classification...'); + return performFallbackPatternClassification(message.visibleDOM); + } +} + +function createViewportAnalysisPrompt(message) { + return `You are an expert in web security and sensitive data classification. Analyze this visible webpage section and identify ALL form fields that could collect SENSITIVE user data. + +CONTEXT: +- URL: ${message.pageURL} +- Page Title: ${message.pageTitle} +- Viewport: ${message.viewport.width}x${message.viewport.height} + +VISIBLE DOM CONTENT: +${message.visibleDOM} + +CLASSIFICATION TASK: +Identify form fields (input, textarea, select) that could contain sensitive data: + +๐Ÿ”ด HIGH SENSITIVITY (Critical Security Risk): +- Passwords, PINs, security codes, OTP/2FA tokens +- Credit card numbers, CVV codes, expiration dates +- Social Security Numbers, Tax IDs, passport numbers, license numbers +- Bank account numbers, routing numbers, IBAN +- Biometric data, security questions/answers + +๐ŸŸก MEDIUM SENSITIVITY (Personal Data): +- Email addresses, usernames, login IDs +- Full names, first name, last name +- Phone numbers, mobile numbers +- Home addresses, postal codes, ZIP codes +- Date of birth, age + +๐ŸŸข LOW SENSITIVITY (General Information): +- Company names, job titles +- Preferences, settings, non-personal choices +- Public information, general comments + +ANALYSIS CRITERIA: +1. Examine field attributes: name, id, placeholder, type, autocomplete +2. Look at surrounding labels and context +3. Consider form purpose and page type +4. Check for password/security patterns +5. Identify financial/payment fields +6. Detect personal identification fields + +RESPONSE FORMAT - Valid JSON Array Only: +[ + { + "selector": "unique CSS selector (id, name, or position-based)", + "type": "input type (password, email, text, tel, etc.)", + "sensitivity": "high|medium|low", + "category": "password|financial|personal|contact|security|identification", + "reason": "specific reason why this field is sensitive", + "confidence": "0.95 for obvious fields, 0.8 for contextual, 0.6 for uncertain", + "fieldName": "name attribute value", + "fieldId": "id attribute value", + "placeholder": "placeholder text", + "context": "surrounding label or form context" + } +] + +If no sensitive fields found: [] + +IMPORTANT: Only classify fields that actually exist in the DOM and truly handle sensitive data. Be specific with selectors.`; +} + +function parseViewportAnalysisResponse(aiData) { + try { + const responseText = aiData.candidates?.[0]?.content?.parts?.[0]?.text || ''; + console.log('๐Ÿ” Viewport Analysis Response:', responseText.substring(0, 300) + '...'); + + // Extract JSON array from response + const jsonMatch = responseText.match(/\[[\s\S]*\]/); + if (!jsonMatch) { + console.warn('โš ๏ธ Could not parse viewport analysis response as JSON'); + return []; + } + + const sensitiveFields = JSON.parse(jsonMatch[0]); + console.log('๐ŸŽฏ AI identified', sensitiveFields.length, 'sensitive fields in viewport'); + + // Validate and process results + return sensitiveFields.filter(field => { + return field.selector && field.type && field.category && field.reason; + }).map(field => ({ + ...field, + confidence: field.sensitivity === 'high' ? 0.9 : field.sensitivity === 'medium' ? 0.7 : 0.5, + detectionMethod: 'AI-Viewport-Analysis' + })); + + } catch (error) { + console.error('โŒ Failed to parse viewport analysis response:', error); + return []; + } +} + +function generateViewportHash(viewport, visibleDOM) { + // Create hash based on viewport position and visible content + const position = `${Math.round(viewport.scrollX / 200)}_${Math.round(viewport.scrollY / 200)}`; + const contentSample = visibleDOM.replace(/\s+/g, '').substring(0, 500); + return btoa(`${position}_${contentSample}`).slice(0, 20); +} + +async function handleBatchDOMAnalysis(message, sendResponse) { + try { + const startTime = Date.now(); + console.log('๐Ÿ“ฆ Batch DOM Analysis requested'); + console.log('๐Ÿ“„ Total DOM size:', Math.round(message.fullDOM.length / 1024), 'KB'); + + // Check if DOM is too large and needs batching + const maxBatchSize = 40000; // 40KB per batch + const domBatches = []; + + if (message.fullDOM.length > maxBatchSize) { + console.log('๐Ÿ”„ DOM too large, splitting into batches...'); + domBatches.push(...splitDOMIntoBatches(message.fullDOM, maxBatchSize)); + } else { + domBatches.push(message.fullDOM); + } + + console.log('๐Ÿ“ฆ Processing', domBatches.length, 'DOM batches'); + + // Process all batches in parallel for speed + const batchPromises = domBatches.map((batch, index) => + processDOMBatch(batch, index + 1, message) + ); + + const batchResults = await Promise.all(batchPromises); + + // Combine results from all batches + const allSensitiveFields = []; + batchResults.forEach(result => { + if (result && result.length > 0) { + allSensitiveFields.push(...result); + } + }); + + // Remove duplicates based on selector + const uniqueFields = removeDuplicateFields(allSensitiveFields); + + const processingTime = Date.now() - startTime; + + console.log('โœ… Batch analysis complete:', { + totalBatches: domBatches.length, + totalSensitiveFields: uniqueFields.length, + processingTime: processingTime + 'ms' + }); + + sendResponse({ + success: true, + results: uniqueFields, + processingTime: processingTime, + batchCount: domBatches.length, + domSize: message.fullDOM.length, + method: 'batch-analysis' + }); + + } catch (error) { + console.error('โŒ Batch DOM Analysis failed:', error); + sendResponse({ success: false, error: error.message }); + } +} + +function splitDOMIntoBatches(fullDOM, maxSize) { + const batches = []; + + // Try to split at logical boundaries (forms, sections) + const logicalSplits = [ + /<\/form>/gi, + /<\/section>/gi, + /<\/div[^>]*class[^>]*form[^>]*>/gi, + /<\/fieldset>/gi + ]; + + let remainingDOM = fullDOM; + + while (remainingDOM.length > maxSize) { + let splitPoint = maxSize; + + // Try to find a logical split point before maxSize + for (const splitRegex of logicalSplits) { + const matches = [...remainingDOM.substring(0, maxSize).matchAll(splitRegex)]; + if (matches.length > 0) { + const lastMatch = matches[matches.length - 1]; + splitPoint = lastMatch.index + lastMatch[0].length; + break; + } + } + + // Extract batch + const batch = remainingDOM.substring(0, splitPoint); + batches.push(batch); + + // Remove processed content + remainingDOM = remainingDOM.substring(splitPoint); + } + + // Add remaining content + if (remainingDOM.length > 0) { + batches.push(remainingDOM); + } + + console.log('๐Ÿ“ฆ Split DOM into', batches.length, 'batches:', + batches.map(batch => Math.round(batch.length / 1024) + 'KB')); + + return batches; +} + +async function processDOMBatch(domBatch, batchNumber, originalMessage) { + try { + console.log(`๐Ÿ“ฆ Processing batch ${batchNumber}...`); + + const prompt = createBatchAnalysisPrompt({ + ...originalMessage, + domBatch: domBatch, + batchNumber: batchNumber + }); + + const requestBody = { + contents: [{ + parts: [{ + text: prompt + }] + }] + }; + + const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }); + + if (!response.ok) { + throw new Error(`Batch ${batchNumber} API request failed: ${response.status}`); + } + + const data = await response.json(); + console.log(`โœ… Batch ${batchNumber} analysis complete`); + + return parseBatchAnalysisResponse(data, batchNumber); + + } catch (error) { + console.error(`โŒ Batch ${batchNumber} processing failed:`, error); + return []; + } +} + +function createBatchAnalysisPrompt(message) { + return `You are an AI expert in web security and sensitive data classification. Analyze this DOM batch and classify ALL form fields for sensitivity level. + +PAGE CONTEXT: +- URL: ${message.pageURL} +- Page Title: ${message.pageTitle} +- Batch: ${message.batchNumber} (Complete analysis required) + +DOM BATCH CONTENT: +${message.domBatch} + +AI CLASSIFICATION TASK: +Analyze ALL form fields (input, textarea, select) and classify each by sensitivity: + +๐Ÿ”ด HIGH SENSITIVITY - Immediate Security Risk: +- Passwords, passphrases, PINs, security codes +- 2FA tokens, OTP codes, authentication codes +- Credit card numbers, debit card numbers +- CVV/CVC codes, card expiration dates +- Social Security Numbers (SSN), Tax IDs +- Bank account numbers, routing numbers, IBAN +- Government ID numbers, passport numbers +- Security questions and answers + +๐ŸŸก MEDIUM SENSITIVITY - Personal Identifiable Information: +- Email addresses, usernames, login IDs +- Full names, first names, last names +- Phone numbers, mobile numbers, fax numbers +- Home addresses, work addresses +- Postal codes, ZIP codes, area codes +- Date of birth, age, birth year + +๐ŸŸข LOW SENSITIVITY - General Personal Data: +- Company names, organization names +- Job titles, professional information +- General preferences, settings +- Non-personal contact information + +ADVANCED AI ANALYSIS: +1. Parse field attributes: name, id, class, placeholder, type, autocomplete +2. Analyze surrounding context: labels, headings, form structure +3. Understand page purpose: login, registration, payment, profile +4. Detect patterns: password confirmation, card number formatting +5. Identify field relationships: grouped personal info, payment flows +6. Consider regulatory compliance: GDPR, PCI-DSS, CCPA relevance + +RESPONSE: Valid JSON Array Only: +[ + { + "selector": "precise CSS selector (prefer #id or [name='value'])", + "type": "exact input type", + "sensitivity": "high|medium|low", + "category": "password|financial|personal|contact|security|identification|authentication", + "reason": "detailed AI analysis reasoning", + "confidence": "AI confidence score 0.1-1.0", + "fieldName": "name attribute", + "fieldId": "id attribute", + "aiAnalysis": "detailed field purpose analysis", + "dataType": "what type of sensitive data this field collects", + "riskLevel": "security risk assessment" + } +] + +Return [] if no sensitive fields in this batch. + +Focus on ACCURACY and COMPREHENSIVE CLASSIFICATION. Every sensitive field must be identified.`; +} + +function parseBatchAnalysisResponse(aiData, batchNumber) { + try { + const responseText = aiData.candidates?.[0]?.content?.parts?.[0]?.text || ''; + console.log(`๐Ÿ” Batch ${batchNumber} Response:`, responseText.substring(0, 200) + '...'); + + const jsonMatch = responseText.match(/\[[\s\S]*\]/); + if (!jsonMatch) { + console.warn(`โš ๏ธ Could not parse batch ${batchNumber} response as JSON`); + return []; + } + + const sensitiveFields = JSON.parse(jsonMatch[0]); + console.log(`๐ŸŽฏ Batch ${batchNumber} found`, sensitiveFields.length, 'sensitive fields'); + + return sensitiveFields.filter(field => { + return field.selector && field.type && field.category && field.reason; + }).map(field => ({ + ...field, + confidence: field.sensitivity === 'high' ? 0.9 : field.sensitivity === 'medium' ? 0.7 : 0.5, + detectionMethod: 'AI-Batch-Analysis', + batchNumber: batchNumber + })); + + } catch (error) { + console.error(`โŒ Failed to parse batch ${batchNumber} response:`, error); + return []; + } +} + +function removeDuplicateFields(allFields) { + const uniqueFields = []; + const seenSelectors = new Set(); + + allFields.forEach(field => { + // Use selector as unique identifier + const uniqueKey = field.selector.toLowerCase(); + + if (!seenSelectors.has(uniqueKey)) { + seenSelectors.add(uniqueKey); + uniqueFields.push(field); + } else { + // If duplicate, keep the one with higher confidence + const existingIndex = uniqueFields.findIndex(f => + f.selector.toLowerCase() === uniqueKey); + + if (existingIndex >= 0 && field.confidence > uniqueFields[existingIndex].confidence) { + uniqueFields[existingIndex] = field; + } + } + }); + + console.log('๐Ÿ”„ Removed', allFields.length - uniqueFields.length, 'duplicate fields'); + return uniqueFields; +} + +async function handleCompleteDOMAnalysis(message, sendResponse) { + try { + const startTime = Date.now(); + console.log('๐ŸŒ Complete DOM Analysis requested'); + console.log('๐Ÿ“„ Complete DOM size:', Math.round(message.completeDOM.length / 1024), 'KB'); + console.log('๐Ÿท๏ธ Page type:', message.pageType); + + // Analyze complete DOM with Gemini + const analysis = await analyzeCompleteDOMWithGemini( + message.completeDOM, + message.pageType + ); + + if (analysis && analysis.sensitiveFields) { + const processingTime = Date.now() - startTime; + console.log('โœ… Complete DOM analysis completed in', processingTime, 'ms'); + console.log('๐ŸŽฏ Found', analysis.sensitiveFields.length, 'sensitive fields'); + + sendResponse({ + success: true, + sensitiveFields: analysis.sensitiveFields, + processingTime: processingTime, + pageType: message.pageType + }); + } else { + console.log('โš ๏ธ No sensitive fields found in complete DOM'); + sendResponse({ + success: true, + sensitiveFields: [], + processingTime: Date.now() - startTime, + pageType: message.pageType + }); + } + } catch (error) { + console.error('โŒ Complete DOM analysis failed:', error); + sendResponse({ + success: false, + error: error.message + }); + } +} + +async function handleDOMAnalysis(message, sendResponse) { + try { + const startTime = Date.now(); + console.log('๐ŸŒ DOM Analysis requested for:', message.pageTitle); + console.log('๐Ÿ“„ DOM size:', Math.round(message.domHTML.length / 1024), 'KB'); + console.log('๐Ÿ” Form elements to analyze:', message.formElements.length); + + // Check if we've already analyzed a similar DOM structure + const domHash = generateDOMHash(message.domHTML, message.pageURL); + + if (processedElements.has(domHash)) { + console.log('โญ๏ธ Similar DOM already analyzed, skipping'); + sendResponse({ success: true, results: [], processingTime: 0, cached: true }); + return; + } + + // Mark this DOM as processed + processedElements.add(domHash); + + const aiResults = await callGeminiForDOM(message); + const processingTime = Date.now() - startTime; + + sendResponse({ + success: true, + results: aiResults, + processingTime: processingTime, + domSize: message.domHTML.length, + formElementsCount: message.formElements.length + }); + + } catch (error) { + console.error('โŒ DOM Analysis failed:', error); + sendResponse({ success: false, error: error.message }); + } +} + +async function callGeminiForDOM(message) { + const prompt = createDOMAnalysisPrompt(message); + + const requestBody = { + contents: [{ + parts: [{ + text: prompt + }] + }] + }; + + console.log('๐Ÿš€ Sending DOM to Gemini API...'); + + const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }); + + if (!response.ok) { + throw new Error(`API request failed: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + console.log('โœ… Gemini DOM analysis response received'); + + return parseDOMAnalysisResponse(data, message.formElements); +} + +function createDOMAnalysisPrompt(message) { + return `Analyze this complete web page DOM and identify ALL SENSITIVE form fields that could contain personal, financial, or security data. + +PAGE CONTEXT: +- URL: ${message.pageURL} +- Title: ${message.pageTitle} +- Form Elements Found: ${message.formElements.length} + +COMPLETE DOM STRUCTURE: +${message.domHTML} + +SENSITIVE FIELD TYPES TO DETECT: +- Passwords, PINs, security codes, OTP fields +- Email addresses, usernames, login IDs +- Credit card numbers, CVV, expiry dates +- Social security numbers, tax IDs, government IDs +- Phone numbers, addresses, postal codes +- Bank account numbers, routing numbers +- Personal names (first, last, full name) +- Birthdates, ages +- Any authentication or security-related fields +- Financial account information +- Personal identification numbers + +INSTRUCTIONS: +1. Read the complete DOM structure and understand the page context +2. Identify form fields (input, textarea, select elements) that are likely to contain sensitive data +3. Look for contextual clues like: + - Field labels and surrounding text + - CSS class names and IDs + - Placeholder text + - Autocomplete attributes + - Field positioning and grouping + - Form purpose and page context + +RESPONSE FORMAT: +Respond with ONLY a JSON array containing objects for each sensitive field found: +[ + { + "selector": "CSS selector to uniquely identify the field", + "type": "field type (password, email, text, etc.)", + "sensitivity": "high|medium|low", + "category": "password|username|financial|personal|contact|security", + "reason": "Brief explanation why this field is sensitive", + "name": "field name attribute if available", + "id": "field id attribute if available" + } +] + +If no sensitive fields are found, respond with: [] + +Example response: +[ + { + "selector": "#password", + "type": "password", + "sensitivity": "high", + "category": "password", + "reason": "Password input field for authentication", + "name": "password", + "id": "password" + }, + { + "selector": "input[name='email']", + "type": "email", + "sensitivity": "medium", + "category": "contact", + "reason": "Email address for user identification", + "name": "email", + "id": "" + } +]`; +} + +function parseDOMAnalysisResponse(aiData, formElements) { + try { + const responseText = aiData.candidates?.[0]?.content?.parts?.[0]?.text || ''; + console.log('๐Ÿ” DOM Analysis Response:', responseText.substring(0, 500) + '...'); + + // Extract JSON array from response + const jsonMatch = responseText.match(/\[[\s\S]*\]/); + if (!jsonMatch) { + console.warn('โš ๏ธ Could not parse DOM analysis response as JSON'); + return []; + } + + const sensitiveFields = JSON.parse(jsonMatch[0]); + console.log('๐ŸŽฏ AI identified', sensitiveFields.length, 'sensitive fields in DOM'); + + // Validate and process results + return sensitiveFields.filter(field => { + return field.selector && field.type && field.category && field.reason; + }).map(field => ({ + ...field, + confidence: field.sensitivity === 'high' ? 0.9 : field.sensitivity === 'medium' ? 0.7 : 0.5, + detectionMethod: 'AI-DOM-Analysis' + })); + + } catch (error) { + console.error('โŒ Failed to parse DOM analysis response:', error); + return []; + } +} + +function generateDOMHash(domHTML, pageURL) { + // Create a hash based on DOM structure and URL to avoid re-analyzing similar pages + const domStructure = domHTML.replace(/\s+/g, '').substring(0, 1000); // Simplified structure + const urlBase = pageURL.split('?')[0]; // Remove query parameters + return btoa(`${urlBase}_${domStructure}`).slice(0, 20); +} + +async function callGeminiAPI(elements) { + const prompt = createSensitiveFieldPrompt(elements); + + const requestBody = { + contents: [{ + parts: [{ + text: prompt + }] + }] + }; + + console.log('๐Ÿš€ Sending request to Gemini API...'); + + const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }); + + if (!response.ok) { + throw new Error(`API request failed: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + console.log('โœ… Gemini API response received'); + + return parseAIResponse(data, elements); +} + +function createSensitiveFieldPrompt(elements) { + const elementsText = elements.map((element, index) => + `${index + 1}. Type: ${element.type}, Name: "${element.name}", ID: "${element.id}", Placeholder: "${element.placeholder}", Autocomplete: "${element.autocomplete}"` + ).join('\n'); + + return `Analyze these form fields and identify which ones are SENSITIVE (contain personal, financial, or security data): + +${elementsText} + +SENSITIVE field types include: +- Passwords, PINs, security codes +- Email addresses, usernames +- Credit card numbers, CVV, expiry dates +- Social security numbers, tax IDs +- Phone numbers, addresses +- Bank account numbers +- Personal names, birthdates +- Any field with security/authentication purpose + +Respond with ONLY a JSON array of numbers (1-based indices) for SENSITIVE fields. +Example: [1, 3, 5] means fields 1, 3, and 5 are sensitive. +If no fields are sensitive, respond with: []`; +} + +function parseAIResponse(aiData, elements) { + try { + const responseText = aiData.candidates?.[0]?.content?.parts?.[0]?.text || ''; + console.log('๐Ÿ” AI Response:', responseText); + + // Extract JSON array from response + const jsonMatch = responseText.match(/\[([\d,\s]*)\]/); + if (!jsonMatch) { + console.warn('โš ๏ธ Could not parse AI response as JSON array'); + return []; + } + + const sensitiveIndices = JSON.parse(jsonMatch[0]); + console.log('๐ŸŽฏ AI identified sensitive fields at indices:', sensitiveIndices); + + // Convert indices to results with element info + return sensitiveIndices.map(index => ({ + index: index, + element: elements[index - 1], // Convert to 0-based + confidence: 0.9, // High confidence from AI + reason: 'AI-detected sensitive field' + })).filter(result => result.element); // Filter out invalid indices + + } catch (error) { + console.error('โŒ Failed to parse AI response:', error); + return []; + } +} + +async function analyzeCompleteDOMWithGemini(completeDOM, pageType) { + console.log('๐Ÿค– Analyzing complete DOM with Gemini...'); + + const prompt = ` +You are an expert web security analyst specializing in sensitive field detection. Analyze the complete DOM structure below and identify ALL sensitive input fields that could contain personal, financial, or confidential information. + +Page Type: ${pageType} +DOM Structure: +${completeDOM} + +IMPORTANT INSTRUCTIONS: +1. Look for ALL input fields, textareas, select elements, and any interactive elements +2. Consider the context: labels, placeholders, names, IDs, surrounding text +3. Identify fields that could contain: passwords, emails, phone numbers, SSN, credit card info, addresses, names, dates of birth, security codes, PINs, account numbers, etc. +4. Pay special attention to dynamically generated fields and fields without obvious names +5. Consider hidden or initially invisible fields that may become visible later +6. For banking/financial sites, be extra thorough with transaction and authentication fields + +Respond with a JSON array of objects, each containing: +{ + "selector": "CSS selector to uniquely identify the element", + "type": "password|email|phone|ssn|credit_card|address|name|date_of_birth|security_code|pin|account_number|other", + "confidence": 0.0-1.0, + "reasoning": "why this field is considered sensitive", + "context": "surrounding labels/text that helped identify it" +} + +Return ONLY the JSON array, no other text.`; + + const requestBody = { + contents: [{ + parts: [{ + text: prompt + }] + }], + generationConfig: { + temperature: 0.1, + topK: 40, + topP: 0.95, + maxOutputTokens: 8192, + } + }; + + try { + console.log('๐Ÿ“ค Sending complete DOM to Gemini API...'); + const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }); + + if (!response.ok) { + console.log('โš ๏ธ Primary endpoint failed, trying alternative...'); + return await callGeminiAlternativeEndpoint(requestBody, 'CompleteDOM'); + } + + const data = await response.json(); + console.log('๐Ÿ“ฅ Gemini complete DOM response received'); + + if (data.candidates && data.candidates[0]?.content?.parts?.[0]?.text) { + const responseText = data.candidates[0].content.parts[0].text; + console.log('๐Ÿ” Gemini complete DOM analysis:', responseText.substring(0, 500)); + + try { + const cleanedResponse = responseText.replace(/```json|```/g, '').trim(); + const sensitiveFields = JSON.parse(cleanedResponse); + + if (Array.isArray(sensitiveFields)) { + console.log('โœ… Successfully parsed', sensitiveFields.length, 'sensitive fields from complete DOM'); + return { sensitiveFields }; + } else { + console.log('โš ๏ธ Response not an array:', sensitiveFields); + return { sensitiveFields: [] }; + } + } catch (parseError) { + console.error('โŒ Failed to parse complete DOM response:', parseError); + console.log('๐Ÿ“ Raw response:', responseText); + return { sensitiveFields: [] }; + } + } else { + console.log('โš ๏ธ No content in complete DOM response'); + return { sensitiveFields: [] }; + } + } catch (error) { + console.error('โŒ Complete DOM analysis failed:', error); + return { sensitiveFields: [] }; + } +} + +async function testGeminiAPI(sendResponse) { + try { + console.log('๐Ÿงช Testing Gemini API connection...'); + + const testPrompt = 'Respond with just the word "CONNECTED" if you can read this message.'; + const requestBody = { + contents: [{ + parts: [{ + text: testPrompt + }] + }] + }; + + const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }); + + if (response.ok) { + console.log('โœ… API test successful'); + sendResponse({ success: true, valid: true }); + } else { + console.error('โŒ API test failed:', response.status); + sendResponse({ success: true, valid: false }); + } + } catch (error) { + console.error('โŒ API test error:', error); + sendResponse({ success: false, error: error.message }); + } +} + +function generateElementHash(element) { + // Create unique hash for element to avoid duplicates + return `${element.type}_${element.name}_${element.id}_${element.placeholder}`.toLowerCase(); +} + +async function callGeminiAlternativeEndpoint(requestBody, analysisType) { + const alternativeEndpoints = [ + 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent', + 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent' + ]; + + for (const endpoint of alternativeEndpoints) { + try { + console.log('๐Ÿ”„ Trying alternative endpoint:', endpoint); + + const response = await fetch(`${endpoint}?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }); + + if (response.ok) { + const data = await response.json(); + console.log('โœ… Alternative endpoint successful'); + + if (analysisType === 'viewport') { + return parseViewportAnalysisResponse(data); + } else if (analysisType === 'batch') { + return parseBatchAnalysisResponse(data, 1); + } + } + } catch (error) { + console.warn('โš ๏ธ Alternative endpoint failed:', endpoint, error.message); + } + } + + throw new Error('All Gemini endpoints failed'); +} + +function performFallbackPatternClassification(domContent) { + console.log('๐ŸŽฏ Performing enhanced pattern-based classification...'); + + const sensitiveFields = []; + + // Create a temporary DOM element to parse the content + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = domContent; + + const formFields = tempDiv.querySelectorAll('input, textarea, select'); + + formFields.forEach((field, index) => { + const classification = classifyFieldByPattern(field); + + if (classification.isSensitive) { + sensitiveFields.push({ + selector: generateFieldSelector(field), + type: field.type || 'text', + sensitivity: classification.sensitivity, + category: classification.category, + reason: classification.reason, + fieldName: field.name || '', + fieldId: field.id || '', + placeholder: field.placeholder || '', + context: getFieldContext(field), + confidence: classification.confidence, + detectionMethod: 'Enhanced-Pattern-Based' + }); + } + }); + + console.log('โœ… Pattern classification found', sensitiveFields.length, 'sensitive fields'); + return sensitiveFields; +} + +function classifyFieldByPattern(field) { + const fieldText = [ + field.type || '', + field.name || '', + field.id || '', + field.placeholder || '', + field.className || '', + field.autocomplete || '', + getFieldLabel(field) + ].join(' ').toLowerCase(); + + // High sensitivity patterns + const highSensitivityPatterns = { + password: /password|passwd|pwd|pass|secret|pin|code|otp|2fa|mfa|auth/i, + financial: /card|credit|debit|cvv|cvc|ccv|expir|expire|bank|account|routing|iban|sort/i, + ssn: /ssn|social.?security|tax.?id|ein|national.?id|gov.?id/i, + security: /security|answer|question|challenge|verify|confirm/i + }; + + // Medium sensitivity patterns + const mediumSensitivityPatterns = { + personal: /name|first|last|full.?name|surname|given/i, + contact: /email|mail|phone|mobile|tel|address|street|city|zip|postal|country/i, + identification: /username|user.?id|login|signin|account.?id|member.?id/i, + birth: /birth|age|dob|date.?of.?birth|born/i + }; + + // Check high sensitivity + for (const [category, pattern] of Object.entries(highSensitivityPatterns)) { + if (pattern.test(fieldText) || field.type === 'password') { + return { + isSensitive: true, + sensitivity: 'high', + category: category, + reason: `High sensitivity ${category} field detected`, + confidence: 0.9 + }; + } + } + + // Check medium sensitivity + for (const [category, pattern] of Object.entries(mediumSensitivityPatterns)) { + if (pattern.test(fieldText)) { + return { + isSensitive: true, + sensitivity: 'medium', + category: category, + reason: `Medium sensitivity ${category} field detected`, + confidence: 0.8 + }; + } + } + + // Special email type check + if (field.type === 'email') { + return { + isSensitive: true, + sensitivity: 'medium', + category: 'contact', + reason: 'Email input field detected', + confidence: 0.95 + }; + } + + return { isSensitive: false }; +} + +function generateFieldSelector(field) { + if (field.id) { + return `#${field.id}`; + } + if (field.name) { + return `input[name="${field.name}"]`; + } + if (field.type && field.type !== 'text') { + return `input[type="${field.type}"]`; + } + return `input:nth-of-type(${Array.from(field.parentNode.querySelectorAll('input')).indexOf(field) + 1})`; +} + +function getFieldLabel(field) { + // Look for associated label + if (field.labels && field.labels.length > 0) { + return field.labels[0].textContent || ''; + } + + // Look for nearby label + const label = field.parentNode.querySelector('label'); + if (label) { + return label.textContent || ''; + } + + // Look for previous text content + const prevElement = field.previousElementSibling; + if (prevElement && prevElement.textContent) { + return prevElement.textContent.trim(); + } + + return ''; +} + +// NEW: Enhanced function for raw DOM analysis - Pattern Detection FIRST, then AI +async function analyzeRawDOMWithGemini(rawDOM, pageInfo = {}) { + console.log('๐Ÿ” NEW: Starting enhanced DOM analysis (Pattern FIRST, then AI)...'); + console.log('๐Ÿ“„ Raw DOM size:', Math.round(rawDOM.length / 1024), 'KB'); + console.log('๐ŸŒ Page info:', pageInfo); + + const startTime = Date.now(); + + // STEP 1: Pattern Detection FIRST (Full DOM) + console.log('๐ŸŽฏ STEP 1: Running pattern detection on FULL DOM...'); + const patternFields = performFallbackPatternDetection(rawDOM, pageInfo); + console.log(`โœ… Pattern detection found ${patternFields.length} sensitive fields`); + + // STEP 1.5: Send pattern detection results IMMEDIATELY to browser + console.log('๐Ÿ“ค STEP 1.5: Sending pattern detection results immediately to browser...'); + const patternSelectors = patternFields.map(field => field.selector); + console.log(`๐Ÿ“Š Sending ${patternSelectors.length} pattern-detected selectors to browser...`); + + // Get tab ID for sending to browser + const tabId = await getTabIdFromPageInfo(pageInfo); + if (tabId) { + executeMaskingScript(tabId, patternSelectors, 'pattern-detection'); + } else { + console.warn('โš ๏ธ Could not determine tab ID for pattern detection results'); + } + + // STEP 2: AI Detection SECOND (Viewport data only to reduce load) + console.log('๐Ÿง  STEP 2: Running AI detection on viewport data...'); + let aiFields = []; + + try { + // Extract viewport data for AI analysis (reduce load) + const viewportData = extractViewportData(rawDOM, pageInfo); + console.log('๐Ÿ“ Viewport data size:', Math.round(viewportData.length / 1024), 'KB'); + + aiFields = await performAIAnalysis(viewportData, pageInfo); + console.log(`โœ… AI detection found ${aiFields.length} additional sensitive fields`); + + } catch (error) { + console.error('โŒ AI detection failed:', error); + console.log('โš ๏ธ Continuing with pattern detection results only'); + } + + // STEP 3: Compare and send only NEW AI-detected fields + console.log('๐Ÿ”„ STEP 3: Comparing pattern and AI results...'); + const newAIFields = findNewAIDetectedFields(patternFields, aiFields); + + if (newAIFields.length > 0) { + console.log(`๐Ÿ“ค STEP 3.5: Sending ${newAIFields.length} NEW AI-detected fields to browser...`); + const newAISelectors = newAIFields.map(field => field.selector); + + if (tabId) { + executeMaskingScript(tabId, newAISelectors, 'ai-detection'); + } else { + console.warn('โš ๏ธ Could not determine tab ID for AI detection results'); + } + } else { + console.log('โœ… No new AI-detected fields found - all fields already covered by pattern detection'); + } + + // STEP 4: Combine all results for logging + console.log('๐Ÿ“Š STEP 4: Combining all results for logging...'); + const combinedFields = [...patternFields, ...newAIFields]; + + const totalTime = Date.now() - startTime; + console.log('โœ… Enhanced DOM analysis completed in:', totalTime, 'ms'); + console.log('๐ŸŽฏ Total detected sensitive fields:', combinedFields.length); + console.log(`๐Ÿ“Š Breakdown: ${patternFields.length} pattern + ${newAIFields.length} new AI = ${combinedFields.length} total`); + + // Log detailed results with breakdown info + const logInfo = { + ...pageInfo, + patternFields: patternFields.length, + aiFields: newAIFields.length, + totalAIFields: aiFields.length + }; + logSensitiveFieldResults(combinedFields, logInfo); + + return { + success: true, + sensitiveFields: combinedFields, + processingTime: totalTime, + patternFields: patternFields.length, + aiFields: newAIFields.length, + totalAIFields: aiFields.length, + domSize: rawDOM.length, + pageInfo: pageInfo, + attempts: 1 + }; +} + +function createRawDOMAnalysisPrompt(rawDOM, pageInfo) { + const url = pageInfo.url || 'Unknown URL'; + const title = pageInfo.title || 'Unknown Page'; + const pageType = pageInfo.pageType || 'Unknown'; + const domState = pageInfo.domState || {}; + + return ` +You are an expert web security analyst specializing in sensitive field detection. Analyze the complete DOM structure below and identify ALL sensitive input fields that could contain personal, financial, or confidential information. + +PAGE INFORMATION: +- URL: ${url} +- Title: ${title} +- Page Type: ${pageType} +- DOM State: ${JSON.stringify(domState)} + +COMPLETE DOM STRUCTURE (INCLUDING SHADOW DOM AND DYNAMIC CONTENT): +${rawDOM} + +DETECTION INSTRUCTIONS: +1. Search for ALL input elements: , + + + +
+

๐ŸŽฏ 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:

+
    +
  1. Make sure the sensitive-field-detector extension is loaded and enabled
  2. +
  3. Refresh this page
  4. +
  5. Watch the console for automatic raw DOM analysis logs
  6. +
  7. Check the status below for confirmation
  8. +
+
+ +
+ โณ Waiting for automatic raw DOM analysis... +
+ +
+

๐Ÿ” Test Form Fields

+ + + + + + + + +
+ +
+

๐Ÿ“Š Analysis Results

+
No results yet...
+
+ +
+

๐Ÿ“ Console Logs

+
Waiting for logs...
+
+
+ + + + \ 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 + + + +
+

๐Ÿ” Raw DOM Analysis Test

+

This page contains various sensitive fields to test the Gemini AI detection capabilities.

+ +
+

๐Ÿ” Authentication Fields

+ + + + + + + + +
+ +
+

๐Ÿ‘ค Personal Information

+ + + + + + + + + + + +
+ +
+

๐Ÿ  Address Information

+ + + + + + + + + + + +
+ +
+

๐Ÿ’ณ Financial Information

+ + + + + + + + + + + +
+ +
+

๐Ÿ†” Government IDs

+ + + + + + + + +
+ +
+

๐Ÿ”’ Security Codes

+ + + + + + + + +
+ +
+ + +
+ +
+

๐Ÿ“Š Analysis Results

+
+
+
+ + + + \ 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 + + + +
+
+

๐Ÿ” Sensitive Field Detector Test Site

+

Comprehensive testing environment for sensitive field detection extension

+
+ +
+
+

๐Ÿงช How to Test the Extension:

+
    +
  1. Load the Extension: Make sure the sensitive-field-detector_1 extension is loaded in Chrome
  2. +
  3. Enable Detection: Click the extension icon and enable detection
  4. +
  5. Open DevTools: Press F12 and go to Console tab
  6. +
  7. Watch for Logs: Look for detection logs starting with ๐Ÿ”, ๐ŸŽฏ, ๐Ÿง , etc.
  8. +
  9. Test Different Scenarios: Scroll through different sections and interact with forms
  10. +
  11. Check Masking: Verify that sensitive fields are being masked by the browser API
  12. +
+
+ + +
+

๐Ÿ” Basic Authentication Fields

+
+ + +
Expected: Medium sensitivity - Username field
+
+
+ + +
Expected: Medium sensitivity - Email field
+
+
+ + +
Expected: High sensitivity - Password field
+
+
+ + +
Expected: High sensitivity - Password confirmation
+
+
+ + +
+

๐Ÿ‘ค Personal Information Fields

+
+ + +
Expected: Medium sensitivity - Personal name
+
+
+ + +
Expected: Medium sensitivity - Personal name
+
+
+ + +
Expected: Medium sensitivity - Phone number
+
+
+ + +
Expected: Medium sensitivity - Date of birth
+
+
+ + +
Expected: High sensitivity - SSN
+
+
+ + +
+

๐Ÿ’ฐ Financial Information Fields

+
+ + +
Expected: High sensitivity - Credit card number
+
+
+ + +
Expected: High sensitivity - CVV code
+
+
+ + +
Expected: High sensitivity - Card expiry
+
+
+ + +
Expected: High sensitivity - Bank account
+
+
+ + +
Expected: High sensitivity - Routing number
+
+
+ + +
+

๐Ÿ  Address Information Fields

+
+ + +
Expected: Medium sensitivity - Street address
+
+
+ + +
Expected: Low sensitivity - City
+
+
+ + +
Expected: Low sensitivity - State
+
+
+ + +
Expected: Medium sensitivity - ZIP code
+
+
+ + +
Expected: Low sensitivity - Country
+
+
+ + +
+

๐Ÿ”’ Security and Token Fields

+
+ + +
Expected: High sensitivity - Security code
+
+
+ + +
Expected: High sensitivity - PIN
+
+
+ + +
Expected: High sensitivity - OTP
+
+
+ + +
Expected: High sensitivity - API key
+
+
+ + +
+

๐Ÿ‘ป 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

+
+ + +
Expected: Low sensitivity - General text
+
+
+ + +
Expected: High sensitivity - Secret information
+
+
+ + +
+

๐Ÿ“„ 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();