Is Browser Security Plus safe?
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.
Who publishes itmanageengine.com - 3 other listings from the same operator, none carrying a finding
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.
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.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
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.
An administrator enables upload monitoring via the Browser Security Plus management policy.
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.
| Field | Value | Why it matters | |
|---|---|---|---|
File name | Q4-board-deck-DRAFT.pdf | The exact filename of the file you are selecting for upload. | |
File size (bytes) | 3145728 | The byte size of the file. | |
MIME type | application/pdf | The format type of the file, identifying whether it is a PDF, spreadsheet, image, and so on. | |
Last modified timestamp | 1750422912000 | When the file was last changed on your device, in milliseconds since Unix epoch. | |
Page URL | https://app.example.com/documents/upload | The URL of the page where the file selection occurred. |
The attachShadow replacement and its toString disguise (shadowhook.js, lines 24-50):
// (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) {}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.
// 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.');
}
})();- 1Open any page in Chrome with Browser Security Plus installed.
- 2Open DevTools (F12) and switch to the Console tab.
- 3Paste and run this script. It prints whether the native API has been patched and, if so, shows the wrapper source.