Is Autofill Smartly - JobFill safe?
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.
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
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.
DA confirmed injection, not the POST body.
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 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.
| Field | Value | Why it matters | |
|---|---|---|---|
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. |
The Save Job button collects readable page content
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)
}
}();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 service worker posts those fields to JobFill
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
});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;- 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
When you use JobFill's autofill, the extension reads the current website hostname and sends it to JobFill.
The hostname also feeds a Google Analytics page_view event, revealing which site you filled during a signed-in session.
You use Autofill on a website.
The extension sends the current site hostname to JobFill and records it as an analytics page view.
| Field | Value | Why it matters | |
|---|---|---|---|
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. |
The content script sends the hostname and the service worker forwards it
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);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
}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)
})- 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
Clicking Summarize in the JobFill sidebar converts the page to visible text and posts that text to JobFill for summarization.
Since the content script runs on all sites, submitted text can come from any page with the sidebar available.
You click Summarize in the JobFill sidebar.
The extension reads visible text from the current page and posts it to JobFill.
| Field | Value | Why it matters | |
|---|---|---|---|
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. |
The sidebar extracts page text and the service worker posts it
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")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()
}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");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;- jobfill.ai
Receives raw page text in POST requests to /api/summary.
+1 more finding not shown