Is LI Prospect Finder safe?

Medium risk

LIPF is medium risk. When LI Prospect Finder can't parse certain LinkedIn search-result pages locally, it posts the page HTML to li-prospect-finder.com. The posted JSON's html field carries the full document, including rendering data used to extract records.

info@lipf.storev3.2.14Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

LinkedIn search HTML sent to parsing endpoints

When LI Prospect Finder can't parse certain LinkedIn search-result pages locally, it posts the page HTML to li-prospect-finder.com.

The posted JSON's html field carries the full document, including rendering data used to extract records.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You run a LinkedIn company or people search through the extension.

The fallback paths appear when LinkedIn pages use the newer rendering data or when local people parsers return no results.

The extension did this

The extension posts the LinkedIn search-result HTML to its vendor server for parsing.

The request body is JSON with an html field containing the page document.

Data placed in the parsing request
  • LinkedIn page HTML
    Illustrative HTML: <html><script>window.__como_rehydration__ = {"data":{"searchDashClustersByAll":{"metadata":{"primaryResultType":"COMPANIES"}}}}</script></html>

    This can include the search-result markup and embedded LinkedIn rendering data for the profiles or companies shown on the page.

  • Request field name
    html

    The server receives the page document under a single html field, which is enough to reconstruct the search results being parsed.

Captured request
POSThttps://li-prospect-finder.com/extension/api/companies/parse
Headers
Accept
application/json
Ext-Version
3.2.12
Content-Type
application/json
Captured request
POSThttps://li-prospect-finder.com/extension/api/peoples/parse
Headers
Accept
application/json
Ext-Version
3.2.12
Content-Type
application/json
The code that does this

Fallbacks that send LinkedIn search-result HTML

Readable version

Company search fallback in readable form

deobfuscated/js/li/search/searchCompanies.js
async function getCompaniesFromDefaultSearch() {  let e = WEB_REQUEST_LI_API_URLS.COMPANIES_SEARCH;  !0 === tab.url?.toLowerCase().includes("schools") && (e = WEB_REQUEST_LI_API_URLS.SCHOOLS_SEARCH);  var a = await getLocalStorageValue(e);  a && a[tab.id] && (searchUrl = a[tab.id], csrfToken) && (a = {    "csrf-token": this.csrfToken,    "x-restli-protocol-version": "2.0.0"  }, 0 < searchUrl.indexOf(WEB_REQUEST_LI_API_URLS.NEW_API) && (a.accept = "application/vnd.linkedin.normalized+json+2.1"), $.ajaxSetup({    headers: a,    global: !1,    type: "GET"  }));  let t = !1,    s = "";  tab.url?.toLowerCase().includes("companies") && (s = await $.get(tab.url) || "", t = s.includes("window.__como_rehydration__")), t ? companies = await apiCompaniesInfoFromHtml({    html: s  }) || [] : (a = await $.get(searchUrl), (companies = liSearchCompaniesParser.handle(a)) && 0 !== companies.length || (companies = liSearchCompaniesParser.getCompanies(a))), renderCompaniesList()}

People parser fallback in readable form

deobfuscated/js/li/search/searchProspectsParser.js
async getPeopleListV4(e) {  return {    people: (await apiProspectsInfoFromHtml({      html: e    }) || []).map(e => ({      ...e,      source: "linkedIn",      searchLink: `https://www.linkedin.com/voyager/api/identity/dash/profiles?q=memberIdentity&memberIdentity=${e.source_id_2}&decorationId=com.linkedin.voyager.dash.deco.identity.profile.FullProfileWithEntities-26`    })),    hasUnadded: !1  }}

Automated company search pagination in readable form

deobfuscated/js/li/autoSearch/autoSearchCompanyTask.js
async goToNextSearchPage() {  let t = !1;  if (!this.isFinished && !this.isPaused && this.isStarted) {    var s;    if (this.currentCompanies = [], (!0 === this.urlStart?.toLowerCase().includes("results/companies") || !0 === this.urlStart?.toLowerCase().includes("results/schools") || 0 < this.urlCurrent.indexOf(WEB_REQUEST_LI_API_URLS.NEW_API)) && (t = !0), +this.pageCurrent != +this.pageStart) return !(+this.pageCurrent <= +this.pageFinish) || (this.errorInternetCount = 0, this.urlCurrent = this.getCurrentSearchUrl(this.urlStart), 0 < this.urlCurrent.indexOf(WEB_REQUEST_LI_API_URLS.NEW_API) && $.ajaxSetup({      headers: {        accept: "application/vnd.linkedin.normalized+json+2.1"      },      global: !1,      type: "GET"    }), "string" == typeof(s = await $.get(this.urlCurrent)) && s.includes("window.__como_rehydration__") ? this.companies = await apiCompaniesInfoFromHtml({      html: s    }) : this.companies = liSearchCompaniesParser.handle(s), this.companies && 0 !== this.companies.length || (this.list.length ? this.companies = this.list : this.companies = t ? liSearchCompaniesParser.getCompanies(s) : getCompaniesSN(s), this.checkIsPageAbsentError(s, t)), this.isPageAbsentError) ? void await this.finishPageHandler() : (await taskManager.setUnfinishedTask(getDeepCopyOfObject({      ...this,      currentCompanies: []    })), void this.sendCompanyBlock());    this.list.length ? this.companies = this.list : this.companies = localStorage.companies ? JSON.parse(localStorage.companies) : [], this.needsSendCompaniesCount = this.companies.length + (this.pageFinish - this.pageStart) * this.companyCountOnPage, await this.sendCompanyBlock()  }}

Automated people search pagination in readable form

deobfuscated/js/li/autoSearch/autoSearchTask.js
async goToNextSearchPage() {  if (!this.isFinished && !this.isPaused && this.isStarted)    if (+this.pageCurrent > +this.pageFinish) this.finish && this.finish();    else {      this.errorInternetCount = 0, this.urlCurrent = this.getCurrentSearchUrl(this.urlStart);      var t = this;      if (this.list) i();      else {        let e = this.urlCurrent;        var s = {            "csrf-token": this.csrfToken,            "x-restli-protocol-version": "2.0.0"          },          s = (0 < e.indexOf(WEB_REQUEST_LI_API_URLS.NEW_API) && (s.accept = "application/vnd.linkedin.normalized+json+2.1"), this.requestMethod === REQUEST_METHODS.POST && (e = this.urlCurrent.split("?")[0], s["Content-Type"] = "application/x-www-form-urlencoded", s["x-http-method-override"] = "GET"), {            method: this.requestMethod,            headers: s          });        this.requestMethod === REQUEST_METHODS.POST && (s.body = this.urlCurrent.split("?")[1]);        try {          i(await (await fetch(e, s)).text())        } catch (e) {          0 === e.status ? (t.error = !0, t.errorCode = 0, t.errorOnPageGlobal()) : t.errorOnPage()        }      }    } async function i(e) {    !e && t.list ? (t.people = t.list, t.list = void 0) : (t.people = t.getPeopleList(e), t.people && t.people?.length || t.isSalesNavInt || (t.people = (await (new searchProspectsParser).getPeopleListV4(e))?.people)), t.getTrackingInfo && (t.trackingInfo = t.getTrackingInfo(e)), t.people && 0 < t.people.length ? (e = await t.checkPrevAddedPeople(t.people) || !1, t.needRecheckPrevAdded = e, await t.checkRecentlyUpdatedPeoples(t.people), t.itemsParsed = 0, t.waitAndGetPerson(), await reRenderTask(t)) : (t.itemsParsed = 0, t.waitAndGetPerson(), reRenderTask(t)), await reRenderTask(t)  }}
The code that does this

The API wrapper builds POST requests to the parsing endpoints

Readable version

Endpoint constants and POST helper in readable form

deobfuscated/js/apiMethods.js
let API_HOST = APP_HOST + "/extension/api",  USER_BALANCE = "/user/balance",  PEOPLE_CREATE = "/peoples/create",  PEOPLE_CONTACTS = "/peoples/contacts",  PEOPLE_RECENTLY_UPDATED = "/peoples/recently-updated",  COMPANIES_CREATE = "/companies/create",  LISTS_BY_USER = "/lists/get-by-user-id",  LISTS_BY_PROSPECTS = "/lists/get-by-peoples-ids",  LISTS_BY_COMPANIES = "/lists/get-by-companies-ids",  NEWS_GET = "/news/get-last",  LI_HTML = "/linkedin/collect",  COMPANIES_PARSE = "/companies/parse",  PROSPECTS_PARSE = "/peoples/parse";function post(e, a) {  return fetch(e, {    method: "POST",    body: JSON.stringify(a),    headers: {      "Content-Language": getUILanguage(),      "Ext-Version": chrome.runtime.getManifest().version,      "Content-Type": "application/json",      Accept: "application/json"    }  })}

Parsing endpoint wrappers in readable form

deobfuscated/js/apiMethods.js
async function apiCompaniesInfoFromHtml(e) {  e = await post(API_HOST + COMPANIES_PARSE, e);  return e.ok && (await e.json())?.companies || []}async function apiProspectsInfoFromHtml(e) {  e = await post(API_HOST + PROSPECTS_PARSE, e);  return e.ok && (await e.json())?.peoples || []}
Server that receives the parsing requests
    • li-prospect-finder.com

    LI Prospect Finder vendor host that receives LinkedIn search-result HTML at /extension/api/companies/parse and /extension/api/peoples/parse.

Updated 30 September 2026mdhjhplkmldfjgoegbdmallcdhlecfcn