Online Tools Toolshu.com Log In Sign Up

How to Detect Global Scope Pollution by Third-Party JavaScript Scripts – Practical Monitoring of window Variable Changes

Original Author:bhnw Edited on 2025-11-10 15:49 25 views Star (0)

In modern web development, we often load third-party scripts—such as ads, analytics, SDKs, and plugins. For the sake of "convenience," these scripts frequently inject variables and functions directly into the global scope (window object), or even modify native objects. This practice not only pollutes the global namespace, but can also lead to variable conflicts, performance degradation, security risks, and even silent tampering with critical APIs (e.g., overriding console.log or XMLHttpRequest).

So, as developers, how can we accurately identify exactly what a third-party script adds to or changes in the window object? In this article, we’ll walk you through building a lightweight yet reliable monitoring solution.


1. Problem Scenario

Suppose we want to load an ad script:

<script src="https://cdn.wwads.cn/js/makemoney.js  "></script>

We’d like to know:

  • What new global variables did it introduce?
  • Did it modify any existing global properties (e.g., Array, Promise, or custom variables)?

Relying solely on Object.keys(window).length only tells us the count has changed—it doesn’t reveal what specifically changed. We need a more granular comparison.


2. Core Approach: Snapshot + Differential Comparison

The most straightforward way to detect “changes” is:

  1. Take a complete snapshot of window (property names + values) before loading the script.
  2. Take another snapshot after the script loads.
  3. Compare the two snapshots to identify newly added properties and properties whose values have changed.

Key considerations:

  • Use Object.getOwnPropertyNames(window) to capture all properties, including non-enumerable ones.
  • Store the property-value mappings in a Map for efficient lookups.
  • Use Object.is() for value comparison to properly handle edge cases like NaN and -0/+0.
  • Wrap access in try...catch to avoid exceptions when reading certain special properties.

3. Full Implementation Code

// 1. Create a window snapshot: record all properties and their current values
function snapshotWindow() {
  const props = Object.getOwnPropertyNames(window);
  const snap = new Map();
  for (const key of props) {
    try {
      snap.set(key, window[key]);
    } catch (e) {
      // Accessing certain properties (e.g., opener in cross-origin iframes) may throw errors
      snap.set(key, '<ACCESS_ERROR>');
    }
  }
  return snap;
}

// 2. Record the state before loading
const beforeSnapshot = snapshotWindow();

// 3. Dynamically load the third-party script
const script = document.createElement('script');
script.src = 'https://cdn.wwads.cn/js/makemoney.js  ';

// 4. Perform comparison after the script loads
script.onload = () => {
  // Add a small delay to ensure the script finishes executing (including microtasks)
  setTimeout(() => {
    const afterSnapshot = snapshotWindow();

    const added = [];
    const modified = [];

    // Iterate over all properties after loading
    for (const [key, newValue] of afterSnapshot) {
      if (!beforeSnapshot.has(key)) {
        added.push(key); // Newly added
      } else {
        const oldValue = beforeSnapshot.get(key);
        if (!Object.is(oldValue, newValue)) {
          modified.push({ key, oldValue, newValue }); // Modified
        }
      }
    }

    console.log('🟢 New global variables:', added);
    console.log('🟡 Modified global variables:', modified);
  }, 100);
};

script.onerror = () => console.error('Failed to load third-party script');
document.head.appendChild(script);

4. Example Output and Interpretation

After running the above code, your console might show:

🟢 New global variables: ['_AdBlockInit', '_IsTrustedClick']
🟡 Modified global variables: [
  { key: "isMobile", oldValue: [Function], newValue: [Object] }
]

This means:

  • The script injected ad-related variables like _AdBlockInit;
  • It likely overwrote the isMobile object.

Such behavior is not uncommon in ad or data-collection scripts, but it poses potential threats to your application’s stability and security.


5. Caveats and Advanced Tips

1. False Positives from Dynamic Properties

Some window properties are getters (e.g., window.devicePixelRatio), and their values may differ on each access. These can be mistakenly flagged as “modified.” You can avoid this by maintaining a whitelist:

const DYNAMIC_PROPS = new Set(['devicePixelRatio', 'innerWidth', 'innerHeight', 'scrollY']);
if (DYNAMIC_PROPS.has(key)) return; // Skip comparison

2. Deep Mutations Go Undetected

If a script only modifies internal properties of an object (e.g., window.myLib.config = {...}) without changing the object reference itself, the change won’t be detected. For deep monitoring, you’d need techniques like Proxy or recursive traversal—but these come with higher performance costs.

3. Use with Caution in Production

This approach is best suited for development debugging or security audits. Avoid running it continuously in production, as iterating over the entire window object incurs noticeable performance overhead.

4. Combine with CSP and Sandboxing

For high-security applications, we recommend:

  • Enforcing a Content Security Policy (CSP) to restrict script sources;
  • Running third-party scripts inside a sandboxed iframe to fully isolate them from the global scope.

5. Isolation Strategy: Auto-Restore Critical Globals After Execution

If you know a third-party script overwrites specific global variables (e.g., isMobile or config) but must load it anyway, you can use a “backup-and-restore” approach to automatically revert those variables after the script runs, containing its side effects:

function loadThirdPartyScript(src, backupGlobals = []) {
  const backup = {};
  backupGlobals.forEach(key => {
    backup[key] = window[key];
  });

  const script = document.createElement('script');
  script.src = src;
  script.async = true;

  script.onload = script.onerror = () => {
    backupGlobals.forEach(key => {
      window[key] = backup[key];
    });
  };

  document.head.appendChild(script);
}

// Usage example
loadThirdPartyScript('https://cdn.wwads.cn/js/makemoney.js ', ['isMobile', 'config']);

⚠️ Note: This technique only works if the script applies its global modifications synchronously during initial execution. If the script uses setTimeout, event listeners, or async callbacks to mutate globals later, the restore may not be effective.


6. Conclusion

Third-party scripts are essentially “black boxes”—we can’t control their implementation, but we can actively monitor their side effects. With a simple snapshot-comparison technique, you gain clear visibility into how they “intrude” on your global environment, providing valuable insights for performance tuning, security audits, and troubleshooting.

Next time you consider loading an unfamiliar script, ask yourself: “What exactly are you writing to my window?”


💡 Pro Tip: Wrap the logic above into a reusable function like detectGlobalPollution(scriptUrl) to easily audit any third-party script.

发现周边 发现周边
Comment area

Loading...

红包