Is Autofill Smartly - JobFill safe?

High risk

Autofill Smartly - JobFill sends full page text, form field metadata, and visited hostnames to jobfill.ai on user actions.

When a user clicks 'Save Job', the extension captures the full text content and URL of the current page and sends them to jobfill.ai/api/applications/extract. On each autofill event, it also transmits form field labels, names, types, and placeholder text to jobfill.ai/api/labels, and sends the visited hostname to both jobfill.ai/api/events and Google Analytics. Additionally, clicking 'Summarize' sends the full page text to jobfill.ai/api/summary.

nhatdxv1.7.39Chrome Web Store
77Risk
Who publishes it

JobFill - 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
nhatdx
Declared legal entity
JobFill
Registered contact
Dinh Xuan Nhat

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

Publishers can request a review.

Findings

Save Job sends page text and URL to JobFill

HighunwantedCWE-359Dynamic sandbox

Clicking Save Job in JobFill clones the page, extracts title and text with Readability, adds the URL, and sends those to the service worker, which POSTs to jobfill.ai/api/applications/extract.

What actually happens

You did this

You click Save Job in the JobFill sidebar on a page.

The sidebar is rendered by the content script that runs on matching pages.

The extension did this

The extension reads the page's readable text, title, and current URL and sends them to JobFill.

The service worker posts those fields to the application-extraction endpoint.

Fields prepared for the Save Job request

  • Readable page text
    Senior Software Engineer, Platform Team. Build internal tools for application workflows and collaborate with product teams. (illustrative)

    This can include the main text from the page where you clicked Save Job, such as a job description or application instructions.

  • Page title
    Senior Software Engineer - Acme Careers (illustrative)

    This identifies the browser page or document title attached to the saved job.

  • Current page URL
    https://www.linkedin.com/jobs/view/3987654321/ (illustrative)

    This identifies the exact page you submitted through Save Job.

Captured request

POSThttps://jobfill.ai/api/applications/extract

No request body was captured during dynamic analysis because the JobFill sign-in flow could not be completed; the shipped service worker posts to this endpoint after receiving the Save Job message.

The code that does this

The Save Job button collects readable page content

Readable version

Readable content-script Save Job handler

content.js
C = function() {  var t = sa(ra().mark((function t() {    var n, e, r, i, o, a, s;    return ra().wrap((function(t) {      for (;;) switch (t.prev = t.next) {        case 0:          return t.prev = 0, c(!0), l().Notify.info("Saving the job...", {            timeout: 2e4          }), n = document.cloneNode(!0), e = new vt.Readability(n), r = e.parse(), t.next = 8, w({            message: "save-job",            title: null == r ? void 0 : r.title,            textContent: null == r ? void 0 : r.textContent,            url: window.location.href          }, !0, "", 1e4);        case 8:          if (!0 !== (null == (i = t.sent) ? void 0 : i.status) || "object" !== na(null == i ? void 0 : i.data)) {            t.next = 13;            break          }          return o = i.data, a = o.status, s = o.message, l().Notify["success" === a ? "success" : "failure"](s), t.abrupt("return");        case 13:          throw new Error("Invalid response format");        case 16:          t.prev = 16, t.t0 = t.catch(0), l().Notify.failure("Failed to save the job");        case 20:          return t.prev = 20, c(!1), t.finish(20);        case 23:        case "end":          return t.stop()      }    }), t, null, [      [0, 16, 20, 23]    ])  })));  return function() {    return t.apply(this, arguments)  }}();

Readable Save Job button click

content.js
pt.createElement("button", {  type: "button",  className: "w-full rounded-md bg-indigo-500 px-0 py-2 text-center text-xs font-semibold mb-2 text-white hover:bg-indigo-400 cursor-pointer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500",  onClick: function() {    C(), w({      message: "ga-event",      event_name: "sw-c-sj"    }, !1)  },  disabled: u}, u ? "Saving..." : "Save Job")

The code that does this

The service worker posts those fields to JobFill

Readable version

Readable authenticated JobFill client

service-worker.js
var $n = null,  Kn = null,  Zn = ["jf_u", "jf_k", "jf_s"],  ti = "https://jobfill.ai",  ei = new URL(ti).hostname,  ni = fn.create({    baseURL: "https://jobfill.ai",    withCredentials: !0,    timeout: 18e4  });

Readable save-job POST branch

service-worker.js
if ("save-job" === t.message) return ni.post("/api/applications/extract", {  title: t.title,  textContent: t.textContent,  url: t.url}).then((function(t) {  n({    status: !0,    data: t.data  })})).catch((function(t) {  n({    status: !1  })})), !0;

Destination named by the Save Job flow

    • jobfill.ai

    Receives the Save Job application-extraction request containing the page title, readable text content, and URL.

Autofill sends site hostnames to JobFill and Google Analytics

MediumunwantedCWE-359Dynamic sandbox

When you use JobFill's autofill, the extension reads the current website hostname and sends it to JobFill.

What actually happens

You did this

You use Autofill on a website.

The extension did this

The extension sends the current site hostname to JobFill and records it as an analytics page view.

Fields sent by this path

  • Website hostname
    careers.example.org

    Shows which site you were using when Autofill ran.

  • Analytics event name
    hn

    Ties the website hostname to a page-view event for this extension.

  • Analytics client ID
    7f0b47a4-83d0-44b3-8f75-1dd6aef8b6c1

    Lets the analytics service group repeated events from the same browser profile.

Captured request

POSThttps://jobfill.ai/api/events

No body or response was recorded in unauthenticated dynamic testing; the shipped handler posts this URL when Autofill reaches the ga-hn path.

Captured request

POSThttps://www.google-analytics.com/mp/collect?measurement_id=G-V79VSW9NZG&api_secret=<redacted>

No live Google Analytics response was recorded in unauthenticated dynamic testing; the shipped handler invokes this endpoint through firePageViewEvent.

The code that does this

The content script sends the hostname and the service worker forwards it

Readable version

Readable content-script hostname message

content.js
case 16:  return Zd.value = !0, rp.value = ru(Zd, rp, _d), ip(), window.self === window.top && w({    message: "ga-hn",    hn: window.self.location.hostname  }, !1), t.abrupt("return", !1);

Readable service-worker hostname handler

service-worker.js
if ("ga-hn" === t.message) {  try {    u.firePageViewEvent("hn", t.hn), ni.post("/api/events", {      hn: t.hn    }).catch((function(t) {}))  } catch (t) {}  return !1}

Readable analytics page-view wrapper

service-worker.js
key: "firePageViewEvent",value: (o = c(a().mark((function t(e, n) {  var i, o = arguments;  return a().wrap((function(t) {    for (;;) switch (t.prev = t.next) {      case 0:        return i = o.length > 2 && void 0 !== o[2] ? o[2] : {}, t.abrupt("return", this.fireEvent("page_view", r({          page_title: e,          page_location: n        }, i)));      case 2:      case "end":        return t.stop()    }  }), t, this)}))), function(t, e) {  return o.apply(this, arguments)})

Destinations reached by the hostname path

    • jobfill.ai

    Receives the hostname in a POST to /api/events when the ga-hn handler runs.

    • www.google-analytics.com

    Receives a Measurement Protocol page_view event with the hostname as page_location.

Summarize sends current page text to JobFill

MediumunwantedCWE-359Dynamic sandbox

Clicking Summarize in the JobFill sidebar converts the page to visible text and posts that text to JobFill for summarization.

What actually happens

You did this

You click Summarize in the JobFill sidebar.

The extension did this

The extension reads visible text from the current page and posts it to JobFill.

Fields sent for summarization

  • Page text
    Senior Product Manager role, responsibilities, requirements, and application questions

    Can include the visible content you are reading, editing, or preparing to submit on the active site.

Captured request

POSThttps://jobfill.ai/api/summary

The static proof path shows the service worker posts raw_text to this endpoint; no recorded request body is included here.

The code that does this

The sidebar extracts page text and the service worker posts it

Readable version

Readable Summarize button handler

content.js
pt.createElement("button", {  type: "button",  className: "w-full rounded-md bg-indigo-500 px-0 py-2 text-center text-xs font-semibold mb-2 text-white hover:bg-indigo-400 cursor-pointer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500",  onClick: function() {    n(), w({      message: "ga-event",      event_name: "sw-c-sm"    }, !1)  },  disabled: e}, e ? "Summarising..." : "Summarize")

Readable text extraction helper

content.js
function Lo(t) {  return function(t, n = {}, e) {    return To(n)(t, e)  }(t.documentElement.innerHTML, {    wordwrap: !1,    selectors: [{      selector: '[style*="display: none"]',      format: "skip"    }, {      selector: '[style*="visibility: hidden"]',      format: "skip"    }, {      selector: "[hidden]",      format: "skip"    }, {      selector: ".hidden",      format: "skip"    }, {      selector: ".d-none",      format: "skip"    }, {      selector: ".invisible",      format: "skip"    }, {      selector: '[aria-hidden="true"]',      format: "skip"    }, {      selector: ".visually-hidden",      format: "skip"    }]  }).trim()}

Readable summary message sender

content.js
case 7:  return t.next = 9, w({    message: "generate-summary",    raw_text: n  }, !0, "", 12e4);case 9:  if ((r = t.sent).status) {    t.next = 13;    break  }  return t.abrupt("return");

Readable service-worker summary handler

service-worker.js
if ("generate-summary" === t.message) return Dn(Bn().mark((function e() {  var i;  return Bn().wrap((function(e) {    for (;;) switch (e.prev = e.next) {      case 0:        return e.prev = 0, e.next = 3, ni.post("/api/summary", {          raw_text: t.raw_text        });      case 3:        i = e.sent, n({          status: !0,          data: i.data        }), e.next = 11;        break;      case 7:        e.prev = 7, e.t0 = e.catch(0), n({          status: !1,          message: "Failed to generate summary"        });      case 11:      case "end":        return e.stop()    }  }), e, null, [    [0, 7]  ])})))(), !0;

Destination reached by the summarize path

    • jobfill.ai

    Receives raw page text in POST requests to /api/summary.

+1 more finding not shown

Where it sends data

Destinations our analysis observed Autofill Smartly - JobFill 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.

  • www.google-analytics.comwidely used

    Autofill Smartly - JobFill sends data to www.google-analytics.com. A widely used service: 388 other extensions we have analyzed send data here.

  • jobfill.ai

    Autofill Smartly - JobFill sends data to jobfill.ai. No other extension we have analyzed sends data here.

Updated 30 September 2026kbgfilncepjeoodogmebahnloidgaibg