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.