// Privacy Attacks Interactive Demonstrations class AttackSimulator { constructor() { this.charts = {}; this.initializeEventListeners(); this.initializeCharts(); } initializeEventListeners() { // Tab switching document.querySelectorAll('.attack-tab').forEach(tab => { tab.addEventListener('click', (e) => { this.switchTab(e.target.dataset.attack); }); }); // Slider updates this.setupSliderUpdates(); } setupSliderUpdates() { // New membership privacy level slider const privacyLevelSlider = document.getElementById('privacy-level-slider'); if (privacyLevelSlider) { privacyLevelSlider.addEventListener('input', (e) => { const levels = ['Very High', 'High', 'Medium', 'Low', 'Very Low']; document.getElementById('privacy-level-text').textContent = levels[e.target.value - 1]; this.updateMembershipDemo(); }); } // Reconstruction sliders const reconClipping = document.getElementById('recon-clipping-slider'); const reconNoise = document.getElementById('recon-noise-slider'); if (reconClipping) { reconClipping.addEventListener('input', (e) => { document.getElementById('recon-clipping').textContent = e.target.value; this.updateReconstructionAttack(); }); } if (reconNoise) { reconNoise.addEventListener('input', (e) => { document.getElementById('recon-noise-level').textContent = e.target.value; this.updateReconstructionAttack(); }); } // Linkage sliders const linkageQuality = document.getElementById('linkage-quality-slider'); const linkagePrivacy = document.getElementById('linkage-privacy-slider'); if (linkageQuality) { linkageQuality.addEventListener('input', (e) => { const qualities = ['Very Low', 'Low', 'Medium', 'High', 'Very High']; document.getElementById('linkage-quality').textContent = qualities[e.target.value - 1]; this.updateLinkageAttack(); }); } if (linkagePrivacy) { linkagePrivacy.addEventListener('input', (e) => { const epsilon = (11 - e.target.value).toFixed(1); document.getElementById('linkage-model-privacy').textContent = `ε=${epsilon}`; this.updateLinkageAttack(); }); } } switchTab(attackType) { // Update tab buttons document.querySelectorAll('.attack-tab').forEach(tab => { tab.classList.remove('active'); }); document.querySelector(`[data-attack="${attackType}"]`).classList.add('active'); // Update content document.querySelectorAll('.attack-content').forEach(content => { content.classList.remove('active'); }); document.getElementById(`${attackType}-content`).classList.add('active'); // Initialize chart for this tab if needed this.initializeTabChart(attackType); } initializeCharts() { this.initializeMembershipChart(); this.initializeComparisonChart(); } initializeMembershipChart() { const ctx = document.getElementById('membership-chart'); if (!ctx) return; this.charts.membership = new Chart(ctx, { type: 'line', data: { labels: ['ε=0.5', 'ε=1.0', 'ε=2.0', 'ε=3.0', 'ε=5.0', 'ε=8.0', 'ε=∞'], datasets: [{ label: 'Attack Success Rate', data: [52, 58, 65, 72, 78, 83, 87], borderColor: '#ff6b6b', backgroundColor: 'rgba(255, 107, 107, 0.1)', tension: 0.4, fill: true }, { label: 'Random Guessing', data: [50, 50, 50, 50, 50, 50, 50], borderColor: '#666', borderDash: [5, 5], fill: false }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Membership Inference Attack Success vs Privacy Budget' }, legend: { display: true } }, scales: { y: { beginAtZero: true, max: 100, title: { display: true, text: 'Attack Success Rate (%)' } }, x: { title: { display: true, text: 'Privacy Budget (ε)' } } } } }); } initializeComparisonChart() { const ctx = document.getElementById('comparison-chart'); if (!ctx) return; this.charts.comparison = new Chart(ctx, { type: 'radar', data: { labels: ['Membership Inference', 'Data Reconstruction', 'Model Inversion', 'Property Inference', 'Linkage Attack'], datasets: [{ label: 'No Privacy (ε=∞)', data: [87, 92, 78, 83, 89], borderColor: '#d32f2f', backgroundColor: 'rgba(211, 47, 47, 0.2)', pointBackgroundColor: '#d32f2f' }, { label: 'Low Privacy (ε=8.0)', data: [72, 76, 65, 70, 74], borderColor: '#f57c00', backgroundColor: 'rgba(245, 124, 0, 0.2)', pointBackgroundColor: '#f57c00' }, { label: 'Medium Privacy (ε=3.0)', data: [58, 61, 52, 56, 60], borderColor: '#fbc02d', backgroundColor: 'rgba(251, 192, 45, 0.2)', pointBackgroundColor: '#fbc02d' }, { label: 'High Privacy (ε=1.0)', data: [42, 45, 38, 41, 44], borderColor: '#2e7d32', backgroundColor: 'rgba(46, 125, 50, 0.2)', pointBackgroundColor: '#2e7d32' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Attack Success Rates Across Different Privacy Levels' } }, scales: { r: { beginAtZero: true, max: 100, ticks: { stepSize: 20 } } } } }); } initializeTabChart(attackType) { if (attackType === 'reconstruction') { this.initializeReconstructionChart(); } else if (attackType === 'property') { this.initializePropertyChart(); } else if (attackType === 'linkage') { this.initializeLinkageChart(); } } initializeReconstructionChart() { const ctx = document.getElementById('reconstruction-chart'); if (!ctx || this.charts.reconstruction) return; this.charts.reconstruction = new Chart(ctx, { type: 'bar', data: { labels: ['No Noise', 'Low Noise (σ=0.5)', 'Medium Noise (σ=1.0)', 'High Noise (σ=2.0)', 'Very High Noise (σ=3.0)'], datasets: [{ label: 'Reconstruction Quality (SSIM)', data: [0.95, 0.78, 0.52, 0.31, 0.18], backgroundColor: ['#d32f2f', '#f57c00', '#fbc02d', '#689f38', '#2e7d32'], borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Data Reconstruction Quality vs Noise Level' } }, scales: { y: { beginAtZero: true, max: 1, title: { display: true, text: 'Reconstruction Quality (SSIM Score)' } } } } }); } initializePropertyChart() { const ctx = document.getElementById('property-chart'); if (!ctx || this.charts.property) return; this.charts.property = new Chart(ctx, { type: 'doughnut', data: { labels: ['Correctly Inferred', 'Incorrectly Inferred', 'Uncertain'], datasets: [{ data: [52, 18, 30], backgroundColor: ['#d32f2f', '#f57c00', '#2e7d32'], borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Property Inference Attack Results' }, legend: { position: 'bottom' } } } }); } initializeLinkageChart() { const ctx = document.getElementById('linkage-chart'); if (!ctx || this.charts.linkage) return; this.charts.linkage = new Chart(ctx, { type: 'scatter', data: { datasets: [{ label: 'Successful Links', data: [ {x: 1, y: 45}, {x: 2, y: 52}, {x: 3, y: 61}, {x: 4, y: 68}, {x: 5, y: 74}, {x: 6, y: 79}, {x: 7, y: 83}, {x: 8, y: 86}, {x: 9, y: 89}, {x: 10, y: 91} ], backgroundColor: '#d32f2f', borderColor: '#d32f2f' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Linkage Attack Success vs Privacy Budget' } }, scales: { x: { title: { display: true, text: 'Privacy Budget (ε)' }, min: 0, max: 11 }, y: { title: { display: true, text: 'Successful Links (%)' }, min: 0, max: 100 } } } }); } // Attack simulation functions updateMembershipDemo() { const privacyLevel = parseInt(document.getElementById('privacy-level-slider').value); // Calculate attack success based on privacy level (1=highest privacy, 5=lowest) const successRates = [45, 52, 65, 78, 87]; // Success rates for each privacy level const successRate = successRates[privacyLevel - 1]; // Update confidence differences based on privacy const confidenceDiffs = [8, 12, 18, 24, 28]; // Confidence differences const trainingConf = [76, 80, 86, 92, 96]; // Training confidence const testConf = trainingConf.map((tc, i) => tc - confidenceDiffs[i]); // Test confidence const currentTrainingConf = trainingConf[privacyLevel - 1]; const currentTestConf = testConf[privacyLevel - 1]; const currentDiff = confidenceDiffs[privacyLevel - 1]; // Update visual elements document.getElementById('training-confidence').style.width = `${currentTrainingConf}%`; document.getElementById('training-confidence').textContent = `${currentTrainingConf}%`; document.getElementById('test-confidence').style.width = `${currentTestConf}%`; document.getElementById('test-confidence').textContent = `${currentTestConf}%`; document.getElementById('confidence-diff').textContent = `${currentDiff}%`; // Update success rate circle document.getElementById('membership-success').textContent = `${successRate}%`; // Update circle color based on success rate const circle = document.getElementById('success-rate-circle'); if (successRate < 55) { circle.style.background = 'linear-gradient(135deg, #28a745, #20c997)'; // Green - good privacy } else if (successRate < 70) { circle.style.background = 'linear-gradient(135deg, #ffc107, #fd7e14)'; // Yellow - medium privacy } else { circle.style.background = 'linear-gradient(135deg, #dc3545, #fd7e14)'; // Red - poor privacy } // Update explanation text const explanations = [ "Excellent! With very high privacy protection, the attacker can barely do better than random guessing (50%). Your data is well protected!", "Great! High privacy protection makes the attack much less effective. The confidence differences are small and hard to exploit.", "With medium privacy protection, the attacker can still succeed 65% of the time. Consider increasing privacy for sensitive data.", "Low privacy protection allows attackers to succeed most of the time. The model shows clear differences between training and test data.", "Very low privacy means the attack is highly successful. The model 'remembers' training data too well, making membership easy to detect." ]; document.getElementById('privacy-explanation').textContent = explanations[privacyLevel - 1]; } updateReconstructionAttack() { const clipping = parseFloat(document.getElementById('recon-clipping-slider').value); const noise = parseFloat(document.getElementById('recon-noise-slider').value); // Calculate reconstruction quality (SSIM score) const baseQuality = 0.95; const clippingReduction = (5 - clipping) * 0.08; const noiseReduction = noise * 0.22; const quality = Math.max(0.05, baseQuality - clippingReduction - noiseReduction); const ssimScore = quality.toFixed(2); // Update SSIM score display const ssimElement = document.getElementById('ssim-score'); if (ssimElement) { ssimElement.textContent = ssimScore; ssimElement.style.color = quality > 0.7 ? '#dc3545' : quality > 0.4 ? '#f57c00' : '#28a745'; } // Update quality badge const qualityElement = document.getElementById('recon-quality'); if (qualityElement) { if (quality > 0.7) { qualityElement.textContent = '❌ High Quality (Vulnerable!)'; qualityElement.className = 'reconstruction-quality quality-high'; } else if (quality > 0.4) { qualityElement.textContent = '⚠️ Medium Quality'; qualityElement.className = 'reconstruction-quality quality-medium'; } else { qualityElement.textContent = '✅ Low Quality (Protected!)'; qualityElement.className = 'reconstruction-quality quality-low'; } } // Calculate privacy level const epsilon = Math.max(0.5, 10 - (clipping * 0.5 + noise * 2)); const privacyStatus = document.getElementById('privacy-status'); if (privacyStatus) { if (epsilon > 6) { privacyStatus.textContent = `❌ Low (ε ≈ ${epsilon.toFixed(1)})`; privacyStatus.style.color = '#dc3545'; } else if (epsilon > 3) { privacyStatus.textContent = `⚠️ Medium (ε ≈ ${epsilon.toFixed(1)})`; privacyStatus.style.color = '#f57c00'; } else { privacyStatus.textContent = `✅ High (ε ≈ ${epsilon.toFixed(1)})`; privacyStatus.style.color = '#28a745'; } } // Update attack success rate const attackSuccess = Math.round(quality * 100); const attackSuccessElement = document.getElementById('attack-success'); if (attackSuccessElement) { attackSuccessElement.textContent = `${attackSuccess}%`; attackSuccessElement.style.color = attackSuccess > 70 ? '#dc3545' : attackSuccess > 40 ? '#f57c00' : '#28a745'; } // Update reconstructed image visualization this.updateReconstructedImage(quality, noise); // Update explanation const explanationElement = document.getElementById('recon-explanation'); if (explanationElement) { if (quality > 0.7) { explanationElement.textContent = '⚠️ Without sufficient privacy protection, attackers can reconstruct training images with high fidelity from gradient information alone!'; explanationElement.style.background = '#fff3cd'; explanationElement.style.borderLeft = '4px solid #ffc107'; } else if (quality > 0.4) { explanationElement.textContent = '🛡️ Medium privacy protection degrades reconstruction quality, but some features may still be visible. Consider increasing noise level.'; explanationElement.style.background = '#fff3e0'; explanationElement.style.borderLeft = '4px solid #f57c00'; } else { explanationElement.textContent = '✅ Excellent! With strong differential privacy, reconstructed images are too noisy to reveal sensitive information. Training data is well protected!'; explanationElement.style.background = '#e8f5e9'; explanationElement.style.borderLeft = '4px solid #28a745'; } } } updateReconstructedImage(quality, noise) { const reconstructedImg = document.getElementById('reconstructed-img'); const privacyOverlay = document.getElementById('privacy-overlay'); const leakStatus = document.getElementById('leak-status'); const modelPrivacyLevel = document.getElementById('model-privacy-level'); if (!reconstructedImg) return; // Calculate epsilon for display const epsilon = Math.max(0.5, 10 - (parseFloat(document.getElementById('recon-clipping-slider').value) * 0.5 + noise * 2)); if (modelPrivacyLevel) { modelPrivacyLevel.textContent = `ε = ${epsilon.toFixed(1)}`; } if (quality < 0.4) { // High privacy - show heavily obscured/protected image with overlay const svg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3Cfilter id='heavynoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='${1.5 + noise * 0.5}' numOctaves='8' /%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3ClinearGradient id='protectgrad' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' style='stop-color:%23e8f5e9;stop-opacity:1' /%3E%3Cstop offset='100%25' style='stop-color:%23c8e6c9;stop-opacity:1' /%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23protectgrad)'/%3E%3Crect width='400' height='500' fill='gray' opacity='0.95' filter='url(%23heavynoise)'/%3E%3C/svg%3E`; reconstructedImg.src = svg; if (privacyOverlay) { privacyOverlay.style.display = 'flex'; privacyOverlay.innerHTML = '
🔒
PROTECTED
'; } if (leakStatus) { leakStatus.innerHTML = '✅ ATTACK
FAILED!'; leakStatus.style.color = '#28a745'; } } else if (quality < 0.7) { // Medium privacy - show partially obscured image const svg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3Cfilter id='mednoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='${0.8 + noise * 0.3}' numOctaves='5' /%3E%3CfeColorMatrix type='saturate' values='0.2'/%3E%3C/filter%3E%3ClinearGradient id='medgrad' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' style='stop-color:%23fff3e0;stop-opacity:1' /%3E%3Cstop offset='100%25' style='stop-color:%23ffe0b2;stop-opacity:1' /%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23medgrad)'/%3E%3Crect width='400' height='500' fill='gray' opacity='${0.5 + noise * 0.15}' filter='url(%23mednoise)'/%3E%3Cellipse cx='200' cy='180' rx='80' ry='90' fill='%238b6f47' opacity='0.25'/%3E%3Crect x='140' y='260' width='120' height='160' fill='%236b8e6b' opacity='0.2' rx='8'/%3E%3C/svg%3E`; reconstructedImg.src = svg; if (privacyOverlay) { privacyOverlay.style.display = 'none'; } if (leakStatus) { leakStatus.innerHTML = '⚠️ PARTIAL
LEAK'; leakStatus.style.color = '#f57c00'; } } else { // Low privacy - show clear reconstruction (vulnerable) const svg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3Cfilter id='lightnoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='${0.3 + noise * 0.1}' numOctaves='2' /%3E%3CfeColorMatrix type='saturate' values='0.4'/%3E%3C/filter%3E%3ClinearGradient id='vulngrad' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' style='stop-color:%23e8b4b8;stop-opacity:1' /%3E%3Cstop offset='100%25' style='stop-color:%23d4a5a8;stop-opacity:1' /%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23vulngrad)'/%3E%3Crect width='400' height='500' fill='gray' opacity='${0.15 + noise * 0.05}' filter='url(%23lightnoise)'/%3E%3Cellipse cx='200' cy='180' rx='80' ry='90' fill='%238b6f47' opacity='0.5'/%3E%3Crect x='140' y='260' width='120' height='160' fill='%236b8e6b' opacity='0.4' rx='8'/%3E%3Ccircle cx='280' cy='140' r='35' fill='%23fff' opacity='0.2'/%3E%3Crect x='80' y='350' width='240' height='100' fill='%23a0826d' opacity='0.3' rx='6'/%3E%3C/svg%3E`; reconstructedImg.src = svg; if (privacyOverlay) { privacyOverlay.style.display = 'none'; } if (leakStatus) { leakStatus.innerHTML = '🚨 LEAKED
PRIVATE DATA!'; leakStatus.style.color = '#c1272d'; } } } updateLinkageAttack() { const quality = parseInt(document.getElementById('linkage-quality-slider').value); const privacySlider = parseInt(document.getElementById('linkage-privacy-slider').value); // Calculate epsilon: epsilon = 11 - privacySlider // privacySlider = 1 → ε = 10.0 (LOW privacy, HIGH success) // privacySlider = 10 → ε = 1.0 (HIGH privacy, LOW success) const epsilon = 11 - privacySlider; // Calculate linkage success // Higher quality = easier to link (increases success) // Higher epsilon (lower privacy) = easier to link (increases success) const qualityBonus = quality * 10; // Quality factor (0-60) const epsilonBonus = epsilon * 4; // Epsilon: 1.0 (high privacy) = 4, 10.0 (low privacy) = 40 // Base success is 20%, add quality scaled by epsilon influence const successRate = Math.max(15, Math.min(95, 20 + qualityBonus * (epsilon / 10))); document.getElementById('linkage-success').textContent = `${Math.round(successRate)}%`; // Update confidence const confidence = document.getElementById('linkage-confidence'); if (successRate > 75) { confidence.textContent = 'High'; } else if (successRate > 50) { confidence.textContent = 'Medium'; } else { confidence.textContent = 'Low'; } } } // Attack simulation functions (called by buttons) function updatePrivacyDemo() { const simulator = window.attackSimulator; simulator.updateMembershipDemo(); // Add visual feedback const button = event.target; const originalText = button.textContent; button.textContent = 'Updating...'; button.disabled = true; setTimeout(() => { button.textContent = originalText; button.disabled = false; }, 800); } function runReconstructionAttack() { const simulator = window.attackSimulator; simulator.updateReconstructionAttack(); // Update the canvas examples updateCurrentReconstructionExamples(); // Add visual feedback const button = event.target; const originalText = button.textContent; button.textContent = 'Reconstructing...'; button.disabled = true; setTimeout(() => { button.textContent = originalText; button.disabled = false; }, 2000); } function runInversionAttack() { const classSelect = document.getElementById('inversion-class-select'); const privacySlider = document.getElementById('inversion-privacy-slider'); const selectedClass = classSelect.value; const sliderValue = parseInt(privacySlider.value); // Invert the slider: slider 1 = high privacy (ε=1), slider 10 = low privacy (ε=10) const privacyLevel = 11 - sliderValue; // For drawing function const epsilon = sliderValue; // epsilon matches slider position now const confidence = Math.max(30, 35 + (sliderValue * 6)); // Higher slider = higher confidence // Update displays document.getElementById('inversion-confidence').textContent = `${confidence}%`; document.getElementById('inversion-class').textContent = classSelect.options[classSelect.selectedIndex].text; // Update privacy bar const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low']; updateInversionPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]); // Update explanation based on slider value (1=high privacy, 10=low privacy) const explanation = document.getElementById('inversion-explanation'); if (explanation) { if (sliderValue <= 3) { explanation.textContent = '✅ High privacy! The inverted features are very noisy and don\'t reveal clear class characteristics. Training data is well protected!'; explanation.style.background = '#e8f5e9'; explanation.style.borderLeft = '4px solid #4caf50'; } else if (sliderValue <= 6) { explanation.textContent = '⚠️ Medium privacy. Some class features are visible but degraded. Consider increasing privacy for sensitive data.'; explanation.style.background = '#fff3e0'; explanation.style.borderLeft = '4px solid #ff9800'; } else { explanation.textContent = '❌ Low privacy! The model reveals clear, detailed class features. An attacker can learn what the model associates with this class.'; explanation.style.background = '#ffebee'; explanation.style.borderLeft = '4px solid #f44336'; } } // Draw the inverted features on canvas drawInvertedFeatures(selectedClass, privacyLevel); // Add visual feedback const button = event.target; const originalText = button.textContent; button.textContent = 'Generating...'; button.disabled = true; setTimeout(() => { button.textContent = originalText; button.disabled = false; }, 1800); } function getPrivacyLevelText(level) { if (level <= 2) return 'Very High'; if (level <= 4) return 'High'; if (level <= 6) return 'Medium'; if (level <= 8) return 'Low'; return 'Very Low'; } function updateInversionPrivacyBar(sliderValue, privacyText) { // Privacy bar has been removed - function kept for compatibility } function drawInvertedFeatures(classDigit, privacyLevel) { const canvas = document.getElementById('inversion-canvas'); if (!canvas) return; const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; ctx.clearRect(0, 0, width, height); // Background ctx.fillStyle = '#e3f2fd'; ctx.fillRect(0, 0, width, height); // Calculate noise level based on privacy (1=high privacy/more noise, 10=low privacy/less noise) const noiseLevel = (privacyLevel - 1) / 9; // 0 to 1, where 0 = most noise const clarity = 1 - noiseLevel; // Inverted: high privacy = low clarity // Draw the digit with varying clarity ctx.save(); ctx.globalAlpha = Math.max(0.3, clarity); // Draw digit based on selected class - scale font size based on canvas size ctx.fillStyle = '#1976d2'; ctx.font = `bold ${Math.floor(width * 0.6)}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(classDigit, width / 2, height / 2); ctx.restore(); // Add noise based on privacy level if (privacyLevel <= 7) { const noiseIntensity = (7 - privacyLevel) / 7; // More noise for higher privacy addCanvasNoise(ctx, noiseIntensity, width, height); } // For very high privacy, add blur effect if (privacyLevel <= 3) { ctx.filter = `blur(${(4 - privacyLevel) * 4}px)`; ctx.drawImage(canvas, 0, 0); ctx.filter = 'none'; } } function addCanvasNoise(ctx, intensity, width, height) { if (intensity < 0.1) return; const imageData = ctx.getImageData(0, 0, width, height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const noise = (Math.random() - 0.5) * intensity * 200; data[i] += noise; // R data[i + 1] += noise; // G data[i + 2] += noise; // B } ctx.putImageData(imageData, 0, 0); } function runPropertyAttack() { const propertyType = document.getElementById('property-type').value; const accessLevel = parseInt(document.getElementById('property-access-slider').value); const privacySlider = document.getElementById('property-privacy-slider'); const sliderValue = privacySlider ? parseInt(privacySlider.value) : 5; // Calculate property inference accuracy based on privacy and access // Lower privacy (higher slider value) = more accurate inference const privacyPenalty = (11 - sliderValue) * 5; // High privacy reduces accuracy const accessBonus = accessLevel * 8; const baseAccuracy = 50; const accuracy = Math.min(95, baseAccuracy + accessBonus + (sliderValue * 3)); // Update uncertainty based on privacy (high privacy = high uncertainty) const uncertainty = Math.max(2, 20 - sliderValue * 1.5 - accessLevel * 2); document.getElementById('property-male').textContent = `${52}% ± ${Math.round(uncertainty)}%`; document.getElementById('property-female').textContent = `${48}% ± ${Math.round(uncertainty)}%`; // Update privacy bar const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low']; updatePropertyPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]); // Update explanation const explanation = document.getElementById('property-explanation'); if (explanation) { if (sliderValue <= 3) { explanation.textContent = '✅ High privacy! The attacker cannot accurately infer dataset properties. Large confidence intervals show high uncertainty.'; explanation.style.background = '#e8f5e9'; explanation.style.borderLeft = '4px solid #4caf50'; } else if (sliderValue <= 6) { explanation.textContent = '⚠️ Medium privacy. The attacker can infer properties with moderate accuracy. Consider increasing privacy for sensitive datasets.'; explanation.style.background = '#fff3e0'; explanation.style.borderLeft = '4px solid #ff9800'; } else { explanation.textContent = '❌ Low privacy! The attacker can accurately infer sensitive dataset properties like demographic distributions. Privacy breach risk!'; explanation.style.background = '#ffebee'; explanation.style.borderLeft = '4px solid #f44336'; } } // Add visual feedback const button = event.target; const originalText = button.textContent; button.textContent = 'Analyzing...'; button.disabled = true; setTimeout(() => { button.textContent = originalText; button.disabled = false; }, 2200); } function updatePropertyPrivacyBar(sliderValue, privacyText) { // Privacy bar has been removed - function kept for compatibility } function runLinkageAttack() { const simulator = window.attackSimulator; simulator.updateLinkageAttack(); // Add visual feedback const button = event.target; const originalText = button.textContent; button.textContent = 'Linking Data...'; button.disabled = true; setTimeout(() => { button.textContent = originalText; button.disabled = false; }, 2500); } // Draw reconstruction examples on canvases function drawReconstructionExamples() { // Example data patterns const examples = [ { // Face-like draw: (ctx, noise) => { // Head ctx.fillStyle = `rgba(139, 111, 71, ${1 - noise * 0.8})`; ctx.beginPath(); ctx.ellipse(60, 45, 30, 35, 0, 0, Math.PI * 2); ctx.fill(); // Eyes ctx.fillStyle = `rgba(50, 50, 50, ${1 - noise * 0.9})`; ctx.fillRect(45 + Math.random() * noise * 5, 35, 8, 8); ctx.fillRect(67 + Math.random() * noise * 5, 35, 8, 8); // Nose ctx.fillStyle = `rgba(100, 80, 60, ${1 - noise * 0.85})`; ctx.fillRect(57, 50, 6, 12); // Mouth ctx.fillStyle = `rgba(80, 60, 50, ${1 - noise * 0.9})`; ctx.fillRect(48, 70, 24, 5); // Body ctx.fillStyle = `rgba(107, 142, 107, ${1 - noise * 0.8})`; ctx.fillRect(35, 85, 50, 30); } }, { // Eye-like medical scan draw: (ctx, noise) => { // Dark background ctx.fillStyle = '#1a1a1a'; ctx.fillRect(0, 0, 120, 120); // Eye socket (bright) ctx.fillStyle = `rgba(255, 200, 100, ${0.9 - noise * 0.7})`; ctx.beginPath(); ctx.ellipse(60, 60, 35, 30, 0, 0, Math.PI * 2); ctx.fill(); // Pupil (dark) ctx.fillStyle = `rgba(20, 20, 20, ${1 - noise * 0.8})`; ctx.beginPath(); ctx.ellipse(60, 60, 15, 15, 0, 0, Math.PI * 2); ctx.fill(); // Highlight ctx.fillStyle = `rgba(255, 255, 255, ${0.8 - noise * 0.7})`; ctx.beginPath(); ctx.arc(55, 52, 5, 0, Math.PI * 2); ctx.fill(); } }, { // Medical scan with bright center draw: (ctx, noise) => { // Dark background ctx.fillStyle = '#0a0a0a'; ctx.fillRect(0, 0, 120, 120); // Bright center (hot spot) const gradient = ctx.createRadialGradient(60, 60, 5, 60, 60, 40); gradient.addColorStop(0, `rgba(255, 100, 50, ${1 - noise * 0.6})`); gradient.addColorStop(0.5, `rgba(200, 80, 40, ${0.8 - noise * 0.6})`); gradient.addColorStop(1, `rgba(100, 40, 20, ${0.3 - noise * 0.3})`); ctx.fillStyle = gradient; ctx.fillRect(0, 0, 120, 120); // Green markers ctx.fillStyle = `rgba(100, 255, 100, ${0.7 - noise * 0.6})`; ctx.fillRect(20, 20, 8, 8); ctx.fillRect(92, 20, 8, 8); ctx.fillRect(20, 92, 8, 8); ctx.fillRect(92, 92, 8, 8); } }, { // Computer/object draw: (ctx, noise) => { // Screen ctx.fillStyle = `rgba(100, 150, 255, ${0.9 - noise * 0.7})`; ctx.fillRect(30, 20, 60, 45); // Screen content ctx.fillStyle = `rgba(200, 220, 255, ${0.8 - noise * 0.7})`; ctx.fillRect(35, 25, 50, 35); // Base ctx.fillStyle = `rgba(80, 80, 80, ${1 - noise * 0.8})`; ctx.fillRect(40, 70, 40, 8); // Stand ctx.fillStyle = `rgba(100, 100, 100, ${1 - noise * 0.8})`; ctx.fillRect(55, 65, 10, 15); // Keyboard ctx.fillStyle = `rgba(60, 60, 60, ${0.9 - noise * 0.7})`; ctx.fillRect(20, 85, 80, 25); } } ]; // Draw each example at different privacy levels examples.forEach((example, idx) => { const exampleNum = idx + 1; // Ground truth (original) const originalCanvas = document.getElementById(`original-${exampleNum}`); if (originalCanvas) { const ctx = originalCanvas.getContext('2d'); ctx.clearRect(0, 0, 120, 120); example.draw(ctx, 0); } // No privacy (nearly perfect reconstruction - privacy breach!) const noPrivacyCanvas = document.getElementById(`no-privacy-${exampleNum}`); if (noPrivacyCanvas) { const ctx = noPrivacyCanvas.getContext('2d'); ctx.clearRect(0, 0, 120, 120); example.draw(ctx, 0.02); // Almost no degradation addNoise(ctx, 0.01, 120, 120); // Minimal noise } // High privacy (ε = 0.1) - complete noise/static const highPrivacyCanvas = document.getElementById(`high-privacy-${exampleNum}`); if (highPrivacyCanvas) { const ctx = highPrivacyCanvas.getContext('2d'); ctx.clearRect(0, 0, 120, 120); // Draw pure random noise - no original features visible drawCompleteNoise(ctx, 120, 120); } }); // Update current setting examples updateCurrentReconstructionExamples(); } function updateCurrentReconstructionExamples() { const clipping = parseFloat(document.getElementById('recon-clipping-slider')?.value || 1.0); const noise = parseFloat(document.getElementById('recon-noise-slider')?.value || 1.0); // Calculate quality/noise level const quality = Math.max(0.05, 0.95 - (5 - clipping) * 0.08 - noise * 0.22); const noiseLevel = 1 - quality; // Calculate epsilon const epsilon = Math.max(0.5, 10 - (clipping * 0.5 + noise * 2)); // Update epsilon display const epsilonDisplay = document.getElementById('current-epsilon'); if (epsilonDisplay) { epsilonDisplay.textContent = `(ε = ${epsilon.toFixed(1)})`; } // Example patterns (same as above) const examples = [ { // Face-like draw: (ctx, noise) => { ctx.fillStyle = `rgba(139, 111, 71, ${1 - noise * 0.8})`; ctx.beginPath(); ctx.ellipse(60, 45, 30, 35, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = `rgba(50, 50, 50, ${1 - noise * 0.9})`; ctx.fillRect(45 + Math.random() * noise * 5, 35, 8, 8); ctx.fillRect(67 + Math.random() * noise * 5, 35, 8, 8); ctx.fillStyle = `rgba(100, 80, 60, ${1 - noise * 0.85})`; ctx.fillRect(57, 50, 6, 12); ctx.fillStyle = `rgba(80, 60, 50, ${1 - noise * 0.9})`; ctx.fillRect(48, 70, 24, 5); ctx.fillStyle = `rgba(107, 142, 107, ${1 - noise * 0.8})`; ctx.fillRect(35, 85, 50, 30); } }, { // Eye-like draw: (ctx, noise) => { ctx.fillStyle = '#1a1a1a'; ctx.fillRect(0, 0, 120, 120); ctx.fillStyle = `rgba(255, 200, 100, ${0.9 - noise * 0.7})`; ctx.beginPath(); ctx.ellipse(60, 60, 35, 30, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = `rgba(20, 20, 20, ${1 - noise * 0.8})`; ctx.beginPath(); ctx.ellipse(60, 60, 15, 15, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = `rgba(255, 255, 255, ${0.8 - noise * 0.7})`; ctx.beginPath(); ctx.arc(55, 52, 5, 0, Math.PI * 2); ctx.fill(); } }, { // Medical scan draw: (ctx, noise) => { ctx.fillStyle = '#0a0a0a'; ctx.fillRect(0, 0, 120, 120); const gradient = ctx.createRadialGradient(60, 60, 5, 60, 60, 40); gradient.addColorStop(0, `rgba(255, 100, 50, ${1 - noise * 0.6})`); gradient.addColorStop(0.5, `rgba(200, 80, 40, ${0.8 - noise * 0.6})`); gradient.addColorStop(1, `rgba(100, 40, 20, ${0.3 - noise * 0.3})`); ctx.fillStyle = gradient; ctx.fillRect(0, 0, 120, 120); ctx.fillStyle = `rgba(100, 255, 100, ${0.7 - noise * 0.6})`; ctx.fillRect(20, 20, 8, 8); ctx.fillRect(92, 20, 8, 8); ctx.fillRect(20, 92, 8, 8); ctx.fillRect(92, 92, 8, 8); } }, { // Computer draw: (ctx, noise) => { ctx.fillStyle = `rgba(100, 150, 255, ${0.9 - noise * 0.7})`; ctx.fillRect(30, 20, 60, 45); ctx.fillStyle = `rgba(200, 220, 255, ${0.8 - noise * 0.7})`; ctx.fillRect(35, 25, 50, 35); ctx.fillStyle = `rgba(80, 80, 80, ${1 - noise * 0.8})`; ctx.fillRect(40, 70, 40, 8); ctx.fillStyle = `rgba(100, 100, 100, ${1 - noise * 0.8})`; ctx.fillRect(55, 65, 10, 15); ctx.fillStyle = `rgba(60, 60, 60, ${0.9 - noise * 0.7})`; ctx.fillRect(20, 85, 80, 25); } } ]; // Draw current setting for each example examples.forEach((example, idx) => { const currentCanvas = document.getElementById(`current-${idx + 1}`); if (currentCanvas) { const ctx = currentCanvas.getContext('2d'); ctx.clearRect(0, 0, 120, 120); // If very high privacy (epsilon < 1.0), show mostly noise if (epsilon < 1.0) { // Draw mostly random noise with tiny hint of original example.draw(ctx, 0.98); drawCompleteNoise(ctx, 120, 120); } else if (epsilon < 3.0) { // High privacy - heavy noise example.draw(ctx, noiseLevel); addNoise(ctx, Math.min(0.85, noiseLevel * 1.2), 120, 120); } else if (epsilon < 6.0) { // Medium privacy - moderate noise example.draw(ctx, noiseLevel * 0.7); addNoise(ctx, noiseLevel * 0.8, 120, 120); } else { // Low privacy - more visible noise (increased from 0.4 to 0.6) example.draw(ctx, noiseLevel * 0.4); addNoise(ctx, Math.max(0.3, noiseLevel * 0.6), 120, 120); } } }); } function addNoise(ctx, intensity, width, height) { if (intensity < 0.05) return; const imageData = ctx.getImageData(0, 0, width, height); const data = imageData.data; // For high intensity, make it more aggressive if (intensity > 0.7) { // Very high noise - almost complete randomization for (let i = 0; i < data.length; i += 4) { const randomness = intensity * 1.5; data[i] = Math.random() * 255 * randomness + data[i] * (1 - randomness); // R data[i + 1] = Math.random() * 255 * randomness + data[i + 1] * (1 - randomness); // G data[i + 2] = Math.random() * 255 * randomness + data[i + 2] * (1 - randomness); // B } } else { // Normal noise addition for (let i = 0; i < data.length; i += 4) { const noise = (Math.random() - 0.5) * intensity * 300; data[i] += noise; // R data[i + 1] += noise; // G data[i + 2] += noise; // B } } ctx.putImageData(imageData, 0, 0); } function drawCompleteNoise(ctx, width, height) { // Draw pure random static - complete privacy protection const imageData = ctx.createImageData(width, height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { // Random RGB values - pure noise data[i] = Math.random() * 255; // R data[i + 1] = Math.random() * 255; // G data[i + 2] = Math.random() * 255; // B data[i + 3] = 255; // A (fully opaque) } ctx.putImageData(imageData, 0, 0); } // Initialize when page loads document.addEventListener('DOMContentLoaded', function() { window.attackSimulator = new AttackSimulator(); // Run initial updates window.attackSimulator.updateMembershipDemo(); window.attackSimulator.updateReconstructionAttack(); window.attackSimulator.updateLinkageAttack(); // Draw reconstruction examples setTimeout(() => { drawReconstructionExamples(); }, 100); // Initialize inversion canvas and privacy bar with default values setTimeout(() => { drawInvertedFeatures('7', 6); // Default: digit 7, privacy level 6 (slider=5, inverted to 6) updateInversionPrivacyBar(5, 'Medium'); // Initialize privacy bar at slider position 5 updatePropertyPrivacyBar(5, 'Medium'); // Initialize property privacy bar }, 100); // Add event listeners for inversion attack controls const inversionPrivacySlider = document.getElementById('inversion-privacy-slider'); if (inversionPrivacySlider) { inversionPrivacySlider.addEventListener('input', function() { const sliderValue = parseInt(this.value); // Invert the slider: slider 1 = high privacy, slider 10 = low privacy const privacyLevel = 11 - sliderValue; const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low']; document.getElementById('inversion-privacy').textContent = privacyLevels[sliderValue - 1]; // Update visualization in real-time const classSelect = document.getElementById('inversion-class-select'); const selectedClass = classSelect ? classSelect.value : '7'; drawInvertedFeatures(selectedClass, privacyLevel); // Update privacy bar indicator updateInversionPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]); // Update explanation based on slider value (1=high privacy, 10=low privacy) const explanation = document.getElementById('inversion-explanation'); if (explanation) { if (sliderValue <= 3) { explanation.textContent = '✅ High privacy! The inverted features are very noisy and don\'t reveal clear class characteristics. Training data is well protected!'; explanation.style.background = '#e8f5e9'; explanation.style.borderLeft = '4px solid #4caf50'; } else if (sliderValue <= 6) { explanation.textContent = '⚠️ Medium privacy. Some class features are visible but degraded. Consider increasing privacy for sensitive data.'; explanation.style.background = '#fff3e0'; explanation.style.borderLeft = '4px solid #ff9800'; } else { explanation.textContent = '❌ Low privacy! The model reveals clear, detailed class features. An attacker can learn what the model associates with this class.'; explanation.style.background = '#ffebee'; explanation.style.borderLeft = '4px solid #f44336'; } } }); } // Add event listener for class selection const inversionClassSelect = document.getElementById('inversion-class-select'); if (inversionClassSelect) { inversionClassSelect.addEventListener('change', function() { const privacySlider = document.getElementById('inversion-privacy-slider'); const sliderValue = privacySlider ? parseInt(privacySlider.value) : 5; const privacyLevel = 11 - sliderValue; // Invert for drawing drawInvertedFeatures(this.value, privacyLevel); document.getElementById('inversion-class').textContent = this.options[this.selectedIndex].text; }); } // Add event listeners for property inference attack controls const propertyPrivacySlider = document.getElementById('property-privacy-slider'); if (propertyPrivacySlider) { propertyPrivacySlider.addEventListener('input', function() { const sliderValue = parseInt(this.value); const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low']; document.getElementById('property-privacy').textContent = privacyLevels[sliderValue - 1]; // Update privacy bar in real-time updatePropertyPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]); // Update uncertainty display in real-time const accessLevel = parseInt(document.getElementById('property-access-slider').value); const uncertainty = Math.max(2, 20 - sliderValue * 1.5 - accessLevel * 2); document.getElementById('property-male').textContent = `${52}% ± ${Math.round(uncertainty)}%`; document.getElementById('property-female').textContent = `${48}% ± ${Math.round(uncertainty)}%`; // Update explanation const explanation = document.getElementById('property-explanation'); if (explanation) { if (sliderValue <= 3) { explanation.textContent = '✅ High privacy! The attacker cannot accurately infer dataset properties. Large confidence intervals show high uncertainty.'; explanation.style.background = '#e8f5e9'; explanation.style.borderLeft = '4px solid #4caf50'; } else if (sliderValue <= 6) { explanation.textContent = '⚠️ Medium privacy. The attacker can infer properties with moderate accuracy. Consider increasing privacy for sensitive datasets.'; explanation.style.background = '#fff3e0'; explanation.style.borderLeft = '4px solid #ff9800'; } else { explanation.textContent = '❌ Low privacy! The attacker can accurately infer sensitive dataset properties like demographic distributions. Privacy breach risk!'; explanation.style.background = '#ffebee'; explanation.style.borderLeft = '4px solid #f44336'; } } }); } // Add event listener for model access slider const propertyAccessSlider = document.getElementById('property-access-slider'); if (propertyAccessSlider) { propertyAccessSlider.addEventListener('input', function() { const accessLevel = parseInt(this.value); const accessLevels = ['Black-box', 'Gray-box', 'White-box']; document.getElementById('property-access').textContent = accessLevels[accessLevel - 1]; // Update uncertainty display when access changes const privacySlider = document.getElementById('property-privacy-slider'); const sliderValue = privacySlider ? parseInt(privacySlider.value) : 5; const uncertainty = Math.max(2, 20 - sliderValue * 1.5 - accessLevel * 2); document.getElementById('property-male').textContent = `${52}% ± ${Math.round(uncertainty)}%`; document.getElementById('property-female').textContent = `${48}% ± ${Math.round(uncertainty)}%`; }); } });