// gmail_scanner.js
console.log("PhishGuard AI: Gmail Scanner loaded.");
// Gmail's DOM can change, but commonly the email text is stored in elements with class '.a3s' or '.ii.gt'
const EMAIL_BODY_SELECTOR = '.a3s, .ii.gt';
// Function to inject a visible warning banner into the Gmail UI
function injectWarningBanner(emailContainer, message) {
// Prevent duplicate banners if one is already injected
if (emailContainer.parentNode.querySelector('.phishguard-banner')) {
return;
}
const banner = document.createElement('div');
banner.className = 'phishguard-banner';
// Styling the banner to look native but urgent, fitting Google's Material Design
banner.style.backgroundColor = '#fce8e6';
banner.style.color = '#c5221f';
banner.style.border = '1px solid #faa59f';
banner.style.borderRadius = '8px';
banner.style.padding = '12px 16px';
// Added margin to ensure it doesn't overlap text awkwardly
banner.style.margin = '16px auto';
banner.style.fontFamily = '"Google Sans", Roboto, Arial, sans-serif';
banner.style.fontSize = '14px';
banner.style.fontWeight = '500';
banner.style.lineHeight = '20px';
banner.style.display = 'flex';
banner.style.alignItems = 'center';
banner.style.boxShadow = '0 1px 2px 0 rgba(60,64,67,0.3), 0 1px 3px 1px rgba(60,64,67,0.15)';
// Add SVG Icon for warning
const iconSvg = `
`;
const textContent = document.createElement('span');
textContent.innerText = message || '🚨 PhishGuard Warning: This email contains suspicious links.';
// Construct the banner
banner.innerHTML = iconSvg;
banner.appendChild(textContent);
// Insert banner at the top of the email container.
// By inserting before the email container, we keep it visible at the top of the body.
if (emailContainer.parentNode) {
emailContainer.parentNode.insertBefore(banner, emailContainer);
}
}
// Helper function to extract all unique URLs from the email body
function extractUrlsFromBody(emailContainer) {
const links = emailContainer.querySelectorAll('a[href]');
const urls = new Set(); // Use a Set to store unique URLs
links.forEach(link => {
const href = link.href;
// Basic filter to ignore mailto: or javascript: links, and only keep http/https
if (href && (href.startsWith('http://') || href.startsWith('https://'))) {
urls.add(href);
}
});
return Array.from(urls);
}
// Function to safely extract the sender's actual email address
function extractSenderEmail(emailContainer) {
// Gmail usually groups each email message into a container block.
// We traverse up to find a common parent containing both header and body.
// '.kv' or 'table' or '.adn' is often the parent wrapper for an individual message.
let messageWrapper = emailContainer.closest('.adn') || emailContainer.closest('.kv') || document;
// The sender element usually has the class '.gD' and contains the 'email' attribute.
const senderElement = messageWrapper.querySelector('.gD');
if (senderElement && senderElement.getAttribute('email')) {
return senderElement.getAttribute('email');
}
// Fallback: Sometimes the email address is enclosed in brackets inside a '.go' element.
const fallbackSenderElement = messageWrapper.querySelector('.go');
if (fallbackSenderElement && fallbackSenderElement.innerText) {
// e.g. "" -> matched and extracted
const match = fallbackSenderElement.innerText.match(/<([^>]+)>/);
if (match && match[1]) {
return match[1];
}
}
return "Unknown Sender";
}
// Function to handle newly opened emails
function handleEmailOpened(emailContainer) {
// Prevent re-scanning the same email element
if (emailContainer.dataset.pgScanned === "true") {
return;
}
console.log("PhishGuard AI: New email thread opened. Extracting data...");
// Mark as scanned
emailContainer.dataset.pgScanned = "true";
// 1. Extract plain text content
const emailBodyText = emailContainer.innerText;
// 2. Extract embedded URLs
const urls = extractUrlsFromBody(emailContainer);
// 3. Extract sender email address
const sender = extractSenderEmail(emailContainer);
// 4. Extract subject line (.hP is a standard class for Gmail's main subject line)
// Sometimes the subject might be in a '.bog' element. We'll default to 'h2.hP'.
const subjectElement = document.querySelector('h2.hP') || document.querySelector('.bog');
const subject = subjectElement ? subjectElement.innerText.trim() : "No Subject Found";
// Package the extracted data into a JSON payload
const emailPayload = {
sender: sender,
subject: subject,
body: emailBodyText,
urls: urls,
timestamp: new Date().toISOString()
};
console.log("PhishGuard AI extracted payload:", emailPayload);
// Send background message to service worker
chrome.runtime.sendMessage(
{
action: "analyzeEmail",
data: emailPayload
},
(response) => {
if (chrome.runtime.lastError) {
console.error("PhishGuard AI: Error communicating with background script:", chrome.runtime.lastError);
return;
}
console.log("PhishGuard AI Background Analysis Response:", response);
// Assume the background script returns `response.analysis` containing `probability` or `isPhishing` flag
const analysis = response && response.analysis ? response.analysis : {};
if (analysis.isPhishing === true || analysis.probability > 0.70) {
console.warn("PhishGuard AI: High risk email detected! Injecting banner...");
injectWarningBanner(
emailContainer,
'🚨 PhishGuard Warning: This email contains suspicious links and exhibits high-risk phishing behavior.'
);
}
}
);
}
// Set up a MutationObserver to watch for DOM changes
// This effectively detects when Gmail dynamically loads an individual email view into the DOM
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
// Check if the added node itself is the email body container
if (node.matches && node.matches(EMAIL_BODY_SELECTOR)) {
handleEmailOpened(node);
}
// Also search securely within the added node structure for the email body
if (node.querySelectorAll) {
const emailBodies = node.querySelectorAll(EMAIL_BODY_SELECTOR);
emailBodies.forEach(body => handleEmailOpened(body));
}
}
});
}
}
});
// Start observing the document body for deeper added nodes (like when navigating between emails)
observer.observe(document.body, {
childList: true,
subtree: true
});
console.log("PhishGuard AI: MutationObserver is listening for email thread opens.");