Is Leeco AI – Auto-Apply Jobs on LinkedIn & Upskill for Job Interviews safe?

Medium risk

Leeco AI is medium risk. Visiting a coding or learning site (LeetCode, GitHub, YouTube, GeeksforGeeks, ~40 others) sends the URL, timestamp, email, browser fingerprint, and LeetCode rank/solve count to Preplaced analytics, rate-limited hourly, no disclosed consent.

Preplacedv2.1.38Chrome Web Store
48Risk
Who publishes it

Preplaced - 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
Preplaced
Registered address
D-110, Sobha Garnet, ibblur village, Bangalorebangalore south ,, bangalore, Karnataka 560103, IN
Registered contact
Keshav Dilip Patil

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

Publishers can request a review.

Findings

Browsing History Sent to Analytics on Every Tab Navigation

Visiting a coding or learning site (LeetCode, GitHub, YouTube, GeeksforGeeks, ~40 others) sends the URL, timestamp, email, browser fingerprint, and LeetCode rank/solve count to Preplaced analytics, rate-limited hourly, no disclosed consent.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You navigate to a whitelisted coding or learning site (e.g., leetcode.com, github.com, geeksforgeeks.org).

Navigation triggers chrome.tabs.onUpdated with status='complete' in the extension's background service worker.

The extension did this

The extension sends your account email, the full visited URL, and your browser fingerprint to a Preplaced analytics endpoint.

The POST fires automatically once per URL per four-hour window with no per-navigation user prompt.

Captured request
POSThttps://common-api.preplaced.in/analytics/track

Two live POST requests captured during dynamic analysis (DA observed requestsand 586), one for geeksforgeeks.org and one for interviewbit.com, each containing the injected planted marker value as user_id.

Headers
Content-Type
application/json
Body
{  "user_id": "[planted marker value email]",  "event": "Tab Visited",  "properties": {    "source": "Extension",    "browser_name": "Chrome",    "browser_version": "146.0.0.0",    "os_family": "Mac OS",    "os_version": "10_15_7",    "engine_name": "Blink",    "device": "Linux x86_64",    "product_name": "MentorAi Extension",    "url": "https://www.geeksforgeeks.org/",    "url_params": {},    "mentorai": {      "name": "Preplaced Mentor",      "record_id": "recXXXXXXXX",      "public_profile_id": "prof_XXXXXXXX"    },    "extension_version": "2.1.38",    "leetcode_profile_data": {      "problems_solved": 247,      "submissions": 891,      "rank": 183042    },    "is_paid_user": false,    "session_type": "default"  }}
Fields sent in every 'Tab Visited' analytics POST
  • Account email
    alice@example.com

    Your Leeco/Preplaced login email, used as the analytics user identifier, linking every navigation event back to your account.

  • Visited URL
    https://leetcode.com/problems/two-sum/

    The full URL of the page you navigated to, including any path or query parameters.

  • Browser fingerprint
    Chrome 146.0.0.0 / Mac OS 10_15_7 / Blink / MacIntel

    Your browser name, version, OS family, OS version, rendering engine, and device platform.

  • Extension version
    2.1.38

    The installed version of the extension, used to correlate analytics events with releases.

  • LeetCode profile stats
    {"rank": 183042, "problems_solved": 247, "submissions": 891}

    Your LeetCode rank, total problems solved, and submission count, attached to every navigation event on whitelisted sites.

  • A/B test variants
    interview_mode_access_option_variant: "sharing"

    Active experiment assignments for features such as interview mode, daily message limit, and trial screen display.

The code that does this

Navigation handler wires chrome.tabs.onUpdated to the analytics POST

Readable version
// yc() = isUrlWhitelisted(url, mentorAiId) checks gc['pro-pilot']['whitelisted-urls']// D = 'authEmail' (the chrome.storage.sync key holding the user's email)// Eu() = hasNotVisitedRecently(url) reads/writes chrome.storage.local 'tabVisits', skips if <4h ago// hu() = trackTabVisited(email, url, eventName, extraProps) calls du() to build payload, then iu() to POSTif (isUrlWhitelisted(tab.url, mentorAiId) === 'whitelisted') { chrome.storage.sync.get(['authEmail'], async (result) => { const userEmail = result['authEmail']; if (userEmail && tab.url && await hasNotVisitedRecently(tab.url)) { const props = { url: tab.url }; trackTabVisited(userEmail, tab.url, 'Tab Visited', props); } });}
Analytics destination
    • common-api.preplaced.in

    Preplaced (India) backend API. Receives Tab Visited events with user email and URL via POST /analytics/track. baseURL 'https://common-api.preplaced.in/' in tabUpdated.BhCmg7uj.js.

What it can do

Permissions this extension asks for, as declared in version 2.1.38. 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 every site you visit

    <all_urls>

  • Show a panel beside the page

    sidePanel

  • Act on the current tab, but only after you click the extension

    activeTab

  • Store data in your browser

    storage

  • See the address and title of every tab you have open

    tabs

Updated 30 September 2026phbhomcaiapjeoghkpegkkccfidalfaa