Is Profile to Resume - Rezi.ai safe?
Rezi is medium risk. Clicking Begin Extraction on a LinkedIn profile reads data from the page DOM: contact details, the about section, work history, education, certifications, volunteering, organizations, skills, languages, and projects, into a resume object.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
LinkedIn profile details copied into a resume object
Clicking Begin Extraction on a LinkedIn profile reads data from the page DOM: contact details, the about section, work history, education, certifications, volunteering, organizations, skills, languages, and projects, into a resume object.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You click Begin Extraction on a LinkedIn profile.
The click handler runs only after this extension-panel action.
The extension reads LinkedIn profile content and builds a resume object.
It also creates off-screen iframes for detail sections such as experience, skills, education, and certifications.
- Name and contact detailsJane Doe, https://www.linkedin.com/in/jane-doe/, https://janedoe.example, jane.doe@example.com
This identifies you and gives the extension your LinkedIn URL, website, and email when those fields are present.
- About sectionProduct designer focused on accessible enterprise workflows.
This can include your self-written professional summary and other personal context from the profile.
- Work historySenior Product Designer, Example Co, January 2021 - Present, San Francisco
This describes where you have worked, your roles, dates, locations, and profile descriptions.
- Education and certificationsExample University, B.S. Human-Computer Interaction, 2018
This adds schools, qualifications, grades, dates, licensing bodies, and certification descriptions to the resume object.
- Skills, languages, projectsProduct Design; Languages: English, Spanish; Checkout Redesign project
This records detailed career attributes that can distinguish your profile from other people.
The panel click sends the extraction message into the active LinkedIn tab
Deobfuscated src/page_action/page_action.js runExtraction
src/page_action/page_action.jsfunction runExtraction() { hasDownloadedProfile = !1, setPanelState("active"), syncReviewCardVisibility(), beginExtractButton.style.display = "none", loading.style.display = "block", appendLog("Begin extraction clicked", "info"), withActiveTab(function(e) { if (!e || !e.id) return setPanelState("infos"), syncReviewCardVisibility(), beginExtractButton.style.display = "", loading.style.display = "none", void appendLog("No active tab found", "error"); isExtracting = !0, lockedTabId = e.id, lockedTabUrl = e.url || "", appendLog("Locked extraction to tab #" + lockedTabId, "info"), ensureContentScript(e, function(t, o) { if (t) return isExtracting = !1, lockedTabId = null, lockedTabUrl = "", setPanelState("infos"), syncReviewCardVisibility(), beginExtractButton.style.display = "", loading.style.display = "none", void appendLog("Cannot start extraction: " + t, "error"); o && appendLog("Injected extraction script into current tab", "info"), chrome.tabs.sendMessage(e.id, { greeting: "begin extract" }, function() { chrome.runtime.lastError ? (isExtracting = !1, lockedTabId = null, lockedTabUrl = "", setPanelState("infos"), syncReviewCardVisibility(), beginExtractButton.style.display = "", loading.style.display = "none", appendLog("Cannot start extraction: " + chrome.runtime.lastError.message, "error")) : appendLog("Extraction message sent to page", "ok"), chrome.runtime.lastError }) }) })}The content script reads contact fields from the LinkedIn page
Deobfuscated getPersonalInfos
src/inject/inject.jsgetPersonalInfos = e => { const t = () => !!(resume.contact.linkedin && resume.contact.website && resume.contact.email), n = () => (parseContactFromDocument(document, resume.contact), !!(resume.contact.linkedin || resume.contact.website || resume.contact.email)); resume.contact = { name: getNameFromProfilePage(e), linkedin: "", website: "", email: "" }; const r = e.querySelectorAll(".ci-vanity-url .pv-contact-info__contact-link"), i = e.querySelectorAll(".ci-websites .pv-contact-info__contact-link"), o = e.querySelectorAll(".ci-email .pv-contact-info__contact-link"); if (r.forEach(e => { e.innerText && (resume.contact.linkedin = e.innerText.trim()) }), i.forEach(e => { e.innerText && (resume.contact.website = e.innerText.trim()) }), o.forEach(e => { e.innerText && (resume.contact.email = e.innerText.trim()) }), n(), t()) return; const a = Array.from(e.querySelectorAll("a,button")).find(e => /contact info/i.test((e.innerText || e.getAttribute("aria-label") || "").trim())); if (a && (a.click(), setTimeout(() => { n(); const e = document.querySelector('dialog[open] button[aria-label*="Dismiss"], dialog[open] button[aria-label*="Close"]'); e && e.click() }, 900)), !t()) try { const e = window.location.pathname.match(/(\/in\/[^/]+)\/?/), t = e && e[1] ? e[1] : window.location.pathname.replace(/\/$/, ""), n = window.location.origin + t + "/overlay/contact-info/", r = document.createElement("iframe"); r.setAttribute("id", "rezi-contact-overlay-frame"), r.style.display = "none", r.src = n, r.onload = () => { try { const e = r.contentWindow && r.contentWindow.document ? r.contentWindow.document : null; parseContactFromDocument(e, resume.contact) } catch (e) { emitError(e) } finally { r.remove() } }, document.body.appendChild(r) } catch (e) { emitError(e) } },Off-screen iframes load LinkedIn detail sections before parsing them
Deobfuscated off-screen iframe loader excerpt
src/inject/inject.jsi = (i, o) => new Promise(a => { const l = document.createElement("iframe"); l.setAttribute("id", i), l.setAttribute("src", getProfileRootUrl() + "details/" + i + "/"), l.setAttribute("tabindex", "-1"), l.setAttribute("aria-hidden", "true"), l.style.position = "fixed", l.style.left = "-10000px", l.style.top = "0", l.style.width = "projects" === i ? "1280px" : "1px", l.style.height = "projects" === i ? "8000px" : "1px", l.style.opacity = "0", l.style.pointerEvents = "none", l.style.border = "0"; const s = document.querySelector("main > div"); if (s && s.parentNode) s.parentNode.insertBefore(l, s.nextSibling); else { (document.body || document.documentElement).appendChild(l) } l.onload = () => { (async () => { if ("timeout" === await r(l, i) && emitStage(`Wait timeout for ${i}, parsing anyway`, "info"), "projects" === i && await new Promise(e => setTimeout(e, 2e3)), t[o]) { let r = "nothing"; try { r = await t[o]() } catch (e) { emitError(`${i} extraction failed: ${e&&e.message?e.message:String(e)}`) } return logSectionResult(i, r), l.remove(), o === e.length - 1 ? (n(), void a()) : void a() } l.remove(), a() })() } }); let o = 0;for (const t of e) await i(t, o), o += 1