Is Browser Security Plus safe?

Medium risk

Browser Security Plus is medium risk. When admin policy enables upload monitoring, Browser Security Plus injects a script into every page replacing attachShadow, intercepting shadow DOM creation, including closed roots. toString() returns the native string to evade detection.

manageengine.comv3.2.11Chrome Web Store
45Risk
Who publishes it

manageengine.com - 3 other listings from the same operator, none carrying a finding

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

Publisher
manageengine.com

Same store account

3 other listings published from this account, 180k+ users between them, none of them carrying a finding.

Shared hosts - 1 hostname

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.

manageengine.com
Also called by 6 other listings, including ManageEngine PAM360

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI SANDBOX

Shadow DOM API Patched to Intercept Closed Shadow Root File Selections

When admin policy enables upload monitoring, Browser Security Plus injects a script into every page replacing attachShadow, intercepting shadow DOM creation, including closed roots. toString() returns the native string to evade detection.

01EvidenceCAUSE EFFECT
What actually happens
You did this

An administrator enables upload monitoring via the Browser Security Plus management policy.

The extension did this

Browser Security Plus injects a content script into the JavaScript main world of every page that replaces the browser's shadow DOM API with a patched version.

The replacement intercepts closed shadow roots, which are normally inaccessible to outside scripts, and attaches file-selection listeners inside them.

02EvidenceFIELD TABLE
File metadata read from every closed shadow root file input:
FieldValueWhy it matters
File name
Q4-board-deck-DRAFT.pdfThe exact filename of the file you are selecting for upload.
File size (bytes)
3145728The byte size of the file.
MIME type
application/pdfThe format type of the file, identifying whether it is a PDF, spreadsheet, image, and so on.
Last modified timestamp
1750422912000When the file was last changed on your device, in milliseconds since Unix epoch.
Page URL
https://app.example.com/documents/uploadThe URL of the page where the file selection occurred.
03EvidenceCODE COMPARE
The code that does this

The attachShadow replacement and its toString disguise (shadowhook.js, lines 24-50):

What it actually does
Annotated: what each section does
// (1) Save the real browser function before replacing it.
var originalAttachShadow = Element.prototype.attachShadow;

// (2) Replace the global function with a wrapper that runs in the page's
//     own JavaScript context (MAIN world). Every call from the page or
//     any framework goes through this wrapper.
Element.prototype.attachShadow = function(init) {
    var shadow = originalAttachShadow.call(this, init);  // call the real API
    allShadowRoots.set(this, shadow);                    // record it (open and closed)

    // (3) For closed roots: immediately attach change listeners so that
    //     file inputs inside them are visible to the extension.
    //     Normally a closed root returns null to all outside accessors;
    //     the WeakMap retains the reference anyway.
    if (init.mode === 'closed') {
        processShadowRoot(shadow);
    }
    return shadow;  // caller receives the real shadow root unmodified
};

// (4) Override toString on the wrapper via Object.defineProperty.
//     When JS frameworks (Lit, Polymer, etc.) call
//     Element.prototype.attachShadow.toString() to verify the function
//     has not been replaced, they receive the original native string
//     ('function attachShadow() { [native code] }') rather than the
//     wrapper source — concealing the replacement from those checks.
try {
    var nativeStr = originalAttachShadow.toString(); // capture original string once
    Object.defineProperty(Element.prototype.attachShadow, 'toString', {
        value: function() { return nativeStr; },     // always returns native string
        configurable: true
    });
} catch(e) {}
04EvidenceARTIFACT
Check if you're affected

Checks whether Browser Security Plus has replaced Element.prototype.attachShadow on the current page. Uses Function.prototype.toString.call() to bypass the extension's toString disguise and reveal the wrapper function source.

RequiresChrome with Browser Security Plus installed and upload monitoring policy active
bsp-shadowhook-detector.js · js
// bsp-shadowhook-detector.js
// Detects whether Browser Security Plus has replaced Element.prototype.attachShadow.
//
// The extension overrides attachShadow.toString() via Object.defineProperty so that
// calling Element.prototype.attachShadow.toString() returns the native browser string
// even though the function is a wrapper.
//
// Detection: Function.prototype.toString.call(fn) invokes the original
// Function.prototype.toString — bypassing any per-property toString override —
// and returns the true function source.

(function() {
  var fn = Element.prototype.attachShadow;
  if (!fn) {
    console.log('[BSP-DETECTOR] Element.prototype.attachShadow is not present in this browser.');
    return;
  }

  // Standard toString — may be overridden by the extension
  var visibleSource = fn.toString();
  // Function.prototype.toString — bypasses per-property override
  var realSource = Function.prototype.toString.call(fn);

  var isNative = /\[native code\]/.test(realSource);
  var hookFlagSet = !!window.__bspShadowHookInstalled;

  console.log('[BSP-DETECTOR] window.__bspShadowHookInstalled:', hookFlagSet);
  console.log('[BSP-DETECTOR] attachShadow.toString() (may be spoofed):', visibleSource);
  console.log('[BSP-DETECTOR] Function.prototype.toString.call(attachShadow):', realSource);
  console.log('[BSP-DETECTOR] Is native?', isNative);

  if (!isNative) {
    console.warn('[BSP-DETECTOR] PATCHED: Element.prototype.attachShadow has been replaced.');
    console.warn('[BSP-DETECTOR] The toString disguise is active: fn.toString() returns native string but Function.prototype.toString reveals the wrapper.');
  } else if (hookFlagSet) {
    console.warn('[BSP-DETECTOR] Idempotency guard is set but attachShadow appears native — unexpected state.');
  } else {
    console.log('[BSP-DETECTOR] NOT PATCHED: attachShadow is the native browser function.');
  }
})();
How to run it
  1. 1
    Open any page in Chrome with Browser Security Plus installed.
  2. 2
    Open DevTools (F12) and switch to the Console tab.
  3. 3
    Paste and run this script. It prints whether the native API has been patched and, if so, shows the wrapper source.
Updated 30 September 2026mhkgegcmcapcgmnnloigolapjkajgfmd