Is Dux-Soup for LinkedIn Automation safe?

High risk

Dux-Soup is high risk. With Dux-Soup installed, two page scripts wrap LinkedIn's XHR and Fetch calls, copying matching API responses into a CustomEvent named datachannel that the content script reads, exposing profile, search, mailbox, and messaging data.…

dux-soupv10.2.31Chrome Web Store
75Risk
Who publishes it

Supertec B.V. - no other listings under this identity, 2 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
dux-soup
Declared legal entity
Supertec B.V.
Registered address
Craenlaer, 14, Ulvenhout 4851 TK, NL
Registered contact
WPC van der Sanden

Shared hosts - 2 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

app.crelate.com
Also called by 3 other listings, including Crelate Talent Chrome Extension
sb.scorecardresearch.com
Also called by 6 other listings, including Ahrefs SEO Toolbar, Notifier for Yahoo Mail

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

Publishers can request a review.

Findings

LinkedIn API responses forwarded through page events

With Dux-Soup installed, two page scripts wrap LinkedIn's XHR and Fetch calls, copying matching API responses into a CustomEvent named datachannel that the content script reads, exposing profile, search, mailbox, and messaging data.

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

You browse LinkedIn while the extension is installed.

The manifest runs client.js at document start on LinkedIn pages.

The extension did this

The extension inserts page scripts that copy matching LinkedIn API responses into a document event.

The event is named datachannel and contains the response body, content type, and response URL.

Fields copied into the datachannel event
  • LinkedIn response body
    JSON response from https://www.linkedin.com/voyager/api/ (illustrative)

    Lets the extension read data LinkedIn returned to your browser: profile, search, mailbox, messaging, Sales Navigator, or Recruiter records.

  • Response URL
    https://www.linkedin.com/sales-api/salesApiMailbox

    Shows which LinkedIn area or record was loaded during your browsing session.

  • Response type
    application/json

    Tells the extension whether the response was JSON or text before it forwards the copied body.

The code that does this

XHR wrapper copies matching LinkedIn responses into a document event

Readable version

Same function in the deobfuscated tree

xhrforwarder.js
const blocktarget = /\/(xhrforwarder|fetchforwarder|navoverride)\.js/function XHRForwarder(open){    XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {        var targets = [ "www.linkedin.com/communities-api/v1/memberships/community",                        "www.linkedin.com/sales/search/results",                        "www.linkedin.com/voyager/api/",                        "www.linkedin.com/connected/api/v2/contacts",                        "www.linkedin.com/connected/api/v1/contacts",                        "www.linkedin.com/sales-api/salesApiPeopleSearch",                        "www.linkedin.com/sales-api/salesApiLeadSearch",                        "www.linkedin.com/sales-api/salesApiCoach",                        "www.linkedin.com/recruiter/api/smartsearch",                        "www.linkedin.com/talent/search/api",                        "www.linkedin.com/talent/api",                        "www.linkedin.com/sales/profile/",                        "www.linkedin.com/sales-api/salesApiProfiles",                        "www.linkedin.com/sales-api/salesApiMessagingThreads",                        "www.linkedin.com/sales-api/salesApiMailbox"];        this.addEventListener("readystatechange", function() {            if (this.readyState == 4 ) {                var resp={};                try{                    resp.data = this.response;                    resp.type = this.responseType;                    resp.url  = this.responseURL;                    if (  typeof(resp.url)!="undefined" &&                            targets.filter(target => resp.url.indexOf(target) >-1 ).length >0                        ){                        let resevent = new CustomEvent("datachannel", {detail:resp});                        document.dispatchEvent(resevent);                    }                }catch(e){                }            }        }, false);        if ((""+url).match(blocktarget)){            url="chrome-extension://invalid/";        }        open.call(this, method, url, async, user, pass);    };};(XHRForwarder)(XMLHttpRequest.prototype.open);
The code that does this

Fetch wrapper copies JSON or text responses before returning the original response

Readable version

Same function in the deobfuscated tree

fetchforwarder.js
const blocktarget = /chrome-extension:\/\/(.*?)\/(xhrforwarder|fetchforwarder|navoverride)\.js/function FETCHForwarder(fetchproto){    const targets = [ "www.linkedin.com/communities-api/v1/memberships/community",    "www.linkedin.com/sales/search/results",    "www.linkedin.com/voyager/api/",    "www.linkedin.com/connected/api/v2/contacts",    "www.linkedin.com/connected/api/v1/contacts",    "www.linkedin.com/sales-api/salesApiPeopleSearch",    "www.linkedin.com/sales-api/salesApiLeadSearch",    "www.linkedin.com/sales-api/salesApiCoach",    "www.linkedin.com/recruiter/api/smartsearch",    "www.linkedin.com/talent/api",    "www.linkedin.com/sales/profile/",    "www.linkedin.com/sales-api/salesApiProfiles",    "www.linkedin.com/sales-api/salesApiMessagingThreads",    "www.linkedin.com/sales-api/salesApiMailbox",    "www.linkedin.com/flagship-web/in"];    fetch = function(url, init){        if ((""+ url).match(blocktarget)){            url="chrome-extension://invalid/";        }        return fetchproto(url, init).then(async function(res){            if ( typeof(res.url)!="undefined" && targets.filter(target => res.url.indexOf(target) > -1 ).length >0 ){                let resp={};                resp.type = res.headers.get("content-type");                resp.url  = res.url;                try{                    if (resp.type=="application/octet-stream"){                        resp.data = await res.clone().text();                    }else{                                                resp.data = await res.clone().json();                    }                    let resevent = new CustomEvent("datachannel", {detail:resp});                    setTimeout(function(){                        document.dispatchEvent(resevent);                                    }, 1000);                }catch(e){                    // ignore, not json or text                }            }            return res;        }).catch((error)=>{            //ignore        });    }}(FETCHForwarder)(fetch);
The code that does this

Content script inserts the forwarder scripts into the LinkedIn page

Readable version

Same functions in the deobfuscated tree

client.js
function inject(filename,payload){let s=document.createElement("script");s.src=chrome.runtime.getURL(filename);if(payload){s.src+="?pl="+encodeURIComponent(JSON.stringify(payload))}s.onload=function(){this.remove()};(document.head||document.documentElement).appendChild(s)}function injectXHRForwarder(){inject("xhrforwarder.js")}function injectFETCHForwarder(){inject("fetchforwarder.js")}function injectNAVOverride(){inject("navoverride.js")}
Hosts named in the confirmed code path
    • www.linkedin.com

    Source of the matched API responses copied into datachannel events.

    • app.dux-soup.com

    Dux-Soup service host present in extension permissions and service-worker code; the forwarder scripts do not send directly to this host.

Cloud edition disables the standard Dux-Soup extension

Running as the Dux-Soup cloud or enterprise edition, this code checks whether the standard Dux-Soup extension is installed and enabled.

If present, the service worker calls Chrome's management API to turn that sibling extension off.

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

You install or start the cloud or enterprise edition while the standard Dux-Soup extension is also installed.

The extension did this

The extension checks for the standard edition and asks Chrome to disable it when it is enabled.

Control values used by the sibling-extension check
  • Current edition ID
    nihpcockefpdcdfakndgdkggodikeldc

    This value decides whether the disable routine runs at all.

  • Target extension ID
    ppdakpfeaodfophjplfdedpcodkdkbal

    This is the standard Dux-Soup extension that the code asks Chrome to look up.

  • Enabled flag
    extinfo.enabled === true

    The sibling extension is only turned off when Chrome reports that it is currently enabled.

  • Management permission
    "management"

    This browser permission lets an extension inspect and change the enabled state of other installed extensions.

The code that does this

The service-worker function that disables the sibling edition

Readable version

Readable service-worker code

sw.js
function checkAndDisableRegularDS() {  try {    chrome.management.get("ppdakpfeaodfophjplfdedpcodkdkbal", function(extinfo) {      if (extinfo != null && extinfo.enabled) {        chrome.management.setEnabled(extinfo.id, false)      } else if (chrome.runtime.lastError) {}    })  } catch (e) {}}if (chrome.runtime.id == "nihpcockefpdcdfakndgdkggodikeldc") {  checkAndDisableRegularDS()}

Readable manifest permission excerpt

manifest.json
permissions: [  "tabs",  "identity",  "webRequest",  "declarativeNetRequest",  "power",  "idle",  "downloads",  "storage",  "management",  "cookies",  "offscreen"]
Updated 30 September 2026ppdakpfeaodfophjplfdedpcodkdkbal