Back to blog
InspectionJanuary 1, 2024Β· 15 min read

Third-party Scripts and Privacy Analysis: Track the Trackers

A practical Fusebox guide to third-party scripts and privacy analysis.

Third-party Scripts and Privacy Analysis: Track the Trackers

Published: January 2024
Reading time: 10 minutes

Modern websites load dozens of third-party scriptsβ€”analytics, ads, widgets, fonts, CDNs. Each script can track users, slow performance, and introduce security vulnerabilities. Here's how to detect all third-party scripts, analyze their privacy impact, and protect user data.

Why Third-party Analysis Matters

The Hidden Cost

  • Average site loads 20+ third-party domains
  • 70% of web traffic goes to third parties
  • Each script can access all page data
  • GDPR violations from unauthorized tracking
  • Supply chain attacks through compromised scripts

Real Privacy Incidents

British Airways (2018): Magecart attack via third-party script
Ticketmaster (2018): Customer data stolen through chat widget
Forbes (2019): Ad network served cryptomining scripts
CNN (2020): 30+ trackers found on single article
Target (2021): Analytics script leaked purchase data

Quick Third-party Detection

1. Third-party Script Scanner

// Detect and analyze all third-party scripts
(function scanThirdPartyScripts() {
  console.log('πŸ” Third-party Script Scanner\n');
  
  const firstPartyDomain = window.location.hostname;
  const thirdPartyScripts = new Map();
  const trackerCategories = {
    analytics: ['google-analytics.com', 'googletagmanager.com', 'segment.com', 'mixpanel.com', 'amplitude.com', 'heap.io'],
    advertising: ['doubleclick.net', 'googlesyndication.com', 'amazon-adsystem.com', 'facebook.com', 'outbrain.com'],
    social: ['facebook.com', 'twitter.com', 'linkedin.com', 'pinterest.com', 'instagram.com'],
    customer_support: ['intercom.io', 'drift.com', 'zendesk.com', 'helpscout.net', 'crisp.chat'],
    cdn: ['cloudflare.com', 'fastly.net', 'akamaihd.net', 'cloudfront.net', 'jsdelivr.net'],
    fonts: ['fonts.googleapis.com', 'fonts.gstatic.com', 'typekit.net', 'use.typekit.net'],
    video: ['youtube.com', 'vimeo.com', 'wistia.com', 'brightcove.com'],
    payments: ['stripe.com', 'paypal.com', 'square.com', 'checkout.com'],
    monitoring: ['sentry.io', 'bugsnag.com', 'rollbar.com', 'logrocket.com']
  };
  
  // Scan all scripts
  const scripts = document.querySelectorAll('script[src]');
  scripts.forEach(script => {
    const url = new URL(script.src);
    const domain = url.hostname;
    
    // Check if third-party
    if (!domain.includes(firstPartyDomain) && domain !== 'localhost') {
      if (!thirdPartyScripts.has(domain)) {
        thirdPartyScripts.set(domain, {
          urls: [],
          category: 'unknown',
          privacyRisk: 'unknown',
          performance: { count: 0, totalSize: 0 }
        });
      }
      
      const scriptInfo = thirdPartyScripts.get(domain);
      scriptInfo.urls.push(url.href);
      scriptInfo.performance.count++;
      
      // Categorize
      for (const [category, domains] of Object.entries(trackerCategories)) {
        if (domains.some(d => domain.includes(d))) {
          scriptInfo.category = category;
          break;
        }
      }
      
      // Assess privacy risk
      scriptInfo.privacyRisk = assessPrivacyRisk(domain, scriptInfo.category);
    }
  });
  
  // Also scan for dynamically loaded scripts
  scanDynamicScripts(thirdPartyScripts);
  
  // Analyze iframes
  const iframes = document.querySelectorAll('iframe[src]');
  iframes.forEach(iframe => {
    try {
      const url = new URL(iframe.src);
      const domain = url.hostname;
      
      if (!domain.includes(firstPartyDomain)) {
        if (!thirdPartyScripts.has(domain)) {
          thirdPartyScripts.set(domain, {
            urls: [],
            category: 'iframe',
            privacyRisk: 'high',
            performance: { count: 1, totalSize: 0 }
          });
        }
        thirdPartyScripts.get(domain).urls.push(`[iframe] ${url.href}`);
      }
    } catch (e) {
      // Invalid URL
    }
  });
  
  // Display results
  console.log(`πŸ“Š Found ${thirdPartyScripts.size} third-party domains\n`);
  
  // Group by category
  const byCategory = {};
  thirdPartyScripts.forEach((info, domain) => {
    if (!byCategory[info.category]) {
      byCategory[info.category] = [];
    }
    byCategory[info.category].push({ domain, ...info });
  });
  
  // Display by category
  Object.entries(byCategory).forEach(([category, scripts]) => {
    console.log(`\nπŸ“ ${category.toUpperCase()} (${scripts.length})`);
    console.log('─'.repeat(40));
    
    scripts.forEach(script => {
      const riskIcon = script.privacyRisk === 'high' ? 'πŸ”΄' :
                      script.privacyRisk === 'medium' ? '🟑' : '🟒';
      
      console.log(`${riskIcon} ${script.domain}`);
      console.log(`   Scripts: ${script.performance.count}`);
      console.log(`   Privacy risk: ${script.privacyRisk}`);
      
      // Show sample URLs
      if (script.urls.length > 0) {
        console.log(`   Sample: ${script.urls[0].substring(0, 60)}...`);
      }
    });
  });
  
  // Privacy summary
  const highRiskCount = Array.from(thirdPartyScripts.values())
    .filter(s => s.privacyRisk === 'high').length;
  
  console.log('\n⚠️ Privacy Risk Summary:');
  console.log(`   High risk scripts: ${highRiskCount}`);
  console.log(`   Total third parties: ${thirdPartyScripts.size}`);
  
  if (highRiskCount > 5) {
    console.log('\n🚨 PRIVACY WARNING: Too many high-risk trackers!');
  }
  
  return thirdPartyScripts;
})();

function assessPrivacyRisk(domain, category) {
  // Known high-risk domains
  const highRiskDomains = [
    'doubleclick.net', 'facebook.com', 'google-analytics.com',
    'scorecardresearch.com', 'quantserve.com', 'outbrain.com',
    'taboola.com', 'amazon-adsystem.com', 'criteo.com'
  ];
  
  if (highRiskDomains.some(d => domain.includes(d))) {
    return 'high';
  }
  
  // Category-based risk
  if (['analytics', 'advertising', 'social'].includes(category)) {
    return 'high';
  }
  
  if (['customer_support', 'monitoring'].includes(category)) {
    return 'medium';
  }
  
  return 'low';
}

function scanDynamicScripts(thirdPartyScripts) {
  // Monitor for dynamically added scripts
  const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
      mutation.addedNodes.forEach(node => {
        if (node.tagName === 'SCRIPT' && node.src) {
          try {
            const url = new URL(node.src);
            const domain = url.hostname;
            
            if (!domain.includes(window.location.hostname)) {
              console.log(`πŸ†• Dynamic script loaded: ${domain}`);
            }
          } catch (e) {
            // Invalid URL
          }
        }
      });
    });
  });
  
  observer.observe(document.body, {
    childList: true,
    subtree: true
  });
  
  // Stop observing after 5 seconds
  setTimeout(() => observer.disconnect(), 5000);
}

2. Privacy Impact Analyzer

// Analyze privacy impact of detected scripts
function analyzePrivacyImpact() {
  console.log('\nπŸ” Privacy Impact Analysis\n');
  
  const privacyViolations = {
    cookies: [],
    localStorage: [],
    fingerprinting: [],
    tracking: []
  };
  
  // 1. Cookie Analysis
  console.log('πŸͺ Third-party Cookie Analysis:');
  
  const cookies = document.cookie.split(';');
  const thirdPartyCookies = new Set();
  
  // Check for known tracking cookies
  const trackingCookies = ['_ga', '_gid', '__utma', '_fbp', 'fr', 'IDE', 'NID', '_pinterest_sess'];
  
  cookies.forEach(cookie => {
    const [name, value] = cookie.trim().split('=');
    
    if (trackingCookies.some(tc => name.includes(tc))) {
      thirdPartyCookies.add(name);
      privacyViolations.cookies.push({
        name: name,
        type: 'tracking',
        service: guessServiceFromCookie(name)
      });
    }
  });
  
  console.log(`  Found ${thirdPartyCookies.size} tracking cookies`);
  thirdPartyCookies.forEach(cookie => {
    console.log(`  πŸͺ ${cookie}`);
  });
  
  // 2. LocalStorage Analysis
  console.log('\nπŸ’Ύ LocalStorage Privacy Check:');
  
  let suspiciousStorageItems = 0;
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    
    // Check for tracking-related keys
    if (key.includes('track') || key.includes('analytics') || 
        key.includes('user') || key.includes('visitor') ||
        key.includes('session') || key.includes('fingerprint')) {
      suspiciousStorageItems++;
      privacyViolations.localStorage.push(key);
      console.log(`  ⚠️ Suspicious key: ${key}`);
    }
  }
  
  if (suspiciousStorageItems === 0) {
    console.log('  βœ… No obvious tracking data in localStorage');
  }
  
  // 3. Fingerprinting Detection
  console.log('\nπŸ–οΈ Browser Fingerprinting Detection:');
  
  const fingerprintingSignals = {
    canvas: detectCanvasFingerprinting(),
    webgl: detectWebGLFingerprinting(),
    audio: detectAudioFingerprinting(),
    fonts: detectFontFingerprinting()
  };
  
  Object.entries(fingerprintingSignals).forEach(([type, detected]) => {
    if (detected) {
      console.log(`  ❌ ${type} fingerprinting detected`);
      privacyViolations.fingerprinting.push(type);
    } else {
      console.log(`  βœ… No ${type} fingerprinting detected`);
    }
  });
  
  // 4. Network Request Analysis
  console.log('\nπŸ“‘ Tracking Request Analysis:');
  
  // Monitor fetch/XHR for tracking
  let trackingRequests = 0;
  const trackingDomains = [
    'google-analytics.com', 'googletagmanager.com', 'facebook.com',
    'doubleclick.net', 'amazon-adsystem.com', 'segment.io'
  ];
  
  // Override fetch to monitor
  const originalFetch = window.fetch;
  window.fetch = function(...args) {
    const url = args[0];
    
    if (trackingDomains.some(domain => url.includes(domain))) {
      trackingRequests++;
      privacyViolations.tracking.push(url);
      console.log(`  πŸ“€ Tracking request: ${new URL(url).hostname}`);
    }
    
    return originalFetch.apply(this, args);
  };
  
  // 5. GDPR Compliance Check
  console.log('\nβš–οΈ GDPR Compliance Check:');
  
  const gdprChecks = {
    consentBanner: document.querySelector('[class*="cookie"], [class*="consent"], [class*="gdpr"]'),
    privacyPolicy: document.querySelector('a[href*="privacy"], a[href*="datenschutz"]'),
    optOut: document.querySelector('[class*="opt-out"], [class*="decline"]')
  };
  
  console.log(`  Consent banner: ${gdprChecks.consentBanner ? 'βœ… Found' : '❌ Not found'}`);
  console.log(`  Privacy policy link: ${gdprChecks.privacyPolicy ? 'βœ… Found' : '❌ Not found'}`);
  console.log(`  Opt-out mechanism: ${gdprChecks.optOut ? 'βœ… Found' : '❌ Not found'}`);
  
  // Privacy Score
  const privacyScore = calculatePrivacyScore(privacyViolations, gdprChecks);
  
  console.log('\nπŸ“Š Privacy Score:');
  console.log(`  ${privacyScore}/100`);
  
  if (privacyScore < 50) {
    console.log('\n🚨 POOR PRIVACY PRACTICES DETECTED');
    console.log('This site may not be compliant with privacy regulations');
  }
  
  return privacyViolations;
}

function guessServiceFromCookie(cookieName) {
  const cookieMap = {
    '_ga': 'Google Analytics',
    '_gid': 'Google Analytics',
    '_fbp': 'Facebook',
    'fr': 'Facebook',
    'IDE': 'Google Ads',
    'NID': 'Google',
    '_pinterest_sess': 'Pinterest'
  };
  
  for (const [cookie, service] of Object.entries(cookieMap)) {
    if (cookieName.includes(cookie)) {
      return service;
    }
  }
  
  return 'Unknown';
}

function detectCanvasFingerprinting() {
  // Check if canvas is being used for fingerprinting
  const canvasElements = document.querySelectorAll('canvas');
  let suspicious = false;
  
  canvasElements.forEach(canvas => {
    const ctx = canvas.getContext('2d');
    if (ctx && canvas.width > 0 && canvas.height > 0 && canvas.style.display === 'none') {
      suspicious = true;
    }
  });
  
  // Check for canvas toDataURL calls (common in fingerprinting)
  const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
  let toDataURLCalled = false;
  
  HTMLCanvasElement.prototype.toDataURL = function(...args) {
    toDataURLCalled = true;
    return originalToDataURL.apply(this, args);
  };
  
  return suspicious || toDataURLCalled;
}

function detectWebGLFingerprinting() {
  // Check for WebGL fingerprinting
  const canvas = document.createElement('canvas');
  const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
  
  if (gl) {
    const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
    if (debugInfo) {
      // If scripts are accessing GPU info, likely fingerprinting
      return true;
    }
  }
  
  return false;
}

function detectAudioFingerprinting() {
  // Check if AudioContext is being used (common for fingerprinting)
  return window.AudioContext && window.OfflineAudioContext;
}

function detectFontFingerprinting() {
  // Check for font enumeration attempts
  const testString = 'mmmmmmmmmmlli';
  const testSize = '72px';
  const baseFonts = ['monospace', 'sans-serif', 'serif'];
  
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  
  // If many fonts are being tested, likely fingerprinting
  let fontsChecked = 0;
  const fonts = ['Arial', 'Verdana', 'Times New Roman', 'Courier New'];
  
  fonts.forEach(font => {
    ctx.font = `${testSize} ${font}`;
    fontsChecked++;
  });
  
  return fontsChecked > 10; // Arbitrary threshold
}

function calculatePrivacyScore(violations, gdprChecks) {
  let score = 100;
  
  // Deduct for violations
  score -= violations.cookies.length * 5;
  score -= violations.localStorage.length * 3;
  score -= violations.fingerprinting.length * 10;
  score -= violations.tracking.length * 2;
  
  // Deduct for missing GDPR compliance
  if (!gdprChecks.consentBanner) score -= 20;
  if (!gdprChecks.privacyPolicy) score -= 10;
  if (!gdprChecks.optOut) score -= 10;
  
  return Math.max(0, score);
}

analyzePrivacyImpact();

3. Performance Impact Monitor

// Monitor performance impact of third-party scripts
class ThirdPartyPerformanceMonitor {
  constructor() {
    this.metrics = {
      totalScripts: 0,
      totalSize: 0,
      totalLoadTime: 0,
      blockingScripts: 0,
      byDomain: new Map()
    };
    
    this.startMonitoring();
  }
  
  startMonitoring() {
    console.log('\n⚑ Third-party Performance Monitor\n');
    
    // Analyze existing scripts
    const resources = performance.getEntriesByType('resource');
    
    resources.forEach(resource => {
      if (resource.initiatorType === 'script') {
        const url = new URL(resource.name);
        const isThirdParty = !url.hostname.includes(window.location.hostname);
        
        if (isThirdParty) {
          this.recordScript(url.hostname, resource);
        }
      }
    });
    
    // Monitor new scripts
    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        if (entry.initiatorType === 'script') {
          const url = new URL(entry.name);
          const isThirdParty = !url.hostname.includes(window.location.hostname);
          
          if (isThirdParty) {
            this.recordScript(url.hostname, entry);
          }
        }
      });
    });
    
    observer.observe({ entryTypes: ['resource'] });
    
    // Report after delay
    setTimeout(() => this.report(), 5000);
  }
  
  recordScript(domain, resource) {
    this.metrics.totalScripts++;
    this.metrics.totalSize += resource.transferSize || 0;
    this.metrics.totalLoadTime += resource.duration || 0;
    
    // Check if render blocking
    if (resource.renderBlockingStatus === 'blocking') {
      this.metrics.blockingScripts++;
    }
    
    // Track by domain
    if (!this.metrics.byDomain.has(domain)) {
      this.metrics.byDomain.set(domain, {
        count: 0,
        totalSize: 0,
        totalTime: 0,
        scripts: []
      });
    }
    
    const domainMetrics = this.metrics.byDomain.get(domain);
    domainMetrics.count++;
    domainMetrics.totalSize += resource.transferSize || 0;
    domainMetrics.totalTime += resource.duration || 0;
    domainMetrics.scripts.push(resource.name);
  }
  
  report() {
    console.log('πŸ“Š Third-party Performance Report');
    console.log('─'.repeat(50));
    
    console.log(`Total third-party scripts: ${this.metrics.totalScripts}`);
    console.log(`Total size: ${(this.metrics.totalSize / 1024 / 1024).toFixed(2)}MB`);
    console.log(`Total load time: ${this.metrics.totalLoadTime.toFixed(0)}ms`);
    console.log(`Render-blocking scripts: ${this.metrics.blockingScripts}`);
    
    console.log('\nπŸ“ˆ Top Third-party Domains by Impact:');
    
    // Sort by total time
    const sortedDomains = Array.from(this.metrics.byDomain.entries())
      .sort((a, b) => b[1].totalTime - a[1].totalTime)
      .slice(0, 10);
    
    sortedDomains.forEach(([domain, metrics]) => {
      console.log(`\n${domain}:`);
      console.log(`  Scripts: ${metrics.count}`);
      console.log(`  Size: ${(metrics.totalSize / 1024).toFixed(0)}KB`);
      console.log(`  Time: ${metrics.totalTime.toFixed(0)}ms`);
      console.log(`  Avg time/script: ${(metrics.totalTime / metrics.count).toFixed(0)}ms`);
    });
    
    // Performance recommendations
    console.log('\nπŸ’‘ Performance Recommendations:');
    
    if (this.metrics.blockingScripts > 0) {
      console.log('1. Add async/defer to non-critical scripts');
    }
    
    if (this.metrics.totalScripts > 20) {
      console.log('2. Reduce number of third-party scripts');
    }
    
    if (this.metrics.totalSize > 1024 * 1024) {
      console.log('3. Third-party scripts exceed 1MB - optimize or remove');
    }
    
    const slowDomains = Array.from(this.metrics.byDomain.entries())
      .filter(([_, metrics]) => metrics.totalTime > 1000);
    
    if (slowDomains.length > 0) {
      console.log('4. Consider removing slow third-party services:');
      slowDomains.forEach(([domain]) => {
        console.log(`   - ${domain}`);
      });
    }
    
    console.log('5. Implement Content Security Policy');
    console.log('6. Use Subresource Integrity for third-party scripts');
  }
}

// Start monitoring
new ThirdPartyPerformanceMonitor();

Security Analysis

1. Third-party Security Scanner

// Scan for security issues in third-party scripts
async function scanThirdPartySecurity() {
  console.log('\nπŸ”’ Third-party Security Scanner\n');
  
  const securityIssues = {
    noSRI: [],
    httpScripts: [],
    outdatedLibraries: [],
    maliciousPatterns: [],
    permissions: []
  };
  
  // 1. Check Subresource Integrity
  console.log('πŸ” Subresource Integrity Check:');
  
  const externalScripts = document.querySelectorAll('script[src*="://"]');
  let scriptsWithSRI = 0;
  
  externalScripts.forEach(script => {
    const src = script.getAttribute('src');
    const integrity = script.getAttribute('integrity');
    const crossorigin = script.getAttribute('crossorigin');
    
    if (!src.includes(window.location.hostname)) {
      if (integrity) {
        scriptsWithSRI++;
      } else {
        securityIssues.noSRI.push(src);
      }
    }
  });
  
  console.log(`  Scripts with SRI: ${scriptsWithSRI}/${externalScripts.length}`);
  
  if (securityIssues.noSRI.length > 0) {
    console.log('  ⚠️ Scripts without SRI:');
    securityIssues.noSRI.slice(0, 5).forEach(script => {
      console.log(`    - ${new URL(script).hostname}`);
    });
  }
  
  // 2. Check for HTTP scripts on HTTPS page
  console.log('\nπŸ”“ Mixed Content Check:');
  
  if (window.location.protocol === 'https:') {
    const httpScripts = Array.from(document.scripts).filter(script => 
      script.src && script.src.startsWith('http://')
    );
    
    if (httpScripts.length > 0) {
      console.log(`  ❌ ${httpScripts.length} scripts loaded over HTTP!`);
      httpScripts.forEach(script => {
        securityIssues.httpScripts.push(script.src);
      });
    } else {
      console.log('  βœ… All scripts use HTTPS');
    }
  }
  
  // 3. Check for known vulnerable libraries
  console.log('\nπŸ“š Vulnerable Library Detection:');
  
  const vulnerableVersions = {
    'jquery': {
      vulnerable: ['1.', '2.'],
      secure: '3.5.0+'
    },
    'angular': {
      vulnerable: ['1.0', '1.1', '1.2', '1.3', '1.4', '1.5'],
      secure: '1.6.0+'
    },
    'bootstrap': {
      vulnerable: ['3.'],
      secure: '4.0+'
    }
  };
  
  // Check global objects for library versions
  Object.entries(vulnerableVersions).forEach(([library, versions]) => {
    let version = null;
    
    if (library === 'jquery' && window.jQuery) {
      version = window.jQuery.fn.jquery;
    } else if (library === 'angular' && window.angular) {
      version = window.angular.version?.full;
    }
    
    if (version) {
      const isVulnerable = versions.vulnerable.some(v => version.startsWith(v));
      
      if (isVulnerable) {
        console.log(`  ❌ ${library} ${version} has known vulnerabilities`);
        securityIssues.outdatedLibraries.push({ library, version });
      } else {
        console.log(`  βœ… ${library} ${version} appears secure`);
      }
    }
  });
  
  // 4. Check for malicious patterns
  console.log('\n🚨 Malicious Pattern Detection:');
  
  const maliciousPatterns = [
    { pattern: /eval\s*\(/, description: 'eval() usage' },
    { pattern: /document\.write/, description: 'document.write()' },
    { pattern: /innerHTML\s*=/, description: 'Direct innerHTML assignment' },
    { pattern: /crypto(?:miner|mining)/i, description: 'Cryptomining' },
    { pattern: /keylogger/i, description: 'Keylogger' }
  ];
  
  const scriptContents = await checkScriptContents(maliciousPatterns);
  
  if (scriptContents.length > 0) {
    scriptContents.forEach(({ script, pattern }) => {
      console.log(`  ⚠️ ${pattern} found in ${new URL(script).hostname}`);
      securityIssues.maliciousPatterns.push({ script, pattern });
    });
  } else {
    console.log('  βœ… No obvious malicious patterns detected');
  }
  
  // 5. Permission & API Usage
  console.log('\nπŸ”‘ Dangerous API Usage:');
  
  const dangerousAPIs = [
    'geolocation',
    'camera',
    'microphone',
    'notifications',
    'payment'
  ];
  
  dangerousAPIs.forEach(api => {
    if (navigator[api] || navigator.permissions?.query({ name: api })) {
      console.log(`  ⚠️ ${api} API available (check if third-parties use it)`);
      securityIssues.permissions.push(api);
    }
  });
  
  // Security Score
  const securityScore = calculateSecurityScore(securityIssues);
  
  console.log('\nπŸ›‘οΈ Security Score:');
  console.log(`  ${securityScore}/100`);
  
  if (securityScore < 50) {
    console.log('\n🚨 CRITICAL SECURITY ISSUES FOUND');
  } else if (securityScore < 70) {
    console.log('\n⚠️ Several security improvements needed');
  } else {
    console.log('\nβœ… Reasonable security posture');
  }
  
  return securityIssues;
}

async function checkScriptContents(patterns) {
  const issues = [];
  const scripts = document.querySelectorAll('script[src]');
  
  // Sample check - in reality would need to fetch and check content
  for (const script of scripts) {
    if (script.src.includes('eval') || script.src.includes('crypto')) {
      issues.push({
        script: script.src,
        pattern: 'Suspicious URL pattern'
      });
    }
  }
  
  return issues;
}

function calculateSecurityScore(issues) {
  let score = 100;
  
  score -= issues.noSRI.length * 5;
  score -= issues.httpScripts.length * 20;
  score -= issues.outdatedLibraries.length * 15;
  score -= issues.maliciousPatterns.length * 25;
  score -= issues.permissions.length * 5;
  
  return Math.max(0, score);
}

scanThirdPartySecurity();

Complete Privacy Audit

Comprehensive Third-party Assessment

// Run complete third-party and privacy audit
async function completePrivacyAudit() {
  console.log('πŸ” COMPLETE THIRD-PARTY & PRIVACY AUDIT');
  console.log('═'.repeat(50));
  console.log(`URL: ${window.location.href}`);
  console.log(`Date: ${new Date().toLocaleDateString()}\n`);
  
  const audit = {
    thirdParties: {
      total: 0,
      byCategory: {},
      highRisk: []
    },
    privacy: {
      score: 100,
      violations: [],
      gdprCompliance: {}
    },
    security: {
      issues: [],
      recommendations: []
    },
    performance: {
      impact: 0,
      slowestDomains: []
    }
  };
  
  // Phase 1: Third-party Discovery
  console.log('🌐 PHASE 1: THIRD-PARTY DISCOVERY');
  console.log('─'.repeat(40));
  
  const scripts = document.querySelectorAll('script[src]');
  const thirdPartyDomains = new Set();
  
  scripts.forEach(script => {
    try {
      const url = new URL(script.src);
      if (!url.hostname.includes(window.location.hostname)) {
        thirdPartyDomains.add(url.hostname);
      }
    } catch (e) {
      // Invalid URL
    }
  });
  
  audit.thirdParties.total = thirdPartyDomains.size;
  console.log(`Third-party domains: ${audit.thirdParties.total}`);
  
  // Categorize
  const categories = {
    tracking: ['google-analytics', 'segment', 'mixpanel'],
    advertising: ['doubleclick', 'adsystem', 'adnxs'],
    social: ['facebook', 'twitter', 'linkedin'],
    cdn: ['cloudflare', 'fastly', 'akamai']
  };
  
  thirdPartyDomains.forEach(domain => {
    let categorized = false;
    
    for (const [category, keywords] of Object.entries(categories)) {
      if (keywords.some(kw => domain.includes(kw))) {
        audit.thirdParties.byCategory[category] = 
          (audit.thirdParties.byCategory[category] || 0) + 1;
        
        if (category === 'tracking' || category === 'advertising') {
          audit.thirdParties.highRisk.push(domain);
        }
        
        categorized = true;
        break;
      }
    }
    
    if (!categorized) {
      audit.thirdParties.byCategory.other = 
        (audit.thirdParties.byCategory.other || 0) + 1;
    }
  });
  
  Object.entries(audit.thirdParties.byCategory).forEach(([category, count]) => {
    console.log(`  ${category}: ${count}`);
  });
  
  // Phase 2: Privacy Analysis
  console.log('\nπŸ” PHASE 2: PRIVACY ANALYSIS');
  console.log('─'.repeat(40));
  
  // Check cookies
  const cookies = document.cookie.split(';').length;
  console.log(`Cookies set: ${cookies}`);
  
  if (cookies > 10) {
    audit.privacy.violations.push('Excessive cookies');
    audit.privacy.score -= 10;
  }
  
  // Check for tracking cookies
  const trackingCookies = ['_ga', '_gid', '_fbp', 'IDE'];
  const hasTrackingCookies = trackingCookies.some(tc => 
    document.cookie.includes(tc)
  );
  
  if (hasTrackingCookies) {
    console.log('⚠️ Tracking cookies detected');
    audit.privacy.violations.push('Tracking cookies without consent');
    audit.privacy.score -= 20;
  }
  
  // GDPR compliance
  const gdprElements = {
    banner: document.querySelector('[class*="cookie-banner"], [class*="consent"]'),
    privacyLink: document.querySelector('a[href*="privacy"]'),
    rejectButton: document.querySelector('button[class*="reject"], button[class*="decline"]')
  };
  
  audit.privacy.gdprCompliance = {
    hasBanner: !!gdprElements.banner,
    hasPrivacyLink: !!gdprElements.privacyLink,
    hasRejectOption: !!gdprElements.rejectButton
  };
  
  console.log(`GDPR Compliance:`);
  console.log(`  Consent banner: ${audit.privacy.gdprCompliance.hasBanner ? 'βœ…' : '❌'}`);
  console.log(`  Privacy link: ${audit.privacy.gdprCompliance.hasPrivacyLink ? 'βœ…' : '❌'}`);
  console.log(`  Reject option: ${audit.privacy.gdprCompliance.hasRejectOption ? 'βœ…' : '❌'}`);
  
  if (!audit.privacy.gdprCompliance.hasBanner && hasTrackingCookies) {
    audit.privacy.score -= 30;
    audit.privacy.violations.push('No consent for tracking');
  }
  
  // Phase 3: Security Check
  console.log('\nπŸ›‘οΈ PHASE 3: SECURITY CHECK');
  console.log('─'.repeat(40));
  
  // Check for SRI
  const externalScripts = Array.from(scripts).filter(s => 
    s.src && !s.src.includes(window.location.hostname)
  );
  const scriptsWithSRI = externalScripts.filter(s => s.integrity).length;
  
  console.log(`Subresource Integrity: ${scriptsWithSRI}/${externalScripts.length} scripts`);
  
  if (scriptsWithSRI < externalScripts.length / 2) {
    audit.security.issues.push('Most scripts lack SRI');
    audit.security.recommendations.push('Add integrity attributes to external scripts');
  }
  
  // Check for mixed content
  if (window.location.protocol === 'https:') {
    const httpResources = document.querySelectorAll('[src^="http:"], [href^="http:"]');
    if (httpResources.length > 0) {
      audit.security.issues.push('Mixed content detected');
      console.log(`❌ Mixed content: ${httpResources.length} HTTP resources`);
    }
  }
  
  // Phase 4: Performance Impact
  console.log('\n⚑ PHASE 4: PERFORMANCE IMPACT');
  console.log('─'.repeat(40));
  
  const resources = performance.getEntriesByType('resource');
  const thirdPartyResources = resources.filter(r => 
    !r.name.includes(window.location.hostname)
  );
  
  const thirdPartyTime = thirdPartyResources.reduce((sum, r) => sum + r.duration, 0);
  const totalTime = resources.reduce((sum, r) => sum + r.duration, 0);
  
  audit.performance.impact = totalTime > 0 ? 
    (thirdPartyTime / totalTime * 100).toFixed(1) : 0;
  
  console.log(`Third-party impact: ${audit.performance.impact}% of load time`);
  
  // Find slowest domains
  const domainTimes = {};
  thirdPartyResources.forEach(r => {
    const domain = new URL(r.name).hostname;
    domainTimes[domain] = (domainTimes[domain] || 0) + r.duration;
  });
  
  audit.performance.slowestDomains = Object.entries(domainTimes)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 5)
    .map(([domain, time]) => ({ domain, time: time.toFixed(0) }));
  
  console.log('Slowest third-party domains:');
  audit.performance.slowestDomains.forEach(({ domain, time }) => {
    console.log(`  ${domain}: ${time}ms`);
  });
  
  // Final Score
  audit.privacy.score = Math.max(0, audit.privacy.score);
  
  console.log('\nπŸ“Š AUDIT SUMMARY');
  console.log('═'.repeat(50));
  console.log(`Privacy Score: ${audit.privacy.score}/100`);
  console.log(`Third-party domains: ${audit.thirdParties.total}`);
  console.log(`High-risk trackers: ${audit.thirdParties.highRisk.length}`);
  console.log(`Security issues: ${audit.security.issues.length}`);
  console.log(`Performance impact: ${audit.performance.impact}%`);
  
  // Risk Level
  let risk = 'LOW';
  if (audit.privacy.score < 50 || audit.thirdParties.highRisk.length > 5) {
    risk = 'HIGH';
  } else if (audit.privacy.score < 70 || audit.thirdParties.highRisk.length > 2) {
    risk = 'MEDIUM';
  }
  
  console.log(`Privacy Risk: ${risk}`);
  
  // Recommendations
  console.log('\nπŸ’‘ TOP RECOMMENDATIONS:');
  
  if (audit.thirdParties.highRisk.length > 3) {
    console.log('1. Reduce number of tracking scripts');
  }
  if (!audit.privacy.gdprCompliance.hasBanner && hasTrackingCookies) {
    console.log('2. Implement proper cookie consent');
  }
  if (scriptsWithSRI < externalScripts.length / 2) {
    console.log('3. Add Subresource Integrity to scripts');
  }
  if (audit.performance.impact > 50) {
    console.log('4. Third-parties dominate load time - optimize');
  }
  console.log('5. Implement Content Security Policy');
  console.log('6. Regular privacy audits');
  console.log('7. Consider self-hosting critical resources');
  
  return audit;
}

// Run the complete audit
completePrivacyAudit();

The Bottom Line

Third-party scripts are a privacy and security minefield:

  • Every script can access everything - Full DOM, cookies, user data
  • Users can't consent to what they can't see - Hidden tracking everywhere
  • Supply chain attacks are real - One compromised CDN affects millions
  • Performance death by a thousand cuts - Each script adds delay
  • GDPR violations are expensive - €20M or 4% of revenue

Audit every third-party. Remove what you don't need. Monitor what remains. Your users' privacy depends on it.


Analyze privacy instantly: Fusebox detects all third-party scripts, analyzes privacy impact, monitors tracking, and ensures GDPR compliance while you browse. Protect user privacy. $29 one-time purchase.