Is Passei Direto – Ferramentas de Estudo safe?
Passei Direto is high risk. We observed a tracking request for a visited mail.google.com page; the event builder pairs the URL with student-profile fields. The script runs on all URLs and posts ExtensionUrlVisits to tracking-api.passeidireto.com/track.…
Who publishes itpd.services - no other listings under this identity, 1 shared hostname
pd.services - no other listings under this identity, 1 shared hostname
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 1 hostname
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Visited URLs are paired with student profile data
We observed a tracking request for a visited mail.google.com page; the event builder pairs the URL with student-profile fields.
The script runs on all URLs and posts ExtensionUrlVisits to tracking-api.passeidireto.com/track.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You open a page while signed in or previously identified by Passei Direto cookies.
The content script is registered on all URLs, not only on Passei Direto pages.
The extension sends the visited URL together with student-profile context to its tracking API.
Dynamic analysis observed the visited URL in the tracking request, and the code adds the profile fields before the background POST.
- Visited page URLhttps://mail.google.com/mail/u/0/
Shows a page you visited outside the extension's own site.
- Anonymous user ID8f4b6c2e-1a0d-4d7f-9a62-3c0b6e9f41ab (illustrative)
Lets multiple page visits be associated with the same browser profile.
- Student degree levelstudentDegreeLevelId=3 (illustrative)
Adds education-profile context from your Passei Direto account when available.
- CoursecourseId=101; courseName=Direito (illustrative)
Adds your course identifier and course name to the same event as the page visit.
- UniversityuniversityId=122; universityName=Universidade de Sao Paulo (illustrative)
Adds your university identifier and university name to the visit event.
- Subscription detailsisPremium=true; planDescription=Premium; subscribedProducts=resumos;exercicios (illustrative)
Adds premium status, plan description, plan ID, and subscribed products when those values exist.
Observed during dynamic analysis: the request body contained ExtensionUrlVisits and urlPath=https://mail.google.com/mail/u/0/.
Code path that pairs visited URLs with student-profile fields
Readable all-sites content script
AnonymousTrackingsRoot.b6ad317b.jslet f = { matches: ["<all_urls>"]},d = () => ((0, i.useEffect)(() => { let e = async () => { try { let e = window.location.href, t = (0, a.sanitizeUrl)(e), r = await p(t); await (0, c.sendTrackingEvent)({ EventName: "ExtensionUrlVisits", Data: r }) } catch (e) { console.warn("Failed to track URL in content script:", e) } }; e()}, []), null),p = async e => { let [t, r] = await Promise.all([(0, u.default).getCurrentUser(), (0, s.getAnonParams)()]), { anonUserId: n } = r; return { id: n, urlPath: e, origin: "pd-interception-extension", isPremium: t?.isPremium, studentDegreeLevelId: t?.student?.StudentDegreeLevelId, courseId: t?.student?.Course?.Id, courseName: t?.student?.Course?.Name, universityId: t?.student?.University?.Id, universityName: t?.student?.University?.Name, planId: t?.plan?.Id, planDescription: t?.plan?.Description, subscribedProducts: t?.plan?.subscribedProducts.join(";") }};Readable service-worker POST path
static/background/index.jscase "trackEvent": (0, en.default)({ ...e, sender: t }, { send: e => r(e) }); break;let a = async (e, t) => { try { let r = e.body; await (0, s.default).track(r), t.send({ success: !0 }) } catch (e) { console.error("Error tracking event:", e), t.send({ success: !1, error: e.message }) }};let a = (0, s.default).pdRequestClient("https://tracking-api.passeidireto.com");r.default = class { static async track({ SessionId: e, EventName: t, Data: r = {} }) { try { await a.postAsync("/track", { SessionId: e, EventName: t, Data: r }) } catch (e) { throw console.error("Failed to track event:", e), e } }}- tracking-api.passeidireto.com
Receives ExtensionUrlVisits events that include the visited URL and the available student-profile fields.
Every visited URL is posted to Passei Direto tracking
We observed a page-visit event sent to tracking-api.passeidireto.com outside Passei Direto.
The content script (all URLs) strips query/fragment and attaches account fields (anon ID, premium, course, university, plan) then POSTs to /track.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You open a web page while the extension is installed.
The manifest registers AnonymousTrackingsRoot.b6ad317b.js on all URLs.
The extension builds an ExtensionUrlVisits event from that page and sends it to Passei Direto's tracking endpoint.
Dynamic analysis observed this for a mail.google.com URL, with similar requests for Google, Amazon, GitHub, and Wikipedia pages.
- Visited page URLhttps://mail.google.com/mail/u/0/
Shows the page you had open when the extension generated the visit event.
- Anonymous user ID8f4b6c2e-1a0d-4d7f-9a62-3c0b6e9f41ab (illustrative)
Lets repeated visit events be linked to the same browser profile.
- Premium statustrue (illustrative)
Adds whether the associated Passei Direto account has premium access.
- Course and universitycourseName=Direito; universityName=Universidade de Sao Paulo (illustrative)
Adds education-profile context to the browsing event when those account fields are available.
- Plan and productsplanId=42; subscribedProducts=study-tools;premium (illustrative)
Adds subscription context such as plan ID, plan description, and subscribed products.
Observed during dynamic analysis: the request body contained EventName ExtensionUrlVisits and Data.urlPath https://mail.google.com/mail/u/0/.
The all-sites content script builds the visit event and the service worker posts it
Readable content-script visit event
AnonymousTrackingsRoot.b6ad317b.jslet f = { matches: ["<all_urls>"]},d = () => ((0, i.useEffect)(() => { let e = async () => { try { let e = window.location.href, t = (0, a.sanitizeUrl)(e), r = await p(t); await (0, c.sendTrackingEvent)({ EventName: "ExtensionUrlVisits", Data: r }) } catch (e) { console.warn("Failed to track URL in content script:", e) } }; e()}, []), null),p = async e => { let [t, r] = await Promise.all([(0, u.default).getCurrentUser(), (0, s.getAnonParams)()]), { anonUserId: n } = r; return { id: n, urlPath: e, origin: "pd-interception-extension", isPremium: t?.isPremium, studentDegreeLevelId: t?.student?.StudentDegreeLevelId, courseId: t?.student?.Course?.Id, courseName: t?.student?.Course?.Name, universityId: t?.student?.University?.Id, universityName: t?.student?.University?.Name, planId: t?.plan?.Id, planDescription: t?.plan?.Description, subscribedProducts: t?.plan?.subscribedProducts.join(";") }};Readable URL sanitizer
AnonymousTrackingsRoot.b6ad317b.jsl = e => { try { let t = new URL(e); return t.search = "", t.hash = "", t.toString() } catch (t) { return e.split("?")[0].split("#")[0] }}Readable service-worker tracker
static/background/index.jscase "trackEvent": (0, en.default)({ ...e, sender: t }, { send: e => r(e) }); break;let a = async (e, t) => { try { let r = e.body; await (0, s.default).track(r), t.send({ success: !0 }) } catch (e) { console.error("Error tracking event:", e), t.send({ success: !1, error: e.message }) }};let a = (0, s.default).pdRequestClient("https://tracking-api.passeidireto.com");r.default = class { static async track({ SessionId: e, EventName: t, Data: r = {} }) { try { await a.postAsync("/track", { SessionId: e, EventName: t, Data: r }) } catch (e) { throw console.error("Failed to track event:", e), e } }}- tracking-api.passeidireto.com
Receives POST /track events named ExtensionUrlVisits containing the sanitized page URL and account context.
Active Tab URLs Saved in Extension Storage
We observed the extension reading the active tab URL after tab changes and saving it in local storage.
The stored key includes the browser window ID; observed values included common destinations such as Google, GitHub, Gmail, and Wikipedia.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You switch tabs or navigate to a new page.
The extension reads the active tab URL and saves it in extension storage.
- Active page URLhttps://github.com/
Shows the page open in your active tab at the time the listener runs.
- Storage keypd-active-tab-url-1585209810
Links the saved page URL to a specific browser window in extension storage.
- Tab load statecomplete
Limits the write to tabs that have finished loading.
The extension keeps a local record of the last active page URL for that browser window.
- Location
- chrome.storage.local key 'pd-active-tab-url-1585209810'
{ "pd-active-tab-url-1585209810": { "url": "https://github.com/" }}The active-tab URL storage module
Readable module that builds the storage key and writes the URL
static/background/index.jsfuicj: [function(e, t, r) { var n = e("@parcel/transformer-js/src/esmodule-helpers.js"); n.defineInteropFlag(r), n.export(r, "buildUrlStorageKey", () => o), n.export(r, "setActiveTabUrlOnActivated", () => a), n.export(r, "setActiveTabUrlOnUpdated", () => i); let o = e => `pd-active-tab-url-${e}`; function s(e) { if (e && e.url && "complete" === e.status) { let t = e.windowId, r = o(t); chrome.storage.local.set({ [r]: { url: e.url } }) } } function a(e) { try { chrome.tabs.get(e.tabId, s) } catch (e) { console.error("Error getting tab URL:", e) } } function i(e, t, r) { try { s(r) } catch (e) { console.error("Error saving tab URL:", e) } }}, { "@parcel/transformer-js/src/esmodule-helpers.js": "flUmw"}]+2 more findings not shown