Is Passei Direto – Ferramentas de Estudo safe?

High risk

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.…

pd.servicesv2.22.2Chrome Web Store
78Risk
Who publishes it

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.

Publisher
pd.services

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.

brainly.com.br
Also called by 1 other listing: Quizit.online

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
What actually happens
You did this

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 did this

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.

URL and profile fields assembled before the POST
  • Visited page URL
    https://mail.google.com/mail/u/0/

    Shows a page you visited outside the extension's own site.

  • Anonymous user ID
    8f4b6c2e-1a0d-4d7f-9a62-3c0b6e9f41ab (illustrative)

    Lets multiple page visits be associated with the same browser profile.

  • Student degree level
    studentDegreeLevelId=3 (illustrative)

    Adds education-profile context from your Passei Direto account when available.

  • Course
    courseId=101; courseName=Direito (illustrative)

    Adds your course identifier and course name to the same event as the page visit.

  • University
    universityId=122; universityName=Universidade de Sao Paulo (illustrative)

    Adds your university identifier and university name to the visit event.

  • Subscription details
    isPremium=true; planDescription=Premium; subscribedProducts=resumos;exercicios (illustrative)

    Adds premium status, plan description, plan ID, and subscribed products when those values exist.

Captured request
POSThttps://tracking-api.passeidireto.com/track

Observed during dynamic analysis: the request body contained ExtensionUrlVisits and urlPath=https://mail.google.com/mail/u/0/.

The code that does this

Code path that pairs visited URLs with student-profile fields

Readable version

Readable all-sites content script

AnonymousTrackingsRoot.b6ad317b.js
let 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.js
case "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    }  }}
Destination that receives the linked visit event
    • 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
What actually happens
You did this

You open a web page while the extension is installed.

The manifest registers AnonymousTrackingsRoot.b6ad317b.js on all URLs.

The extension did this

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.

Fields assembled for the ExtensionUrlVisits event
  • Visited page URL
    https://mail.google.com/mail/u/0/

    Shows the page you had open when the extension generated the visit event.

  • Anonymous user ID
    8f4b6c2e-1a0d-4d7f-9a62-3c0b6e9f41ab (illustrative)

    Lets repeated visit events be linked to the same browser profile.

  • Premium status
    true (illustrative)

    Adds whether the associated Passei Direto account has premium access.

  • Course and university
    courseName=Direito; universityName=Universidade de Sao Paulo (illustrative)

    Adds education-profile context to the browsing event when those account fields are available.

  • Plan and products
    planId=42; subscribedProducts=study-tools;premium (illustrative)

    Adds subscription context such as plan ID, plan description, and subscribed products.

Captured request
POSThttps://tracking-api.passeidireto.com/track

Observed during dynamic analysis: the request body contained EventName ExtensionUrlVisits and Data.urlPath https://mail.google.com/mail/u/0/.

The code that does this

The all-sites content script builds the visit event and the service worker posts it

Readable version

Readable content-script visit event

AnonymousTrackingsRoot.b6ad317b.js
let 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.js
l = 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.js
case "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    }  }}
Destination reached by the visit event
    • 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
What actually happens
You did this

You switch tabs or navigate to a new page.

The extension did this

The extension reads the active tab URL and saves it in extension storage.

Values written to extension storage
  • Active page URL
    https://github.com/

    Shows the page open in your active tab at the time the listener runs.

  • Storage key
    pd-active-tab-url-1585209810

    Links the saved page URL to a specific browser window in extension storage.

  • Tab load state
    complete

    Limits the write to tabs that have finished loading.

What's stored on your device

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'
Contents
{  "pd-active-tab-url-1585209810": {    "url": "https://github.com/"  }}
The code that does this

The active-tab URL storage module

Readable version

Readable module that builds the storage key and writes the URL

static/background/index.js
fuicj: [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

Updated 30 September 2026jnefaihbaohechagcjhebbjofkiolgan