Is Octopus - All-in-One LinkedIn Automation safe?

High risk

Octopus CRM is high risk. When the LinkedIn lead-sync path runs, the extension reads contact details from LinkedIn Voyager and sends the lead record (email, phone, profile site, name, company site) to Dripify's shared leads email API. No request body was captured.…

Octopus CRMv3.7.17Chrome Web Store
75Risk
Who publishes it

Octopus CRM - 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
Octopus CRM

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

Publishers can request a review.

Findings

LinkedIn contact details are posted to Dripify

When the LinkedIn lead-sync path runs, the extension reads contact details from LinkedIn Voyager and sends the lead record (email, phone, profile site, name, company site) to Dripify's shared leads email API.

No request body was captured.

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

You start the LinkedIn lead-sync workflow for a profile.

The code path is tied to the extension's getSyncLeadData action and requires the authenticated campaign workflow.

The extension did this

The extension gathers LinkedIn contact details and sends the lead record to Dripify.

The source constructs a JSON POST to the shared leads email API.

Fields assembled for the lead record
  • Lead email address
    alex.chen@example.com (illustrative)

    This directly identifies the LinkedIn lead being synced.

  • Lead phone number
    +1 415 555 0137 (illustrative)

    This can identify and contact the lead outside LinkedIn.

  • Profile website
    https://alexchen.example (illustrative)

    This adds external profile or business context to the synced lead.

  • LinkedIn profile ID
    ACoAADeF123B456c789D (illustrative)

    This ties the lead record back to a specific LinkedIn profile.

Captured request
POSThttps://app.dripify.com/api/v1/shared/leads/email

No captured response body was recorded for this authenticated campaign path.

Headers
Authorization
Bearer <redacted>
The code that does this

Contact fields are read from LinkedIn and then posted to Dripify

Readable version

Readable contact-field extractor

assets/action-content.js
function i(e) {  var e = e?.data?.identityDashProfilesByMemberIdentity?.elements;  return Array.isArray(e) && 0 !== e.length ? {    emailAddress: (e = e[0]).emailAddress?.emailAddress,    websites: function(e) {      e = e.websites;      return Array.isArray(e) && 0 !== e.length ? e.map(e => ({        type: {          "com.linkedin.voyager.identity.profile.StandardWebsite": {            category: e.category          }        },        url: e.url      })) : []    }(e),    phoneNumbers: function(e) {      e = e.phoneNumbers;      return Array.isArray(e) && 0 !== e.length ? e.filter(e => e?.phoneNumber?.number).map(e => ({        type: e.type,        number: e.phoneNumber.number      })) : []    }(e)  } : {}}

Readable lead-sync handler

assets/action-content.js
"getSyncLeadData" == o.action && (n = r.then(function(e) {  return async function(e, t) {    var n = {},      i = await p(e),      r = i.elements[0],      i = (n.FIRST_NAME = i, n.COMPANY = await h(e), n.EMAIL = await d(e, t), r.profilePositionGroups.elements);    return i.length && i[0].companyUrn && (e = i[0].companyUrn.replace("urn:li:fsd_company:", ""), n.COMPANY_WEBSITE = await g(e, t).catch(() => ({}))), {      leadData: n,      leadId: r.entityUrn.substring(19)    }  }(o.id, e).then(a)}))

Readable Dripify sender

main-es2018.8479c1dfcb59ff03ea0b.js
sendDripifyRequest(t) {  var e = this;  return yg(function*() {    let n = yield e.appContext.currentUser.getIdToken();    return e.http.request(t.method, "https://app.dripify.com/api" + t.url, {      responseType: "json",      body: t.isJson ? t.params : null,      params: t.isJson ? null : t.params,      headers: {        Authorization: "Bearer " + n      }    }).toPromise()  })()}syncAndFindEmail(t, e) {  var n = this;  return this.httpUtils.sendDripifyRequest({    url: "/v1/shared/leads/email",    method: "POST",    params: t,    isJson: !0  }).catch(function() {    var i = yg(function*(i) {      return i.error && i.error.message && "OUT_OF_VERIFY_EMAIL_CREDITS" === i.error.message ? (yield n.homepageService.sendHomepageEvent(n_.FORCE_REFRESH_EMAIL_FINDER_CREDITS), n.forceStopProcessing(), !1) : e >= 3 ? (n.forceStopProcessing("Unpredicted errors", "Please contact support"), !1) : (yield e_.later(1e4), n.syncAndFindEmail(t, ++e))    });    return function(t) {      return i.apply(this, arguments)    }  }())}
Destination reached by the lead-sync POST
    • app.dripify.com

    Receives the JSON lead record at /api/v1/shared/leads/email.

    • www.linkedin.com

    Supplies profile and contact data used to build the lead record.

LinkedIn Recruiter search URLs sent to Dripify API

Starting Octopus's Send to CRM flow from a LinkedIn Recruiter Talent search posts the search URL and Recruiter context to app.dripify.com.

Not captured dynamically; the test session couldn't authenticate Recruiter and Octopus CRM together.

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

You start the Octopus Send to CRM workflow from a LinkedIn Recruiter Talent search.

The popup copies the current search URL into the selection data before sending the request to the extension workflow.

The extension did this

The extension sends that Recruiter search context to Dripify to build the search URL used for profile selection.

The code path is specific to Talent search pages rather than every LinkedIn page.

Fields the code places in the Dripify search URL request
  • LinkedIn Recruiter search URL
    https://www.linkedin.com/talent/search/results/people?keywords=account%20executive&origin=GLOBAL_SEARCH_HEADER

    Shows the Recruiter search page you are using and can include search keywords, filters, project context, and result-page state.

  • Recruiter contract
    urn:li:contract:82374651

    Connects the request to the Recruiter account context active in the LinkedIn tab.

  • Hiring project state
    [{"state":"IN_REVIEW","count":25}]

    Adds project-specific Recruiter state when the Talent URL points at an all-candidates project view.

Captured request
POSThttps://app.dripify.com/api/searches/url

The shipped code expects a JSON response with a url property and returns that value to the profile-selection workflow.

Headers
Authorization
Bearer <redacted>
The code that does this

The popup selection flow and Dripify request construction

Readable version

Readable request helper builds the Dripify API call

deobfuscated/main-es2018.8479c1dfcb59ff03ea0b.js
sendDripifyRequest(t) {  var e = this;  return yg(function*() {    let n = yield e.appContext.currentUser.getIdToken();    return e.http.request(t.method, "https://app.dripify.com/api" + t.url, {      responseType: "json",      body: t.isJson ? t.params : null,      params: t.isJson ? null : t.params,      headers: {        Authorization: "Bearer " + n      }    }).toPromise()  })()}

Readable selection method posts the Recruiter URL fields

deobfuscated/main-es2018.8479c1dfcb59ff03ea0b.js
getSearchUrl(t, e) {  var n = this;  return yg(function*() {    let i, r = t.selectionType.toLowerCase();    if (r.includes("profile") || r.includes("mynetwork")) {      let i = n.selectionTypeConfigFactory.getSelectionTypeConfig(t.selectionType);      return yield i.composeUrlFunction({        url: t.url,        params: null,        tabId: e      })    }    t.selectionType == ZS.RECRUITER_TALENT_PEOPLE && (i = (yield e_.sendTabMessage(n_.GET_TALENT_ME, e, {      headers: n.appContext.headers    })).contract);    const s = new URL(t.url).pathname.split("/");    let o;    return t.selectionType === ZS.RECRUITER_TALENT_PEOPLE && "all" === (s.length > 5 ? s[5] : null) && (o = yield e_.sendFirstTabMessage(n_.GET_TALENT_HIRING_PROJECTS, {      headers: n.appContext.headers,      contract: i,      projectId: s[3]    })), (yield n.httpUtils.sendDripifyRequest({      method: "POST",      url: "/searches/url",      isJson: !0,      params: {        url: t.url,        contract: i,        hiringStates: o      }    })).url  })()}

Readable popup submit handler starts the selection workflow

deobfuscated/main-es2018.8479c1dfcb59ff03ea0b.js
sendToCRM() {  if (!this.selectForm.valid) return;  let t = {};  t.url = this.searchContext ? this.searchUrl : null, t.selectionType = this.searchUrlService.determineSelectionType(this.searchUrl), t.profilesToSelect = this.profilesToSelect > 0 ? this.profilesToSelect : 100, t.selectedCampaign = this.selectedCampaign, t.selectedAction = this.selectedAction, this.helperService.showSelectModal(), e_.sendMessage(n_.START_SELECT, {    data: t  })}
External destination for the Recruiter search context
    • app.dripify.com

    Receives the POST to /api/searches/url containing the LinkedIn Recruiter search URL fields prepared by the extension code.

What it can do

Permissions this extension asks for, as declared in version 3.7.19. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 3.7.17, which we have not unpacked yet.

  • Read and change your data on linkedin.com

    https://linkedin.com/* and 1 more

  • Read and change your data on api.octopuscrm.io

    https://api.octopuscrm.io/*

  • Read and change your data on hooks.zapier.com

    https://hooks.zapier.com/*

  • Read and change your data on octopuscrm-io.firebaseapp.com

    https://octopuscrm-io.firebaseapp.com/*

  • Read and change your data on make.com

    https://*.make.com/*

  • Read and change your data on cdnjs.cloudflare.com

    https://cdnjs.cloudflare.com/ajax/libs/raphael/*

  • Read and change your data on firebaseio.com

    https://*.firebaseio.com/*

  • Read and change your data on www.googleapis.com

    https://www.googleapis.com/*

  • Read and change your data on www.gstatic.com

    https://www.gstatic.com/*

  • Read and change your data on apis.google.com

    https://apis.google.com/*

  • Read and change your data on us-central1-test-proj-7d39d.cloudfunctions.net

    https://us-central1-test-proj-7d39d.cloudfunctions.net/*

  • See the address and title of every tab you have open

    tabs

  • Show you desktop notifications

    notifications

  • Read and change your full browsing history

    history

  • Run its own code inside the pages you visit

    scripting

  • Run hidden pages in the background

    offscreen

Updated 30 September 2026afahlliooeebnifondmbhcaghcapepbm