Is Profile to Resume - Rezi.ai safe?

Medium risk

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.

45Risk

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

You click Begin Extraction on a LinkedIn profile.

The click handler runs only after this extension-panel action.

The extension did this

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.

Profile fields copied from LinkedIn DOM into the resume object
  • Name and contact details
    Jane 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 section
    Product designer focused on accessible enterprise workflows.

    This can include your self-written professional summary and other personal context from the profile.

  • Work history
    Senior Product Designer, Example Co, January 2021 - Present, San Francisco

    This describes where you have worked, your roles, dates, locations, and profile descriptions.

  • Education and certifications
    Example University, B.S. Human-Computer Interaction, 2018

    This adds schools, qualifications, grades, dates, licensing bodies, and certification descriptions to the resume object.

  • Skills, languages, projects
    Product Design; Languages: English, Spanish; Checkout Redesign project

    This records detailed career attributes that can distinguish your profile from other people.

The code that does this

The panel click sends the extraction message into the active LinkedIn tab

Readable version

Deobfuscated src/page_action/page_action.js runExtraction

src/page_action/page_action.js
function 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 code that does this

The content script reads contact fields from the LinkedIn page

Readable version

Deobfuscated getPersonalInfos

src/inject/inject.js
getPersonalInfos = 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)    }  },
The code that does this

Off-screen iframes load LinkedIn detail sections before parsing them

Readable version

Deobfuscated off-screen iframe loader excerpt

src/inject/inject.js
i = (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
Updated 21 September 2026delakdmnpanaclafnplfomddhlhlcloe