Shuya Feng commited on
Commit
6264b1a
·
1 Parent(s): 7bfe1af

Enhanced privacy attack visualizations and DP-SGD defense explanations

Browse files

Major improvements:
- Replaced digit 7 with realistic medical patient records in membership inference
- Fixed tab overlap with responsive flex layout
- Added concrete 4x4 grid visualization for data reconstruction with varying privacy levels
- Implemented dynamic Model Inversion attack with privacy-based noise/blur
- Added privacy sliders and real-time feedback for Property Inference
- Fixed linkage attack logic (correct epsilon to success rate mapping)
- Removed redundant privacy indicator bars for cleaner UI
- Added personalized DP-SGD defense cards within each attack section
- Each defense explanation is specific to its attack type with unique analogies

Files changed (2) hide show
  1. app/static/js/attacks.js +697 -30
  2. app/templates/attacks.html +689 -91
app/static/js/attacks.js CHANGED
@@ -336,8 +336,8 @@ class AttackSimulator {
336
  const successRate = successRates[privacyLevel - 1];
337
 
338
  // Update confidence differences based on privacy
339
- const confidenceDiffs = [6, 8, 14, 22, 28]; // Confidence differences
340
- const trainingConf = [88, 90, 92, 94, 96]; // Training confidence
341
  const testConf = trainingConf.map((tc, i) => tc - confidenceDiffs[i]); // Test confidence
342
 
343
  const currentTrainingConf = trainingConf[privacyLevel - 1];
@@ -382,35 +382,156 @@ class AttackSimulator {
382
  const clipping = parseFloat(document.getElementById('recon-clipping-slider').value);
383
  const noise = parseFloat(document.getElementById('recon-noise-slider').value);
384
 
385
- // Calculate reconstruction quality
386
  const baseQuality = 0.95;
387
- const clippingReduction = (5 - clipping) * 0.1;
388
- const noiseReduction = noise * 0.25;
389
 
390
- const quality = Math.max(0.1, baseQuality - clippingReduction - noiseReduction);
 
391
 
 
 
 
 
 
 
 
 
392
  const qualityElement = document.getElementById('recon-quality');
393
- if (quality > 0.7) {
394
- qualityElement.textContent = 'High Quality';
395
- qualityElement.className = 'reconstruction-quality quality-high';
396
- } else if (quality > 0.4) {
397
- qualityElement.textContent = 'Medium Quality';
398
- qualityElement.className = 'reconstruction-quality quality-medium';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  } else {
400
- qualityElement.textContent = 'Low Quality';
401
- qualityElement.className = 'reconstruction-quality quality-low';
 
 
 
 
 
 
 
 
 
 
402
  }
403
  }
404
 
405
  updateLinkageAttack() {
406
  const quality = parseInt(document.getElementById('linkage-quality-slider').value);
407
- const privacy = parseInt(document.getElementById('linkage-privacy-slider').value);
 
 
 
 
 
408
 
409
  // Calculate linkage success
410
- const baseSuccess = quality * 15; // Quality factor
411
- const privacyPenalty = (11 - privacy) * 2; // Privacy factor
 
 
412
 
413
- const successRate = Math.max(25, Math.min(95, baseSuccess + privacyPenalty));
 
414
 
415
  document.getElementById('linkage-success').textContent = `${Math.round(successRate)}%`;
416
 
@@ -447,6 +568,9 @@ function runReconstructionAttack() {
447
  const simulator = window.attackSimulator;
448
  simulator.updateReconstructionAttack();
449
 
 
 
 
450
  // Add visual feedback
451
  const button = event.target;
452
  const originalText = button.textContent;
@@ -464,15 +588,42 @@ function runInversionAttack() {
464
  const privacySlider = document.getElementById('inversion-privacy-slider');
465
 
466
  const selectedClass = classSelect.value;
467
- const privacyLevel = parseInt(privacySlider.value);
468
 
469
- // Calculate confidence based on privacy level
470
- const confidence = Math.max(30, 95 - (privacyLevel * 6));
471
- document.getElementById('inversion-confidence').textContent = `${confidence}%`;
 
472
 
473
- // Update class display
 
474
  document.getElementById('inversion-class').textContent = classSelect.options[classSelect.selectedIndex].text;
475
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  // Add visual feedback
477
  const button = event.target;
478
  const originalText = button.textContent;
@@ -485,20 +636,119 @@ function runInversionAttack() {
485
  }, 1800);
486
  }
487
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  function runPropertyAttack() {
489
  const propertyType = document.getElementById('property-type').value;
490
  const accessLevel = parseInt(document.getElementById('property-access-slider').value);
 
 
491
 
492
- // Calculate property inference accuracy
493
- const baseAccuracy = 70;
 
494
  const accessBonus = accessLevel * 8;
495
- const accuracy = Math.min(95, baseAccuracy + accessBonus);
 
 
 
 
 
 
 
496
 
497
- // Update uncertainty based on privacy (simulated)
498
- const uncertainty = Math.max(3, 15 - accessLevel * 2);
 
499
 
500
- document.getElementById('property-male').textContent = `${52}% ± ${uncertainty}%`;
501
- document.getElementById('property-female').textContent = `${48}% ± ${uncertainty}%`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
 
503
  // Add visual feedback
504
  const button = event.target;
@@ -512,6 +762,10 @@ function runPropertyAttack() {
512
  }, 2200);
513
  }
514
 
 
 
 
 
515
  function runLinkageAttack() {
516
  const simulator = window.attackSimulator;
517
  simulator.updateLinkageAttack();
@@ -528,6 +782,303 @@ function runLinkageAttack() {
528
  }, 2500);
529
  }
530
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
  // Initialize when page loads
532
  document.addEventListener('DOMContentLoaded', function() {
533
  window.attackSimulator = new AttackSimulator();
@@ -536,4 +1087,120 @@ document.addEventListener('DOMContentLoaded', function() {
536
  window.attackSimulator.updateMembershipDemo();
537
  window.attackSimulator.updateReconstructionAttack();
538
  window.attackSimulator.updateLinkageAttack();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  });
 
336
  const successRate = successRates[privacyLevel - 1];
337
 
338
  // Update confidence differences based on privacy
339
+ const confidenceDiffs = [8, 12, 18, 24, 28]; // Confidence differences
340
+ const trainingConf = [76, 80, 86, 92, 96]; // Training confidence
341
  const testConf = trainingConf.map((tc, i) => tc - confidenceDiffs[i]); // Test confidence
342
 
343
  const currentTrainingConf = trainingConf[privacyLevel - 1];
 
382
  const clipping = parseFloat(document.getElementById('recon-clipping-slider').value);
383
  const noise = parseFloat(document.getElementById('recon-noise-slider').value);
384
 
385
+ // Calculate reconstruction quality (SSIM score)
386
  const baseQuality = 0.95;
387
+ const clippingReduction = (5 - clipping) * 0.08;
388
+ const noiseReduction = noise * 0.22;
389
 
390
+ const quality = Math.max(0.05, baseQuality - clippingReduction - noiseReduction);
391
+ const ssimScore = quality.toFixed(2);
392
 
393
+ // Update SSIM score display
394
+ const ssimElement = document.getElementById('ssim-score');
395
+ if (ssimElement) {
396
+ ssimElement.textContent = ssimScore;
397
+ ssimElement.style.color = quality > 0.7 ? '#dc3545' : quality > 0.4 ? '#f57c00' : '#28a745';
398
+ }
399
+
400
+ // Update quality badge
401
  const qualityElement = document.getElementById('recon-quality');
402
+ if (qualityElement) {
403
+ if (quality > 0.7) {
404
+ qualityElement.textContent = ' High Quality (Vulnerable!)';
405
+ qualityElement.className = 'reconstruction-quality quality-high';
406
+ } else if (quality > 0.4) {
407
+ qualityElement.textContent = '⚠️ Medium Quality';
408
+ qualityElement.className = 'reconstruction-quality quality-medium';
409
+ } else {
410
+ qualityElement.textContent = '✅ Low Quality (Protected!)';
411
+ qualityElement.className = 'reconstruction-quality quality-low';
412
+ }
413
+ }
414
+
415
+ // Calculate privacy level
416
+ const epsilon = Math.max(0.5, 10 - (clipping * 0.5 + noise * 2));
417
+ const privacyStatus = document.getElementById('privacy-status');
418
+ if (privacyStatus) {
419
+ if (epsilon > 6) {
420
+ privacyStatus.textContent = `❌ Low (ε ≈ ${epsilon.toFixed(1)})`;
421
+ privacyStatus.style.color = '#dc3545';
422
+ } else if (epsilon > 3) {
423
+ privacyStatus.textContent = `⚠️ Medium (ε ≈ ${epsilon.toFixed(1)})`;
424
+ privacyStatus.style.color = '#f57c00';
425
+ } else {
426
+ privacyStatus.textContent = `✅ High (ε ≈ ${epsilon.toFixed(1)})`;
427
+ privacyStatus.style.color = '#28a745';
428
+ }
429
+ }
430
+
431
+ // Update attack success rate
432
+ const attackSuccess = Math.round(quality * 100);
433
+ const attackSuccessElement = document.getElementById('attack-success');
434
+ if (attackSuccessElement) {
435
+ attackSuccessElement.textContent = `${attackSuccess}%`;
436
+ attackSuccessElement.style.color = attackSuccess > 70 ? '#dc3545' : attackSuccess > 40 ? '#f57c00' : '#28a745';
437
+ }
438
+
439
+ // Update reconstructed image visualization
440
+ this.updateReconstructedImage(quality, noise);
441
+
442
+ // Update explanation
443
+ const explanationElement = document.getElementById('recon-explanation');
444
+ if (explanationElement) {
445
+ if (quality > 0.7) {
446
+ explanationElement.textContent = '⚠️ Without sufficient privacy protection, attackers can reconstruct training images with high fidelity from gradient information alone!';
447
+ explanationElement.style.background = '#fff3cd';
448
+ explanationElement.style.borderLeft = '4px solid #ffc107';
449
+ } else if (quality > 0.4) {
450
+ explanationElement.textContent = '🛡️ Medium privacy protection degrades reconstruction quality, but some features may still be visible. Consider increasing noise level.';
451
+ explanationElement.style.background = '#fff3e0';
452
+ explanationElement.style.borderLeft = '4px solid #f57c00';
453
+ } else {
454
+ explanationElement.textContent = '✅ Excellent! With strong differential privacy, reconstructed images are too noisy to reveal sensitive information. Training data is well protected!';
455
+ explanationElement.style.background = '#e8f5e9';
456
+ explanationElement.style.borderLeft = '4px solid #28a745';
457
+ }
458
+ }
459
+ }
460
+
461
+ updateReconstructedImage(quality, noise) {
462
+ const reconstructedImg = document.getElementById('reconstructed-img');
463
+ const privacyOverlay = document.getElementById('privacy-overlay');
464
+ const leakStatus = document.getElementById('leak-status');
465
+ const modelPrivacyLevel = document.getElementById('model-privacy-level');
466
+
467
+ if (!reconstructedImg) return;
468
+
469
+ // Calculate epsilon for display
470
+ const epsilon = Math.max(0.5, 10 - (parseFloat(document.getElementById('recon-clipping-slider').value) * 0.5 + noise * 2));
471
+ if (modelPrivacyLevel) {
472
+ modelPrivacyLevel.textContent = `ε = ${epsilon.toFixed(1)}`;
473
+ }
474
+
475
+ if (quality < 0.4) {
476
+ // High privacy - show heavily obscured/protected image with overlay
477
+ 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`;
478
+ reconstructedImg.src = svg;
479
+
480
+ if (privacyOverlay) {
481
+ privacyOverlay.style.display = 'flex';
482
+ privacyOverlay.innerHTML = '<div style="font-size: 3rem;">🔒</div><div>PROTECTED</div>';
483
+ }
484
+
485
+ if (leakStatus) {
486
+ leakStatus.innerHTML = '✅ ATTACK<br/>FAILED!';
487
+ leakStatus.style.color = '#28a745';
488
+ }
489
+ } else if (quality < 0.7) {
490
+ // Medium privacy - show partially obscured image
491
+ 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`;
492
+ reconstructedImg.src = svg;
493
+
494
+ if (privacyOverlay) {
495
+ privacyOverlay.style.display = 'none';
496
+ }
497
+
498
+ if (leakStatus) {
499
+ leakStatus.innerHTML = '⚠️ PARTIAL<br/>LEAK';
500
+ leakStatus.style.color = '#f57c00';
501
+ }
502
  } else {
503
+ // Low privacy - show clear reconstruction (vulnerable)
504
+ 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`;
505
+ reconstructedImg.src = svg;
506
+
507
+ if (privacyOverlay) {
508
+ privacyOverlay.style.display = 'none';
509
+ }
510
+
511
+ if (leakStatus) {
512
+ leakStatus.innerHTML = '🚨 LEAKED<br/>PRIVATE DATA!';
513
+ leakStatus.style.color = '#c1272d';
514
+ }
515
  }
516
  }
517
 
518
  updateLinkageAttack() {
519
  const quality = parseInt(document.getElementById('linkage-quality-slider').value);
520
+ const privacySlider = parseInt(document.getElementById('linkage-privacy-slider').value);
521
+
522
+ // Calculate epsilon: epsilon = 11 - privacySlider
523
+ // privacySlider = 1 → ε = 10.0 (LOW privacy, HIGH success)
524
+ // privacySlider = 10 → ε = 1.0 (HIGH privacy, LOW success)
525
+ const epsilon = 11 - privacySlider;
526
 
527
  // Calculate linkage success
528
+ // Higher quality = easier to link (increases success)
529
+ // Higher epsilon (lower privacy) = easier to link (increases success)
530
+ const qualityBonus = quality * 10; // Quality factor (0-60)
531
+ const epsilonBonus = epsilon * 4; // Epsilon: 1.0 (high privacy) = 4, 10.0 (low privacy) = 40
532
 
533
+ // Base success is 20%, add quality scaled by epsilon influence
534
+ const successRate = Math.max(15, Math.min(95, 20 + qualityBonus * (epsilon / 10)));
535
 
536
  document.getElementById('linkage-success').textContent = `${Math.round(successRate)}%`;
537
 
 
568
  const simulator = window.attackSimulator;
569
  simulator.updateReconstructionAttack();
570
 
571
+ // Update the canvas examples
572
+ updateCurrentReconstructionExamples();
573
+
574
  // Add visual feedback
575
  const button = event.target;
576
  const originalText = button.textContent;
 
588
  const privacySlider = document.getElementById('inversion-privacy-slider');
589
 
590
  const selectedClass = classSelect.value;
591
+ const sliderValue = parseInt(privacySlider.value);
592
 
593
+ // Invert the slider: slider 1 = high privacy (ε=1), slider 10 = low privacy (ε=10)
594
+ const privacyLevel = 11 - sliderValue; // For drawing function
595
+ const epsilon = sliderValue; // epsilon matches slider position now
596
+ const confidence = Math.max(30, 35 + (sliderValue * 6)); // Higher slider = higher confidence
597
 
598
+ // Update displays
599
+ document.getElementById('inversion-confidence').textContent = `${confidence}%`;
600
  document.getElementById('inversion-class').textContent = classSelect.options[classSelect.selectedIndex].text;
601
 
602
+ // Update privacy bar
603
+ const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low'];
604
+ updateInversionPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]);
605
+
606
+ // Update explanation based on slider value (1=high privacy, 10=low privacy)
607
+ const explanation = document.getElementById('inversion-explanation');
608
+ if (explanation) {
609
+ if (sliderValue <= 3) {
610
+ explanation.textContent = '✅ High privacy! The inverted features are very noisy and don\'t reveal clear class characteristics. Training data is well protected!';
611
+ explanation.style.background = '#e8f5e9';
612
+ explanation.style.borderLeft = '4px solid #4caf50';
613
+ } else if (sliderValue <= 6) {
614
+ explanation.textContent = '⚠️ Medium privacy. Some class features are visible but degraded. Consider increasing privacy for sensitive data.';
615
+ explanation.style.background = '#fff3e0';
616
+ explanation.style.borderLeft = '4px solid #ff9800';
617
+ } else {
618
+ explanation.textContent = '❌ Low privacy! The model reveals clear, detailed class features. An attacker can learn what the model associates with this class.';
619
+ explanation.style.background = '#ffebee';
620
+ explanation.style.borderLeft = '4px solid #f44336';
621
+ }
622
+ }
623
+
624
+ // Draw the inverted features on canvas
625
+ drawInvertedFeatures(selectedClass, privacyLevel);
626
+
627
  // Add visual feedback
628
  const button = event.target;
629
  const originalText = button.textContent;
 
636
  }, 1800);
637
  }
638
 
639
+ function getPrivacyLevelText(level) {
640
+ if (level <= 2) return 'Very High';
641
+ if (level <= 4) return 'High';
642
+ if (level <= 6) return 'Medium';
643
+ if (level <= 8) return 'Low';
644
+ return 'Very Low';
645
+ }
646
+
647
+ function updateInversionPrivacyBar(sliderValue, privacyText) {
648
+ // Privacy bar has been removed - function kept for compatibility
649
+ }
650
+
651
+ function drawInvertedFeatures(classDigit, privacyLevel) {
652
+ const canvas = document.getElementById('inversion-canvas');
653
+ if (!canvas) return;
654
+
655
+ const ctx = canvas.getContext('2d');
656
+ const width = canvas.width;
657
+ const height = canvas.height;
658
+
659
+ ctx.clearRect(0, 0, width, height);
660
+
661
+ // Background
662
+ ctx.fillStyle = '#e3f2fd';
663
+ ctx.fillRect(0, 0, width, height);
664
+
665
+ // Calculate noise level based on privacy (1=high privacy/more noise, 10=low privacy/less noise)
666
+ const noiseLevel = (privacyLevel - 1) / 9; // 0 to 1, where 0 = most noise
667
+ const clarity = 1 - noiseLevel; // Inverted: high privacy = low clarity
668
+
669
+ // Draw the digit with varying clarity
670
+ ctx.save();
671
+ ctx.globalAlpha = Math.max(0.3, clarity);
672
+
673
+ // Draw digit based on selected class - scale font size based on canvas size
674
+ ctx.fillStyle = '#1976d2';
675
+ ctx.font = `bold ${Math.floor(width * 0.6)}px Arial`;
676
+ ctx.textAlign = 'center';
677
+ ctx.textBaseline = 'middle';
678
+ ctx.fillText(classDigit, width / 2, height / 2);
679
+
680
+ ctx.restore();
681
+
682
+ // Add noise based on privacy level
683
+ if (privacyLevel <= 7) {
684
+ const noiseIntensity = (7 - privacyLevel) / 7; // More noise for higher privacy
685
+ addCanvasNoise(ctx, noiseIntensity, width, height);
686
+ }
687
+
688
+ // For very high privacy, add blur effect
689
+ if (privacyLevel <= 3) {
690
+ ctx.filter = `blur(${(4 - privacyLevel) * 4}px)`;
691
+ ctx.drawImage(canvas, 0, 0);
692
+ ctx.filter = 'none';
693
+ }
694
+ }
695
+
696
+ function addCanvasNoise(ctx, intensity, width, height) {
697
+ if (intensity < 0.1) return;
698
+
699
+ const imageData = ctx.getImageData(0, 0, width, height);
700
+ const data = imageData.data;
701
+
702
+ for (let i = 0; i < data.length; i += 4) {
703
+ const noise = (Math.random() - 0.5) * intensity * 200;
704
+ data[i] += noise; // R
705
+ data[i + 1] += noise; // G
706
+ data[i + 2] += noise; // B
707
+ }
708
+
709
+ ctx.putImageData(imageData, 0, 0);
710
+ }
711
+
712
  function runPropertyAttack() {
713
  const propertyType = document.getElementById('property-type').value;
714
  const accessLevel = parseInt(document.getElementById('property-access-slider').value);
715
+ const privacySlider = document.getElementById('property-privacy-slider');
716
+ const sliderValue = privacySlider ? parseInt(privacySlider.value) : 5;
717
 
718
+ // Calculate property inference accuracy based on privacy and access
719
+ // Lower privacy (higher slider value) = more accurate inference
720
+ const privacyPenalty = (11 - sliderValue) * 5; // High privacy reduces accuracy
721
  const accessBonus = accessLevel * 8;
722
+ const baseAccuracy = 50;
723
+ const accuracy = Math.min(95, baseAccuracy + accessBonus + (sliderValue * 3));
724
+
725
+ // Update uncertainty based on privacy (high privacy = high uncertainty)
726
+ const uncertainty = Math.max(2, 20 - sliderValue * 1.5 - accessLevel * 2);
727
+
728
+ document.getElementById('property-male').textContent = `${52}% ± ${Math.round(uncertainty)}%`;
729
+ document.getElementById('property-female').textContent = `${48}% ± ${Math.round(uncertainty)}%`;
730
 
731
+ // Update privacy bar
732
+ const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low'];
733
+ updatePropertyPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]);
734
 
735
+ // Update explanation
736
+ const explanation = document.getElementById('property-explanation');
737
+ if (explanation) {
738
+ if (sliderValue <= 3) {
739
+ explanation.textContent = '✅ High privacy! The attacker cannot accurately infer dataset properties. Large confidence intervals show high uncertainty.';
740
+ explanation.style.background = '#e8f5e9';
741
+ explanation.style.borderLeft = '4px solid #4caf50';
742
+ } else if (sliderValue <= 6) {
743
+ explanation.textContent = '⚠️ Medium privacy. The attacker can infer properties with moderate accuracy. Consider increasing privacy for sensitive datasets.';
744
+ explanation.style.background = '#fff3e0';
745
+ explanation.style.borderLeft = '4px solid #ff9800';
746
+ } else {
747
+ explanation.textContent = '❌ Low privacy! The attacker can accurately infer sensitive dataset properties like demographic distributions. Privacy breach risk!';
748
+ explanation.style.background = '#ffebee';
749
+ explanation.style.borderLeft = '4px solid #f44336';
750
+ }
751
+ }
752
 
753
  // Add visual feedback
754
  const button = event.target;
 
762
  }, 2200);
763
  }
764
 
765
+ function updatePropertyPrivacyBar(sliderValue, privacyText) {
766
+ // Privacy bar has been removed - function kept for compatibility
767
+ }
768
+
769
  function runLinkageAttack() {
770
  const simulator = window.attackSimulator;
771
  simulator.updateLinkageAttack();
 
782
  }, 2500);
783
  }
784
 
785
+ // Draw reconstruction examples on canvases
786
+ function drawReconstructionExamples() {
787
+ // Example data patterns
788
+ const examples = [
789
+ { // Face-like
790
+ draw: (ctx, noise) => {
791
+ // Head
792
+ ctx.fillStyle = `rgba(139, 111, 71, ${1 - noise * 0.8})`;
793
+ ctx.beginPath();
794
+ ctx.ellipse(60, 45, 30, 35, 0, 0, Math.PI * 2);
795
+ ctx.fill();
796
+
797
+ // Eyes
798
+ ctx.fillStyle = `rgba(50, 50, 50, ${1 - noise * 0.9})`;
799
+ ctx.fillRect(45 + Math.random() * noise * 5, 35, 8, 8);
800
+ ctx.fillRect(67 + Math.random() * noise * 5, 35, 8, 8);
801
+
802
+ // Nose
803
+ ctx.fillStyle = `rgba(100, 80, 60, ${1 - noise * 0.85})`;
804
+ ctx.fillRect(57, 50, 6, 12);
805
+
806
+ // Mouth
807
+ ctx.fillStyle = `rgba(80, 60, 50, ${1 - noise * 0.9})`;
808
+ ctx.fillRect(48, 70, 24, 5);
809
+
810
+ // Body
811
+ ctx.fillStyle = `rgba(107, 142, 107, ${1 - noise * 0.8})`;
812
+ ctx.fillRect(35, 85, 50, 30);
813
+ }
814
+ },
815
+ { // Eye-like medical scan
816
+ draw: (ctx, noise) => {
817
+ // Dark background
818
+ ctx.fillStyle = '#1a1a1a';
819
+ ctx.fillRect(0, 0, 120, 120);
820
+
821
+ // Eye socket (bright)
822
+ ctx.fillStyle = `rgba(255, 200, 100, ${0.9 - noise * 0.7})`;
823
+ ctx.beginPath();
824
+ ctx.ellipse(60, 60, 35, 30, 0, 0, Math.PI * 2);
825
+ ctx.fill();
826
+
827
+ // Pupil (dark)
828
+ ctx.fillStyle = `rgba(20, 20, 20, ${1 - noise * 0.8})`;
829
+ ctx.beginPath();
830
+ ctx.ellipse(60, 60, 15, 15, 0, 0, Math.PI * 2);
831
+ ctx.fill();
832
+
833
+ // Highlight
834
+ ctx.fillStyle = `rgba(255, 255, 255, ${0.8 - noise * 0.7})`;
835
+ ctx.beginPath();
836
+ ctx.arc(55, 52, 5, 0, Math.PI * 2);
837
+ ctx.fill();
838
+ }
839
+ },
840
+ { // Medical scan with bright center
841
+ draw: (ctx, noise) => {
842
+ // Dark background
843
+ ctx.fillStyle = '#0a0a0a';
844
+ ctx.fillRect(0, 0, 120, 120);
845
+
846
+ // Bright center (hot spot)
847
+ const gradient = ctx.createRadialGradient(60, 60, 5, 60, 60, 40);
848
+ gradient.addColorStop(0, `rgba(255, 100, 50, ${1 - noise * 0.6})`);
849
+ gradient.addColorStop(0.5, `rgba(200, 80, 40, ${0.8 - noise * 0.6})`);
850
+ gradient.addColorStop(1, `rgba(100, 40, 20, ${0.3 - noise * 0.3})`);
851
+ ctx.fillStyle = gradient;
852
+ ctx.fillRect(0, 0, 120, 120);
853
+
854
+ // Green markers
855
+ ctx.fillStyle = `rgba(100, 255, 100, ${0.7 - noise * 0.6})`;
856
+ ctx.fillRect(20, 20, 8, 8);
857
+ ctx.fillRect(92, 20, 8, 8);
858
+ ctx.fillRect(20, 92, 8, 8);
859
+ ctx.fillRect(92, 92, 8, 8);
860
+ }
861
+ },
862
+ { // Computer/object
863
+ draw: (ctx, noise) => {
864
+ // Screen
865
+ ctx.fillStyle = `rgba(100, 150, 255, ${0.9 - noise * 0.7})`;
866
+ ctx.fillRect(30, 20, 60, 45);
867
+
868
+ // Screen content
869
+ ctx.fillStyle = `rgba(200, 220, 255, ${0.8 - noise * 0.7})`;
870
+ ctx.fillRect(35, 25, 50, 35);
871
+
872
+ // Base
873
+ ctx.fillStyle = `rgba(80, 80, 80, ${1 - noise * 0.8})`;
874
+ ctx.fillRect(40, 70, 40, 8);
875
+
876
+ // Stand
877
+ ctx.fillStyle = `rgba(100, 100, 100, ${1 - noise * 0.8})`;
878
+ ctx.fillRect(55, 65, 10, 15);
879
+
880
+ // Keyboard
881
+ ctx.fillStyle = `rgba(60, 60, 60, ${0.9 - noise * 0.7})`;
882
+ ctx.fillRect(20, 85, 80, 25);
883
+ }
884
+ }
885
+ ];
886
+
887
+ // Draw each example at different privacy levels
888
+ examples.forEach((example, idx) => {
889
+ const exampleNum = idx + 1;
890
+
891
+ // Ground truth (original)
892
+ const originalCanvas = document.getElementById(`original-${exampleNum}`);
893
+ if (originalCanvas) {
894
+ const ctx = originalCanvas.getContext('2d');
895
+ ctx.clearRect(0, 0, 120, 120);
896
+ example.draw(ctx, 0);
897
+ }
898
+
899
+ // No privacy (nearly perfect reconstruction - privacy breach!)
900
+ const noPrivacyCanvas = document.getElementById(`no-privacy-${exampleNum}`);
901
+ if (noPrivacyCanvas) {
902
+ const ctx = noPrivacyCanvas.getContext('2d');
903
+ ctx.clearRect(0, 0, 120, 120);
904
+ example.draw(ctx, 0.02); // Almost no degradation
905
+ addNoise(ctx, 0.01, 120, 120); // Minimal noise
906
+ }
907
+
908
+ // High privacy (ε = 0.1) - complete noise/static
909
+ const highPrivacyCanvas = document.getElementById(`high-privacy-${exampleNum}`);
910
+ if (highPrivacyCanvas) {
911
+ const ctx = highPrivacyCanvas.getContext('2d');
912
+ ctx.clearRect(0, 0, 120, 120);
913
+ // Draw pure random noise - no original features visible
914
+ drawCompleteNoise(ctx, 120, 120);
915
+ }
916
+ });
917
+
918
+ // Update current setting examples
919
+ updateCurrentReconstructionExamples();
920
+ }
921
+
922
+ function updateCurrentReconstructionExamples() {
923
+ const clipping = parseFloat(document.getElementById('recon-clipping-slider')?.value || 1.0);
924
+ const noise = parseFloat(document.getElementById('recon-noise-slider')?.value || 1.0);
925
+
926
+ // Calculate quality/noise level
927
+ const quality = Math.max(0.05, 0.95 - (5 - clipping) * 0.08 - noise * 0.22);
928
+ const noiseLevel = 1 - quality;
929
+
930
+ // Calculate epsilon
931
+ const epsilon = Math.max(0.5, 10 - (clipping * 0.5 + noise * 2));
932
+
933
+ // Update epsilon display
934
+ const epsilonDisplay = document.getElementById('current-epsilon');
935
+ if (epsilonDisplay) {
936
+ epsilonDisplay.textContent = `(ε = ${epsilon.toFixed(1)})`;
937
+ }
938
+
939
+ // Example patterns (same as above)
940
+ const examples = [
941
+ { // Face-like
942
+ draw: (ctx, noise) => {
943
+ ctx.fillStyle = `rgba(139, 111, 71, ${1 - noise * 0.8})`;
944
+ ctx.beginPath();
945
+ ctx.ellipse(60, 45, 30, 35, 0, 0, Math.PI * 2);
946
+ ctx.fill();
947
+ ctx.fillStyle = `rgba(50, 50, 50, ${1 - noise * 0.9})`;
948
+ ctx.fillRect(45 + Math.random() * noise * 5, 35, 8, 8);
949
+ ctx.fillRect(67 + Math.random() * noise * 5, 35, 8, 8);
950
+ ctx.fillStyle = `rgba(100, 80, 60, ${1 - noise * 0.85})`;
951
+ ctx.fillRect(57, 50, 6, 12);
952
+ ctx.fillStyle = `rgba(80, 60, 50, ${1 - noise * 0.9})`;
953
+ ctx.fillRect(48, 70, 24, 5);
954
+ ctx.fillStyle = `rgba(107, 142, 107, ${1 - noise * 0.8})`;
955
+ ctx.fillRect(35, 85, 50, 30);
956
+ }
957
+ },
958
+ { // Eye-like
959
+ draw: (ctx, noise) => {
960
+ ctx.fillStyle = '#1a1a1a';
961
+ ctx.fillRect(0, 0, 120, 120);
962
+ ctx.fillStyle = `rgba(255, 200, 100, ${0.9 - noise * 0.7})`;
963
+ ctx.beginPath();
964
+ ctx.ellipse(60, 60, 35, 30, 0, 0, Math.PI * 2);
965
+ ctx.fill();
966
+ ctx.fillStyle = `rgba(20, 20, 20, ${1 - noise * 0.8})`;
967
+ ctx.beginPath();
968
+ ctx.ellipse(60, 60, 15, 15, 0, 0, Math.PI * 2);
969
+ ctx.fill();
970
+ ctx.fillStyle = `rgba(255, 255, 255, ${0.8 - noise * 0.7})`;
971
+ ctx.beginPath();
972
+ ctx.arc(55, 52, 5, 0, Math.PI * 2);
973
+ ctx.fill();
974
+ }
975
+ },
976
+ { // Medical scan
977
+ draw: (ctx, noise) => {
978
+ ctx.fillStyle = '#0a0a0a';
979
+ ctx.fillRect(0, 0, 120, 120);
980
+ const gradient = ctx.createRadialGradient(60, 60, 5, 60, 60, 40);
981
+ gradient.addColorStop(0, `rgba(255, 100, 50, ${1 - noise * 0.6})`);
982
+ gradient.addColorStop(0.5, `rgba(200, 80, 40, ${0.8 - noise * 0.6})`);
983
+ gradient.addColorStop(1, `rgba(100, 40, 20, ${0.3 - noise * 0.3})`);
984
+ ctx.fillStyle = gradient;
985
+ ctx.fillRect(0, 0, 120, 120);
986
+ ctx.fillStyle = `rgba(100, 255, 100, ${0.7 - noise * 0.6})`;
987
+ ctx.fillRect(20, 20, 8, 8);
988
+ ctx.fillRect(92, 20, 8, 8);
989
+ ctx.fillRect(20, 92, 8, 8);
990
+ ctx.fillRect(92, 92, 8, 8);
991
+ }
992
+ },
993
+ { // Computer
994
+ draw: (ctx, noise) => {
995
+ ctx.fillStyle = `rgba(100, 150, 255, ${0.9 - noise * 0.7})`;
996
+ ctx.fillRect(30, 20, 60, 45);
997
+ ctx.fillStyle = `rgba(200, 220, 255, ${0.8 - noise * 0.7})`;
998
+ ctx.fillRect(35, 25, 50, 35);
999
+ ctx.fillStyle = `rgba(80, 80, 80, ${1 - noise * 0.8})`;
1000
+ ctx.fillRect(40, 70, 40, 8);
1001
+ ctx.fillStyle = `rgba(100, 100, 100, ${1 - noise * 0.8})`;
1002
+ ctx.fillRect(55, 65, 10, 15);
1003
+ ctx.fillStyle = `rgba(60, 60, 60, ${0.9 - noise * 0.7})`;
1004
+ ctx.fillRect(20, 85, 80, 25);
1005
+ }
1006
+ }
1007
+ ];
1008
+
1009
+ // Draw current setting for each example
1010
+ examples.forEach((example, idx) => {
1011
+ const currentCanvas = document.getElementById(`current-${idx + 1}`);
1012
+ if (currentCanvas) {
1013
+ const ctx = currentCanvas.getContext('2d');
1014
+ ctx.clearRect(0, 0, 120, 120);
1015
+
1016
+ // If very high privacy (epsilon < 1.0), show mostly noise
1017
+ if (epsilon < 1.0) {
1018
+ // Draw mostly random noise with tiny hint of original
1019
+ example.draw(ctx, 0.98);
1020
+ drawCompleteNoise(ctx, 120, 120);
1021
+ } else if (epsilon < 3.0) {
1022
+ // High privacy - heavy noise
1023
+ example.draw(ctx, noiseLevel);
1024
+ addNoise(ctx, Math.min(0.85, noiseLevel * 1.2), 120, 120);
1025
+ } else if (epsilon < 6.0) {
1026
+ // Medium privacy - moderate noise
1027
+ example.draw(ctx, noiseLevel * 0.7);
1028
+ addNoise(ctx, noiseLevel * 0.8, 120, 120);
1029
+ } else {
1030
+ // Low privacy - more visible noise (increased from 0.4 to 0.6)
1031
+ example.draw(ctx, noiseLevel * 0.4);
1032
+ addNoise(ctx, Math.max(0.3, noiseLevel * 0.6), 120, 120);
1033
+ }
1034
+ }
1035
+ });
1036
+ }
1037
+
1038
+ function addNoise(ctx, intensity, width, height) {
1039
+ if (intensity < 0.05) return;
1040
+
1041
+ const imageData = ctx.getImageData(0, 0, width, height);
1042
+ const data = imageData.data;
1043
+
1044
+ // For high intensity, make it more aggressive
1045
+ if (intensity > 0.7) {
1046
+ // Very high noise - almost complete randomization
1047
+ for (let i = 0; i < data.length; i += 4) {
1048
+ const randomness = intensity * 1.5;
1049
+ data[i] = Math.random() * 255 * randomness + data[i] * (1 - randomness); // R
1050
+ data[i + 1] = Math.random() * 255 * randomness + data[i + 1] * (1 - randomness); // G
1051
+ data[i + 2] = Math.random() * 255 * randomness + data[i + 2] * (1 - randomness); // B
1052
+ }
1053
+ } else {
1054
+ // Normal noise addition
1055
+ for (let i = 0; i < data.length; i += 4) {
1056
+ const noise = (Math.random() - 0.5) * intensity * 300;
1057
+ data[i] += noise; // R
1058
+ data[i + 1] += noise; // G
1059
+ data[i + 2] += noise; // B
1060
+ }
1061
+ }
1062
+
1063
+ ctx.putImageData(imageData, 0, 0);
1064
+ }
1065
+
1066
+ function drawCompleteNoise(ctx, width, height) {
1067
+ // Draw pure random static - complete privacy protection
1068
+ const imageData = ctx.createImageData(width, height);
1069
+ const data = imageData.data;
1070
+
1071
+ for (let i = 0; i < data.length; i += 4) {
1072
+ // Random RGB values - pure noise
1073
+ data[i] = Math.random() * 255; // R
1074
+ data[i + 1] = Math.random() * 255; // G
1075
+ data[i + 2] = Math.random() * 255; // B
1076
+ data[i + 3] = 255; // A (fully opaque)
1077
+ }
1078
+
1079
+ ctx.putImageData(imageData, 0, 0);
1080
+ }
1081
+
1082
  // Initialize when page loads
1083
  document.addEventListener('DOMContentLoaded', function() {
1084
  window.attackSimulator = new AttackSimulator();
 
1087
  window.attackSimulator.updateMembershipDemo();
1088
  window.attackSimulator.updateReconstructionAttack();
1089
  window.attackSimulator.updateLinkageAttack();
1090
+
1091
+ // Draw reconstruction examples
1092
+ setTimeout(() => {
1093
+ drawReconstructionExamples();
1094
+ }, 100);
1095
+
1096
+ // Initialize inversion canvas and privacy bar with default values
1097
+ setTimeout(() => {
1098
+ drawInvertedFeatures('7', 6); // Default: digit 7, privacy level 6 (slider=5, inverted to 6)
1099
+ updateInversionPrivacyBar(5, 'Medium'); // Initialize privacy bar at slider position 5
1100
+ updatePropertyPrivacyBar(5, 'Medium'); // Initialize property privacy bar
1101
+ }, 100);
1102
+
1103
+ // Add event listeners for inversion attack controls
1104
+ const inversionPrivacySlider = document.getElementById('inversion-privacy-slider');
1105
+ if (inversionPrivacySlider) {
1106
+ inversionPrivacySlider.addEventListener('input', function() {
1107
+ const sliderValue = parseInt(this.value);
1108
+ // Invert the slider: slider 1 = high privacy, slider 10 = low privacy
1109
+ const privacyLevel = 11 - sliderValue;
1110
+ const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low'];
1111
+ document.getElementById('inversion-privacy').textContent = privacyLevels[sliderValue - 1];
1112
+
1113
+ // Update visualization in real-time
1114
+ const classSelect = document.getElementById('inversion-class-select');
1115
+ const selectedClass = classSelect ? classSelect.value : '7';
1116
+ drawInvertedFeatures(selectedClass, privacyLevel);
1117
+
1118
+ // Update privacy bar indicator
1119
+ updateInversionPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]);
1120
+
1121
+ // Update explanation based on slider value (1=high privacy, 10=low privacy)
1122
+ const explanation = document.getElementById('inversion-explanation');
1123
+ if (explanation) {
1124
+ if (sliderValue <= 3) {
1125
+ explanation.textContent = '✅ High privacy! The inverted features are very noisy and don\'t reveal clear class characteristics. Training data is well protected!';
1126
+ explanation.style.background = '#e8f5e9';
1127
+ explanation.style.borderLeft = '4px solid #4caf50';
1128
+ } else if (sliderValue <= 6) {
1129
+ explanation.textContent = '⚠️ Medium privacy. Some class features are visible but degraded. Consider increasing privacy for sensitive data.';
1130
+ explanation.style.background = '#fff3e0';
1131
+ explanation.style.borderLeft = '4px solid #ff9800';
1132
+ } else {
1133
+ explanation.textContent = '❌ Low privacy! The model reveals clear, detailed class features. An attacker can learn what the model associates with this class.';
1134
+ explanation.style.background = '#ffebee';
1135
+ explanation.style.borderLeft = '4px solid #f44336';
1136
+ }
1137
+ }
1138
+ });
1139
+ }
1140
+
1141
+ // Add event listener for class selection
1142
+ const inversionClassSelect = document.getElementById('inversion-class-select');
1143
+ if (inversionClassSelect) {
1144
+ inversionClassSelect.addEventListener('change', function() {
1145
+ const privacySlider = document.getElementById('inversion-privacy-slider');
1146
+ const sliderValue = privacySlider ? parseInt(privacySlider.value) : 5;
1147
+ const privacyLevel = 11 - sliderValue; // Invert for drawing
1148
+ drawInvertedFeatures(this.value, privacyLevel);
1149
+ document.getElementById('inversion-class').textContent = this.options[this.selectedIndex].text;
1150
+ });
1151
+ }
1152
+
1153
+ // Add event listeners for property inference attack controls
1154
+ const propertyPrivacySlider = document.getElementById('property-privacy-slider');
1155
+ if (propertyPrivacySlider) {
1156
+ propertyPrivacySlider.addEventListener('input', function() {
1157
+ const sliderValue = parseInt(this.value);
1158
+ const privacyLevels = ['Very High', 'Very High', 'High', 'High', 'Medium', 'Medium', 'Low', 'Low', 'Very Low', 'Very Low'];
1159
+ document.getElementById('property-privacy').textContent = privacyLevels[sliderValue - 1];
1160
+
1161
+ // Update privacy bar in real-time
1162
+ updatePropertyPrivacyBar(sliderValue, privacyLevels[sliderValue - 1]);
1163
+
1164
+ // Update uncertainty display in real-time
1165
+ const accessLevel = parseInt(document.getElementById('property-access-slider').value);
1166
+ const uncertainty = Math.max(2, 20 - sliderValue * 1.5 - accessLevel * 2);
1167
+ document.getElementById('property-male').textContent = `${52}% ± ${Math.round(uncertainty)}%`;
1168
+ document.getElementById('property-female').textContent = `${48}% ± ${Math.round(uncertainty)}%`;
1169
+
1170
+ // Update explanation
1171
+ const explanation = document.getElementById('property-explanation');
1172
+ if (explanation) {
1173
+ if (sliderValue <= 3) {
1174
+ explanation.textContent = '✅ High privacy! The attacker cannot accurately infer dataset properties. Large confidence intervals show high uncertainty.';
1175
+ explanation.style.background = '#e8f5e9';
1176
+ explanation.style.borderLeft = '4px solid #4caf50';
1177
+ } else if (sliderValue <= 6) {
1178
+ explanation.textContent = '⚠️ Medium privacy. The attacker can infer properties with moderate accuracy. Consider increasing privacy for sensitive datasets.';
1179
+ explanation.style.background = '#fff3e0';
1180
+ explanation.style.borderLeft = '4px solid #ff9800';
1181
+ } else {
1182
+ explanation.textContent = '❌ Low privacy! The attacker can accurately infer sensitive dataset properties like demographic distributions. Privacy breach risk!';
1183
+ explanation.style.background = '#ffebee';
1184
+ explanation.style.borderLeft = '4px solid #f44336';
1185
+ }
1186
+ }
1187
+ });
1188
+ }
1189
+
1190
+ // Add event listener for model access slider
1191
+ const propertyAccessSlider = document.getElementById('property-access-slider');
1192
+ if (propertyAccessSlider) {
1193
+ propertyAccessSlider.addEventListener('input', function() {
1194
+ const accessLevel = parseInt(this.value);
1195
+ const accessLevels = ['Black-box', 'Gray-box', 'White-box'];
1196
+ document.getElementById('property-access').textContent = accessLevels[accessLevel - 1];
1197
+
1198
+ // Update uncertainty display when access changes
1199
+ const privacySlider = document.getElementById('property-privacy-slider');
1200
+ const sliderValue = privacySlider ? parseInt(privacySlider.value) : 5;
1201
+ const uncertainty = Math.max(2, 20 - sliderValue * 1.5 - accessLevel * 2);
1202
+ document.getElementById('property-male').textContent = `${52}% ± ${Math.round(uncertainty)}%`;
1203
+ document.getElementById('property-female').textContent = `${48}% ± ${Math.round(uncertainty)}%`;
1204
+ });
1205
+ }
1206
  });
app/templates/attacks.html CHANGED
@@ -17,11 +17,13 @@
17
  padding: 4px;
18
  margin-bottom: 2rem;
19
  overflow-x: auto;
 
 
20
  }
21
 
22
  .attack-tab {
23
- flex: 1;
24
- min-width: 150px;
25
  padding: 0.75rem 1rem;
26
  text-align: center;
27
  background: transparent;
@@ -31,6 +33,7 @@
31
  font-weight: 500;
32
  transition: all 0.3s ease;
33
  white-space: nowrap;
 
34
  }
35
 
36
  .attack-tab:hover {
@@ -332,8 +335,28 @@
332
  flex-shrink: 0;
333
  }
334
 
335
- /* Mobile responsive */
 
 
 
 
 
 
 
 
336
  @media (max-width: 768px) {
 
 
 
 
 
 
 
 
 
 
 
 
337
  .story-flow {
338
  flex-direction: column;
339
  }
@@ -348,6 +371,13 @@
348
  }
349
  }
350
 
 
 
 
 
 
 
 
351
  .interactive-demo {
352
  background: #f8f9fa;
353
  padding: 2rem;
@@ -590,6 +620,374 @@
590
  color: #666;
591
  line-height: 1.5;
592
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
  </style>
594
  {% endblock %}
595
 
@@ -672,7 +1070,7 @@
672
  <div class="demo-scenario">
673
  <div class="scenario-setup">
674
  <h4>The Setup</h4>
675
- <p>We have a model trained to recognize handwritten digits. Let's see if we can tell which images were in the training set!</p>
676
  </div>
677
 
678
  <div class="confidence-comparison">
@@ -682,12 +1080,16 @@
682
  <span class="sample-status training">✓ Was in training</span>
683
  </div>
684
  <div class="sample-image">
685
- <div class="digit-display">7</div>
 
 
 
 
686
  </div>
687
  <div class="confidence-bar">
688
  <div class="confidence-label">Model Confidence:</div>
689
  <div class="confidence-meter">
690
- <div class="confidence-fill training-confidence" id="training-confidence" style="width: 92%;">92%</div>
691
  </div>
692
  </div>
693
  </div>
@@ -700,12 +1102,16 @@
700
  <span class="sample-status test">✗ Not in training</span>
701
  </div>
702
  <div class="sample-image">
703
- <div class="digit-display">7</div>
 
 
 
 
704
  </div>
705
  <div class="confidence-bar">
706
  <div class="confidence-label">Model Confidence:</div>
707
  <div class="confidence-meter">
708
- <div class="confidence-fill test-confidence" id="test-confidence" style="width: 78%;">78%</div>
709
  </div>
710
  </div>
711
  </div>
@@ -713,8 +1119,8 @@
713
 
714
  <div class="attack-insight">
715
  <div class="insight-box">
716
- <strong>🔍 The Attack Insight:</strong> The model is <span id="confidence-diff">14%</span> more confident on training data!
717
- An attacker can use this difference to guess membership.
718
  </div>
719
  </div>
720
  </div>
@@ -777,6 +1183,16 @@
777
  <div class="chart-container">
778
  <canvas id="membership-chart"></canvas>
779
  </div>
 
 
 
 
 
 
 
 
 
 
780
  </div>
781
  </div>
782
 
@@ -795,41 +1211,173 @@
795
  <strong>Threat:</strong> Attackers with access to gradients can potentially reconstruct original training images, especially in federated learning scenarios.
796
  </div>
797
 
798
- <div class="attack-demo">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
799
  <div class="demo-panel">
800
- <h3>Gradient Privacy Settings</h3>
801
  <div class="demo-controls">
802
- <label>Clipping Norm: <span id="recon-clipping">1.0</span></label>
803
  <input type="range" class="demo-slider" id="recon-clipping-slider" min="0.1" max="5" step="0.1" value="1.0">
 
 
 
 
804
 
805
- <label>Noise Level: <span id="recon-noise-level">1.0</span></label>
806
  <input type="range" class="demo-slider" id="recon-noise-slider" min="0" max="3" step="0.1" value="1.0">
 
 
 
 
807
  </div>
808
- <button class="control-button" onclick="runReconstructionAttack()">Simulate Reconstruction</button>
809
  </div>
810
 
811
  <div class="demo-panel">
812
- <h3>Reconstruction Quality</h3>
813
- <div class="visual-demo">
814
- <div class="visual-item">
815
- <div style="width: 120px; height: 120px; background: #f0f0f0; border-radius: 4px; margin: 0 auto 0.5rem; display: flex; align-items: center; justify-content: center;">
816
- Original Image
 
 
817
  </div>
818
- <div>Ground Truth</div>
819
  </div>
820
- <div class="visual-item">
821
- <div style="width: 120px; height: 120px; background: #ffcccb; border-radius: 4px; margin: 0 auto 0.5rem; display: flex; align-items: center; justify-content: center;">
822
- Reconstructed
 
 
 
 
 
 
823
  </div>
824
- <div class="reconstruction-quality quality-high" id="recon-quality">High Quality</div>
825
  </div>
826
  </div>
 
 
 
 
827
  </div>
828
  </div>
829
 
830
  <div class="chart-container">
831
  <canvas id="reconstruction-chart"></canvas>
832
  </div>
 
 
 
 
 
 
 
 
 
 
833
  </div>
834
  </div>
835
 
@@ -849,38 +1397,64 @@
849
  </div>
850
 
851
  <div class="attack-demo">
852
- <div class="demo-panel">
853
- <h3>Inversion Parameters</h3>
854
- <div class="demo-controls">
855
- <label>Target Class: <span id="inversion-class">Digit 7</span></label>
856
- <select id="inversion-class-select" class="parameter-select">
857
- <option value="0">Digit 0</option>
858
- <option value="1">Digit 1</option>
859
- <option value="7" selected>Digit 7</option>
860
- <option value="9">Digit 9</option>
861
- </select>
 
 
 
 
862
 
863
- <label>Privacy Level: <span id="inversion-privacy">Medium</span></label>
864
- <input type="range" class="demo-slider" id="inversion-privacy-slider" min="1" max="10" step="1" value="5">
 
 
 
 
 
 
 
 
865
  </div>
866
- <button class="control-button" onclick="runInversionAttack()">Generate Class Representative</button>
867
  </div>
868
 
869
- <div class="demo-panel">
870
- <h3>Generated Features</h3>
871
  <div class="visual-demo">
872
  <div class="visual-item">
873
- <div style="width: 120px; height: 120px; background: #e3f2fd; border-radius: 4px; margin: 0 auto 0.5rem; display: flex; align-items: center; justify-content: center; font-size: 3rem;">
874
- 7
875
- </div>
876
- <div>Inverted Features</div>
877
- <div style="font-size: 0.8rem; color: #666; margin-top: 0.5rem;">
878
- Confidence: <span id="inversion-confidence">87%</span>
879
  </div>
880
  </div>
881
  </div>
 
 
 
 
 
 
882
  </div>
883
  </div>
 
 
 
 
 
 
 
 
 
 
884
  </div>
885
  </div>
886
 
@@ -900,44 +1474,82 @@
900
  </div>
901
 
902
  <div class="attack-demo">
903
- <div class="demo-panel">
904
- <h3>Target Properties</h3>
905
- <div class="demo-controls">
906
- <label>Property Type:</label>
907
- <select id="property-type" class="parameter-select">
908
- <option value="gender">Gender Distribution</option>
909
- <option value="age">Age Distribution</option>
910
- <option value="location">Geographic Distribution</option>
911
- </select>
 
 
912
 
913
- <label>Model Access: <span id="property-access">Black-box</span></label>
914
- <input type="range" class="demo-slider" id="property-access-slider" min="1" max="3" step="1" value="1">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
915
  </div>
916
- <button class="control-button" onclick="runPropertyAttack()">Infer Properties</button>
917
  </div>
918
 
919
- <div class="demo-panel">
920
- <h3>Inferred Properties</h3>
921
  <div class="demo-result">
922
- <div>Inferred Distribution:</div>
923
- <div style="margin: 1rem 0;">
924
- <div style="display: flex; justify-content: space-between; margin: 0.5rem 0;">
925
- <span>Male:</span> <span id="property-male">52% ± 8%</span>
926
  </div>
927
- <div style="display: flex; justify-content: space-between; margin: 0.5rem 0;">
928
- <span>Female:</span> <span id="property-female">48% ± 8%</span>
929
  </div>
930
  </div>
931
- <div style="font-size: 0.8rem; color: #666;">
932
  Confidence intervals show attack uncertainty
933
  </div>
934
  </div>
 
 
 
 
 
 
935
  </div>
936
  </div>
937
 
938
  <div class="chart-container">
939
  <canvas id="property-chart"></canvas>
940
  </div>
 
 
 
 
 
 
 
 
 
 
941
  </div>
942
  </div>
943
 
@@ -1013,6 +1625,16 @@
1013
  <div class="chart-container">
1014
  <canvas id="linkage-chart"></canvas>
1015
  </div>
 
 
 
 
 
 
 
 
 
 
1016
  </div>
1017
  </div>
1018
 
@@ -1060,30 +1682,6 @@
1060
  </div>
1061
  </div>
1062
 
1063
- <!-- Defense Mechanisms -->
1064
- <div class="defense-section">
1065
- <h2>🛡️ How DP-SGD Defends Against These Attacks</h2>
1066
- <p>Differential privacy provides mathematical guarantees against all these attack types</p>
1067
-
1068
- <div class="defense-grid">
1069
- <div class="defense-item">
1070
- <h3>Gradient Noise</h3>
1071
- <p>Adds calibrated noise to gradients, making it impossible to perfectly reconstruct training data or infer membership with certainty.</p>
1072
- </div>
1073
- <div class="defense-item">
1074
- <h3>Gradient Clipping</h3>
1075
- <p>Limits the influence of any single training example, preventing attackers from identifying outliers or high-influence samples.</p>
1076
- </div>
1077
- <div class="defense-item">
1078
- <h3>Privacy Accounting</h3>
1079
- <p>Tracks cumulative privacy loss across training, ensuring mathematical bounds on what attackers can learn.</p>
1080
- </div>
1081
- <div class="defense-item">
1082
- <h3>Composition Guarantees</h3>
1083
- <p>Provides formal bounds even when multiple attacks are combined, protecting against sophisticated adversaries.</p>
1084
- </div>
1085
- </div>
1086
- </div>
1087
  </div>
1088
  {% endblock %}
1089
 
 
17
  padding: 4px;
18
  margin-bottom: 2rem;
19
  overflow-x: auto;
20
+ gap: 4px;
21
+ flex-wrap: wrap;
22
  }
23
 
24
  .attack-tab {
25
+ flex: 1 1 auto;
26
+ min-width: 160px;
27
  padding: 0.75rem 1rem;
28
  text-align: center;
29
  background: transparent;
 
33
  font-weight: 500;
34
  transition: all 0.3s ease;
35
  white-space: nowrap;
36
+ font-size: 0.9rem;
37
  }
38
 
39
  .attack-tab:hover {
 
335
  flex-shrink: 0;
336
  }
337
 
338
+ /* Responsive design */
339
+ @media (max-width: 1024px) {
340
+ .attack-tab {
341
+ flex: 1 1 calc(33.333% - 4px);
342
+ min-width: 150px;
343
+ font-size: 0.88rem;
344
+ }
345
+ }
346
+
347
  @media (max-width: 768px) {
348
+ .attack-tabs {
349
+ gap: 4px;
350
+ padding: 4px;
351
+ }
352
+
353
+ .attack-tab {
354
+ flex: 1 1 calc(50% - 4px);
355
+ min-width: 140px;
356
+ font-size: 0.85rem;
357
+ padding: 0.6rem 0.75rem;
358
+ }
359
+
360
  .story-flow {
361
  flex-direction: column;
362
  }
 
371
  }
372
  }
373
 
374
+ @media (max-width: 480px) {
375
+ .attack-tab {
376
+ flex: 1 1 100%;
377
+ min-width: auto;
378
+ }
379
+ }
380
+
381
  .interactive-demo {
382
  background: #f8f9fa;
383
  padding: 2rem;
 
620
  color: #666;
621
  line-height: 1.5;
622
  }
623
+
624
+ /* Reconstruction Grid Styles */
625
+ .reconstruction-visual-demo {
626
+ background: white;
627
+ padding: 2rem;
628
+ border-radius: 12px;
629
+ margin: 1.5rem 0;
630
+ }
631
+
632
+ .reconstruction-grid {
633
+ display: grid;
634
+ grid-template-columns: repeat(4, 1fr);
635
+ gap: 1rem;
636
+ margin: 0 auto;
637
+ max-width: 800px;
638
+ }
639
+
640
+ .grid-header {
641
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
642
+ color: white;
643
+ padding: 0.75rem 0.5rem;
644
+ text-align: center;
645
+ font-weight: bold;
646
+ font-size: 0.9rem;
647
+ border-radius: 8px 8px 0 0;
648
+ line-height: 1.3;
649
+ }
650
+
651
+ .example-cell {
652
+ background: #f8f9fa;
653
+ border: 2px solid #dee2e6;
654
+ border-radius: 8px;
655
+ padding: 0.5rem;
656
+ display: flex;
657
+ align-items: center;
658
+ justify-content: center;
659
+ transition: all 0.3s ease;
660
+ }
661
+
662
+ .example-cell:hover {
663
+ transform: scale(1.05);
664
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
665
+ z-index: 10;
666
+ }
667
+
668
+ .example-cell canvas {
669
+ display: block;
670
+ border-radius: 4px;
671
+ image-rendering: pixelated;
672
+ image-rendering: -moz-crisp-edges;
673
+ image-rendering: crisp-edges;
674
+ }
675
+
676
+ .example-cell.current-setting {
677
+ border: 3px solid #ffc107;
678
+ background: #fffbf0;
679
+ box-shadow: 0 0 0 3px rgba(255, 193, 7, 0.2);
680
+ }
681
+
682
+ .reconstruction-legend {
683
+ display: grid;
684
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
685
+ gap: 1rem;
686
+ margin-top: 2rem;
687
+ padding: 1.5rem;
688
+ background: #f8f9fa;
689
+ border-radius: 8px;
690
+ }
691
+
692
+ .legend-item {
693
+ display: flex;
694
+ align-items: center;
695
+ gap: 0.75rem;
696
+ }
697
+
698
+ .legend-box {
699
+ width: 30px;
700
+ height: 30px;
701
+ border-radius: 4px;
702
+ flex-shrink: 0;
703
+ }
704
+
705
+ .current-highlight {
706
+ background: #fffbf0;
707
+ border: 3px solid #ffc107;
708
+ animation: pulse-border 2s infinite;
709
+ }
710
+
711
+ @keyframes pulse-border {
712
+ 0%, 100% { border-color: #ffc107; }
713
+ 50% { border-color: #ff9800; }
714
+ }
715
+
716
+ .legend-item span {
717
+ font-size: 0.85rem;
718
+ line-height: 1.4;
719
+ }
720
+
721
+ @media (max-width: 768px) {
722
+ .reconstruction-grid {
723
+ grid-template-columns: repeat(2, 1fr);
724
+ gap: 0.5rem;
725
+ }
726
+
727
+ .grid-header {
728
+ font-size: 0.75rem;
729
+ padding: 0.5rem 0.25rem;
730
+ }
731
+
732
+ .example-cell canvas {
733
+ width: 80px;
734
+ height: 80px;
735
+ }
736
+
737
+ .reconstruction-legend {
738
+ grid-template-columns: 1fr;
739
+ gap: 0.75rem;
740
+ }
741
+ }
742
+
743
+ .three-panel-container {
744
+ display: grid;
745
+ grid-template-columns: 1fr auto 1fr auto 1fr;
746
+ gap: 1rem;
747
+ align-items: stretch;
748
+ max-width: 1400px;
749
+ margin: 0 auto;
750
+ }
751
+
752
+ .privacy-panel {
753
+ display: flex;
754
+ flex-direction: column;
755
+ border-radius: 12px;
756
+ overflow: hidden;
757
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
758
+ transition: transform 0.3s ease;
759
+ }
760
+
761
+ .privacy-panel:hover {
762
+ transform: translateY(-4px);
763
+ }
764
+
765
+ .panel-header {
766
+ padding: 1.5rem 1rem;
767
+ text-align: center;
768
+ font-weight: bold;
769
+ }
770
+
771
+ .panel-header h3 {
772
+ margin: 0;
773
+ font-size: 1rem;
774
+ line-height: 1.4;
775
+ letter-spacing: 0.5px;
776
+ }
777
+
778
+ .private-panel {
779
+ background: #f5e6d3;
780
+ border: 4px solid #c89968;
781
+ }
782
+
783
+ .private-panel .panel-header {
784
+ background: #f5e6d3;
785
+ color: #8b4513;
786
+ }
787
+
788
+ .safe-panel {
789
+ background: #e8f4f8;
790
+ border: 4px solid #7fb3d5;
791
+ }
792
+
793
+ .safe-panel .panel-header {
794
+ background: #e8f4f8;
795
+ color: #2c5f2d;
796
+ }
797
+
798
+ .attack-panel {
799
+ background: #fce4e4;
800
+ border: 4px solid #d4a5a8;
801
+ }
802
+
803
+ .attack-panel .panel-header {
804
+ background: #fce4e4;
805
+ color: #c1272d;
806
+ }
807
+
808
+ .panel-image-wrapper {
809
+ position: relative;
810
+ flex: 1;
811
+ display: flex;
812
+ align-items: center;
813
+ justify-content: center;
814
+ overflow: hidden;
815
+ background: #f8f9fa;
816
+ }
817
+
818
+ .panel-image {
819
+ width: 100%;
820
+ height: 100%;
821
+ object-fit: cover;
822
+ display: block;
823
+ }
824
+
825
+ .privacy-lock-overlay {
826
+ position: absolute;
827
+ bottom: 20px;
828
+ left: 50%;
829
+ transform: translateX(-50%);
830
+ background: rgba(255, 255, 255, 0.95);
831
+ padding: 0.75rem 1.5rem;
832
+ border-radius: 30px;
833
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
834
+ }
835
+
836
+ .lock-icon {
837
+ font-size: 2rem;
838
+ text-align: center;
839
+ }
840
+
841
+ .privacy-overlay {
842
+ position: absolute;
843
+ top: 0;
844
+ left: 0;
845
+ width: 100%;
846
+ height: 100%;
847
+ background: rgba(255, 255, 255, 0.95);
848
+ backdrop-filter: blur(30px);
849
+ display: flex;
850
+ align-items: center;
851
+ justify-content: center;
852
+ font-size: 1.5rem;
853
+ font-weight: bold;
854
+ color: #28a745;
855
+ transition: all 0.5s ease;
856
+ flex-direction: column;
857
+ gap: 1rem;
858
+ }
859
+
860
+ .panel-footer {
861
+ padding: 1rem;
862
+ text-align: center;
863
+ font-size: 0.9rem;
864
+ background: rgba(255, 255, 255, 0.5);
865
+ }
866
+
867
+ .safe-footer {
868
+ display: flex;
869
+ align-items: center;
870
+ justify-content: center;
871
+ gap: 0.75rem;
872
+ background: rgba(255, 255, 255, 0.7);
873
+ }
874
+
875
+ .check-icon {
876
+ font-size: 1.5rem;
877
+ color: #28a745;
878
+ background: white;
879
+ width: 35px;
880
+ height: 35px;
881
+ border-radius: 50%;
882
+ display: flex;
883
+ align-items: center;
884
+ justify-content: center;
885
+ font-weight: bold;
886
+ border: 2px solid #28a745;
887
+ }
888
+
889
+ .footer-metrics {
890
+ text-align: left;
891
+ font-size: 0.85rem;
892
+ }
893
+
894
+ .small-text {
895
+ font-size: 0.75rem;
896
+ color: #666;
897
+ margin-top: 0.25rem;
898
+ }
899
+
900
+ .danger-footer {
901
+ display: flex;
902
+ align-items: center;
903
+ justify-content: center;
904
+ gap: 0.75rem;
905
+ background: rgba(255, 255, 255, 0.7);
906
+ padding: 1.25rem;
907
+ }
908
+
909
+ .alert-icon {
910
+ font-size: 2rem;
911
+ animation: pulse 2s infinite;
912
+ }
913
+
914
+ @keyframes pulse {
915
+ 0%, 100% { opacity: 1; transform: scale(1); }
916
+ 50% { opacity: 0.7; transform: scale(1.1); }
917
+ }
918
+
919
+ .danger-text {
920
+ font-weight: bold;
921
+ color: #c1272d;
922
+ font-size: 1rem;
923
+ line-height: 1.3;
924
+ text-align: center;
925
+ }
926
+
927
+ .footer-text {
928
+ color: #666;
929
+ font-size: 0.9rem;
930
+ }
931
+
932
+ .flow-arrow {
933
+ display: flex;
934
+ flex-direction: column;
935
+ align-items: center;
936
+ justify-content: center;
937
+ gap: 0.5rem;
938
+ }
939
+
940
+ .arrow-symbol {
941
+ font-size: 2.5rem;
942
+ color: #dc3545;
943
+ font-weight: bold;
944
+ }
945
+
946
+ .attack-arrow .arrow-symbol {
947
+ color: #c1272d;
948
+ }
949
+
950
+ .arrow-label {
951
+ font-size: 0.85rem;
952
+ color: #666;
953
+ font-weight: 600;
954
+ text-transform: uppercase;
955
+ letter-spacing: 0.5px;
956
+ }
957
+
958
+ @media (max-width: 1200px) {
959
+ .three-panel-container {
960
+ grid-template-columns: 1fr;
961
+ gap: 1.5rem;
962
+ }
963
+
964
+ .flow-arrow {
965
+ flex-direction: row;
966
+ gap: 1rem;
967
+ }
968
+
969
+ .arrow-symbol {
970
+ transform: rotate(90deg);
971
+ }
972
+
973
+ .panel-header h3 {
974
+ font-size: 0.95rem;
975
+ }
976
+ }
977
+
978
+ @media (max-width: 768px) {
979
+ .panel-header h3 {
980
+ font-size: 0.85rem;
981
+ }
982
+
983
+ .arrow-symbol {
984
+ font-size: 2rem;
985
+ }
986
+
987
+ .reconstruction-visual-demo {
988
+ padding: 1rem;
989
+ }
990
+ }
991
  </style>
992
  {% endblock %}
993
 
 
1070
  <div class="demo-scenario">
1071
  <div class="scenario-setup">
1072
  <h4>The Setup</h4>
1073
+ <p>A hospital trained a model to predict heart disease risk. Let's see if an attacker can tell which patient records were in the training set!</p>
1074
  </div>
1075
 
1076
  <div class="confidence-comparison">
 
1080
  <span class="sample-status training">✓ Was in training</span>
1081
  </div>
1082
  <div class="sample-image">
1083
+ <div class="digit-display" style="font-size: 1rem; flex-direction: column; gap: 0.5rem; height: auto; padding: 1rem;">
1084
+ <div style="font-size: 2.5rem;">🏥</div>
1085
+ <div style="font-size: 0.85rem; color: #666;">Patient #A2847</div>
1086
+ <div style="font-size: 0.8rem; color: #999; line-height: 1.3;">Age: 58 | BP: 145/92<br/>Cholesterol: High</div>
1087
+ </div>
1088
  </div>
1089
  <div class="confidence-bar">
1090
  <div class="confidence-label">Model Confidence:</div>
1091
  <div class="confidence-meter">
1092
+ <div class="confidence-fill training-confidence" id="training-confidence" style="width: 96%;">96%</div>
1093
  </div>
1094
  </div>
1095
  </div>
 
1102
  <span class="sample-status test">✗ Not in training</span>
1103
  </div>
1104
  <div class="sample-image">
1105
+ <div class="digit-display" style="font-size: 1rem; flex-direction: column; gap: 0.5rem; height: auto; padding: 1rem;">
1106
+ <div style="font-size: 2.5rem;">🏥</div>
1107
+ <div style="font-size: 0.85rem; color: #666;">Patient #B3192</div>
1108
+ <div style="font-size: 0.8rem; color: #999; line-height: 1.3;">Age: 56 | BP: 142/90<br/>Cholesterol: High</div>
1109
+ </div>
1110
  </div>
1111
  <div class="confidence-bar">
1112
  <div class="confidence-label">Model Confidence:</div>
1113
  <div class="confidence-meter">
1114
+ <div class="confidence-fill test-confidence" id="test-confidence" style="width: 68%;">68%</div>
1115
  </div>
1116
  </div>
1117
  </div>
 
1119
 
1120
  <div class="attack-insight">
1121
  <div class="insight-box">
1122
+ <strong>🔍 The Attack Insight:</strong> The model is <span id="confidence-diff">28%</span> more confident on training data!
1123
+ An attacker can use this difference to infer that Patient A2847 was treated at this hospital - a serious privacy breach.
1124
  </div>
1125
  </div>
1126
  </div>
 
1183
  <div class="chart-container">
1184
  <canvas id="membership-chart"></canvas>
1185
  </div>
1186
+
1187
+ <!-- Defense against this attack -->
1188
+ <div style="margin-top: 2rem; padding: 1.5rem; background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); border-radius: 8px; color: white;">
1189
+ <h3 style="margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem;">
1190
+ <span style="font-size: 1.5rem;">🛡️</span> How DP-SGD Defends Against This Attack
1191
+ </h3>
1192
+ <p style="font-size: 1rem; line-height: 1.6; margin: 0;">
1193
+ <strong>Random noise addition:</strong> DP-SGD adds carefully calibrated noise to model outputs, making Alice's record indistinguishable from any other patient. The model gives similar confidence scores whether or not someone was in the training data - attackers can't tell the difference between training and test examples.
1194
+ </p>
1195
+ </div>
1196
  </div>
1197
  </div>
1198
 
 
1211
  <strong>Threat:</strong> Attackers with access to gradients can potentially reconstruct original training images, especially in federated learning scenarios.
1212
  </div>
1213
 
1214
+ <div class="attack-scenario-intro" style="background: #f8f9fa; padding: 1.5rem; border-radius: 8px; margin-bottom: 1.5rem;">
1215
+ <h3>📋 Scenario: Federated Learning Attack</h3>
1216
+ <p style="margin: 0.5rem 0 0 0; color: #666;">
1217
+ In federated learning, a company trains a model using images from users' devices. An attacker with access to
1218
+ gradient updates attempts to reconstruct the original training images. Let's see how differential privacy protects against this!
1219
+ </p>
1220
+ </div>
1221
+
1222
+ <div class="reconstruction-visual-demo">
1223
+ <h3 style="text-align: center; margin-bottom: 1.5rem;">🔍 Reconstruction Quality at Different Privacy Levels</h3>
1224
+
1225
+ <!-- Multi-example comparison grid -->
1226
+ <div class="reconstruction-grid">
1227
+ <!-- Header Row -->
1228
+ <div class="grid-header">Ground Truth</div>
1229
+ <div class="grid-header">No Privacy<br/><span style="font-size: 0.85rem; font-weight: normal;">(ε = ∞)</span></div>
1230
+ <div class="grid-header">Low Privacy<br/><span style="font-size: 0.85rem; font-weight: normal;" id="current-epsilon">(ε = 8.0)</span></div>
1231
+ <div class="grid-header">High Privacy<br/><span style="font-size: 0.85rem; font-weight: normal;">(ε = 0.1)</span></div>
1232
+
1233
+ <!-- Example 1: Face-like -->
1234
+ <div class="example-cell">
1235
+ <canvas id="original-1" width="120" height="120"></canvas>
1236
+ </div>
1237
+ <div class="example-cell">
1238
+ <canvas id="no-privacy-1" width="120" height="120"></canvas>
1239
+ </div>
1240
+ <div class="example-cell current-setting">
1241
+ <canvas id="current-1" width="120" height="120"></canvas>
1242
+ </div>
1243
+ <div class="example-cell">
1244
+ <canvas id="high-privacy-1" width="120" height="120"></canvas>
1245
+ </div>
1246
+
1247
+ <!-- Example 2: Eye-like -->
1248
+ <div class="example-cell">
1249
+ <canvas id="original-2" width="120" height="120"></canvas>
1250
+ </div>
1251
+ <div class="example-cell">
1252
+ <canvas id="no-privacy-2" width="120" height="120"></canvas>
1253
+ </div>
1254
+ <div class="example-cell current-setting">
1255
+ <canvas id="current-2" width="120" height="120"></canvas>
1256
+ </div>
1257
+ <div class="example-cell">
1258
+ <canvas id="high-privacy-2" width="120" height="120"></canvas>
1259
+ </div>
1260
+
1261
+ <!-- Example 3: Medical scan-like -->
1262
+ <div class="example-cell">
1263
+ <canvas id="original-3" width="120" height="120"></canvas>
1264
+ </div>
1265
+ <div class="example-cell">
1266
+ <canvas id="no-privacy-3" width="120" height="120"></canvas>
1267
+ </div>
1268
+ <div class="example-cell current-setting">
1269
+ <canvas id="current-3" width="120" height="120"></canvas>
1270
+ </div>
1271
+ <div class="example-cell">
1272
+ <canvas id="high-privacy-3" width="120" height="120"></canvas>
1273
+ </div>
1274
+
1275
+ <!-- Example 4: Object-like -->
1276
+ <div class="example-cell">
1277
+ <canvas id="original-4" width="120" height="120"></canvas>
1278
+ </div>
1279
+ <div class="example-cell">
1280
+ <canvas id="no-privacy-4" width="120" height="120"></canvas>
1281
+ </div>
1282
+ <div class="example-cell current-setting">
1283
+ <canvas id="current-4" width="120" height="120"></canvas>
1284
+ </div>
1285
+ <div class="example-cell">
1286
+ <canvas id="high-privacy-4" width="120" height="120"></canvas>
1287
+ </div>
1288
+ </div>
1289
+
1290
+ <!-- Legend -->
1291
+ <div class="reconstruction-legend">
1292
+ <div class="legend-item">
1293
+ <div class="legend-box" style="background: #e8f5e9; border: 2px solid #4caf50;"></div>
1294
+ <span><strong>Ground Truth:</strong> Original private training images</span>
1295
+ </div>
1296
+ <div class="legend-item">
1297
+ <div class="legend-box" style="background: #ffebee; border: 2px solid #f44336;"></div>
1298
+ <span><strong>No Privacy:</strong> Perfect reconstruction - privacy breach!</span>
1299
+ </div>
1300
+ <div class="legend-item">
1301
+ <div class="legend-box current-highlight"></div>
1302
+ <span><strong>Your Settings:</strong> Adjust sliders below to see changes</span>
1303
+ </div>
1304
+ <div class="legend-item">
1305
+ <div class="legend-box" style="background: #e3f2fd; border: 2px solid #2196f3;"></div>
1306
+ <span><strong>High Privacy:</strong> Reconstruction fails - data protected!</span>
1307
+ </div>
1308
+ </div>
1309
+
1310
+ <!-- Quality Badge Below -->
1311
+ <div style="text-align: center; margin-top: 1.5rem;">
1312
+ <div class="reconstruction-quality quality-high" id="recon-quality" style="display: inline-block; padding: 0.75rem 1.5rem; font-size: 1.1rem;">
1313
+ ⚠️ Medium Quality
1314
+ </div>
1315
+ </div>
1316
+ </div>
1317
+
1318
+ <div class="attack-demo" style="margin-top: 2rem;">
1319
  <div class="demo-panel">
1320
+ <h3>🛡️ Differential Privacy Settings</h3>
1321
  <div class="demo-controls">
1322
+ <label>Gradient Clipping Norm: <span id="recon-clipping">1.0</span></label>
1323
  <input type="range" class="demo-slider" id="recon-clipping-slider" min="0.1" max="5" step="0.1" value="1.0">
1324
+ <div class="privacy-scale">
1325
+ <span style="font-size: 0.75rem;">🔒 Tight Clipping (More Private)</span>
1326
+ <span style="font-size: 0.75rem;">🔓 Loose Clipping (Less Private)</span>
1327
+ </div>
1328
 
1329
+ <label style="margin-top: 1rem;">Noise Multiplier (σ): <span id="recon-noise-level">1.0</span></label>
1330
  <input type="range" class="demo-slider" id="recon-noise-slider" min="0" max="3" step="0.1" value="1.0">
1331
+ <div class="privacy-scale">
1332
+ <span style="font-size: 0.75rem;">🔇 No Noise (Vulnerable)</span>
1333
+ <span style="font-size: 0.75rem;">🔊 High Noise (Protected)</span>
1334
+ </div>
1335
  </div>
1336
+ <button class="control-button" onclick="runReconstructionAttack()">🔄 Update Reconstruction</button>
1337
  </div>
1338
 
1339
  <div class="demo-panel">
1340
+ <h3>📊 Attack Analysis</h3>
1341
+ <div class="demo-result">
1342
+ <div style="margin-bottom: 1rem;">
1343
+ <strong>Reconstruction SSIM Score:</strong>
1344
+ <div style="font-size: 1.8rem; color: #dc3545; font-weight: bold; margin: 0.5rem 0;" id="ssim-score">0.85</div>
1345
+ <div style="font-size: 0.85rem; color: #666;">
1346
+ (1.0 = perfect reconstruction, 0.0 = completely failed)
1347
  </div>
 
1348
  </div>
1349
+
1350
+ <div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #ddd;">
1351
+ <div style="display: flex; justify-content: space-between; margin: 0.5rem 0;">
1352
+ <span>Privacy Level:</span>
1353
+ <span id="privacy-status" style="font-weight: bold; color: #dc3545;">❌ Low (ε ≈ 8.0)</span>
1354
+ </div>
1355
+ <div style="display: flex; justify-content: space-between; margin: 0.5rem 0;">
1356
+ <span>Attack Success:</span>
1357
+ <span id="attack-success" style="font-weight: bold; color: #dc3545;">92%</span>
1358
  </div>
 
1359
  </div>
1360
  </div>
1361
+
1362
+ <div class="privacy-explanation" id="recon-explanation" style="margin-top: 1rem;">
1363
+ ⚠️ Without sufficient privacy protection, attackers can reconstruct training images with high fidelity from gradient information alone!
1364
+ </div>
1365
  </div>
1366
  </div>
1367
 
1368
  <div class="chart-container">
1369
  <canvas id="reconstruction-chart"></canvas>
1370
  </div>
1371
+
1372
+ <!-- Defense against this attack -->
1373
+ <div style="margin-top: 2rem; padding: 1.5rem; background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); border-radius: 8px; color: white;">
1374
+ <h3 style="margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem;">
1375
+ <span style="font-size: 1.5rem;">🛡️</span> How DP-SGD Defends Against This Attack
1376
+ </h3>
1377
+ <p style="font-size: 1rem; line-height: 1.6; margin: 0;">
1378
+ <strong>Gradient clipping + noise:</strong> DP-SGD clips each person's influence on the model, then drowns it in noise - like taking a photo and adding so much blur that faces become unrecognizable blobs. Attackers cannot reverse-engineer the original training images from the model's outputs.
1379
+ </p>
1380
+ </div>
1381
  </div>
1382
  </div>
1383
 
 
1397
  </div>
1398
 
1399
  <div class="attack-demo">
1400
+ <div class="demo-panel" style="padding: 1.5rem;">
1401
+ <h3 style="margin-bottom: 1.5rem;">Inversion Parameters</h3>
1402
+ <div class="demo-controls" style="gap: 1.5rem;">
1403
+ <div style="margin-bottom: 1.5rem;">
1404
+ <label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
1405
+ Target Class: <span id="inversion-class" style="color: #1976d2; font-weight: 600;">Digit 7</span>
1406
+ </label>
1407
+ <select id="inversion-class-select" class="parameter-select" style="width: 100%; padding: 0.5rem; font-size: 1rem; border-radius: 4px; border: 2px solid #e0e0e0;">
1408
+ <option value="0">Digit 0</option>
1409
+ <option value="1">Digit 1</option>
1410
+ <option value="7" selected>Digit 7</option>
1411
+ <option value="9">Digit 9</option>
1412
+ </select>
1413
+ </div>
1414
 
1415
+ <div style="margin-bottom: 1.5rem;">
1416
+ <label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
1417
+ Privacy Level: <span id="inversion-privacy" style="color: #1976d2; font-weight: 600;">Medium</span>
1418
+ </label>
1419
+ <input type="range" class="demo-slider" id="inversion-privacy-slider" min="1" max="10" step="1" value="5" style="width: 100%; margin-top: 0.5rem;">
1420
+ <div style="display: flex; justify-content: space-between; font-size: 0.75rem; color: #999; margin-top: 0.5rem;">
1421
+ <span style="text-align: left;">🔒 High Privacy<br/>(ε = 1.0)</span>
1422
+ <span style="text-align: right;">🔓 Low Privacy<br/>(ε = 10.0)</span>
1423
+ </div>
1424
+ </div>
1425
  </div>
1426
+ <button class="control-button" onclick="runInversionAttack()" style="width: 100%; padding: 0.75rem; font-size: 1rem; margin-top: 1rem;">Generate Class Representative</button>
1427
  </div>
1428
 
1429
+ <div class="demo-panel" style="padding: 1.5rem;">
1430
+ <h3 style="margin-bottom: 1.5rem;">Generated Features</h3>
1431
  <div class="visual-demo">
1432
  <div class="visual-item">
1433
+ <canvas id="inversion-canvas" width="160" height="160" style="border-radius: 8px; margin: 0 auto 1rem; display: block; border: 3px solid #2196f3; box-shadow: 0 2px 8px rgba(0,0,0,0.1);"></canvas>
1434
+ <div style="font-size: 1rem; font-weight: 500; margin-bottom: 0.75rem;">Inverted Features</div>
1435
+ <div style="font-size: 0.9rem; color: #666; margin-bottom: 0.5rem;">
1436
+ Confidence: <span id="inversion-confidence" style="font-weight: 600; color: #1976d2;">87%</span>
 
 
1437
  </div>
1438
  </div>
1439
  </div>
1440
+
1441
+ <div class="privacy-explanation" style="margin-top: 1.5rem; padding: 1.25rem; background: #fff3e0; border-radius: 6px; font-size: 0.9rem; line-height: 1.5; border-left: 4px solid #ff9800;">
1442
+ <div id="inversion-explanation">
1443
+ ⚠️ Medium privacy. Some class features are visible but degraded. Move the slider left for higher privacy!
1444
+ </div>
1445
+ </div>
1446
  </div>
1447
  </div>
1448
+
1449
+ <!-- Defense against this attack -->
1450
+ <div style="margin-top: 2rem; padding: 1.5rem; background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); border-radius: 8px; color: white;">
1451
+ <h3 style="margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem;">
1452
+ <span style="font-size: 1.5rem;">🛡️</span> How DP-SGD Defends Against This Attack
1453
+ </h3>
1454
+ <p style="font-size: 1rem; line-height: 1.6; margin: 0;">
1455
+ <strong>Feature scrambling:</strong> DP-SGD scrambles what the model "remembers" about each class. Instead of learning "digit 7 looks like this sharp image," it learns "7 is somewhere in this fuzzy cloud." Attackers cannot extract clear class representatives or features.
1456
+ </p>
1457
+ </div>
1458
  </div>
1459
  </div>
1460
 
 
1474
  </div>
1475
 
1476
  <div class="attack-demo">
1477
+ <div class="demo-panel" style="padding: 1.5rem;">
1478
+ <h3 style="margin-bottom: 1.5rem;">Target Properties</h3>
1479
+ <div class="demo-controls" style="gap: 1.5rem;">
1480
+ <div style="margin-bottom: 1.5rem;">
1481
+ <label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">Property Type:</label>
1482
+ <select id="property-type" class="parameter-select" style="width: 100%; padding: 0.5rem; font-size: 1rem; border-radius: 4px; border: 2px solid #e0e0e0;">
1483
+ <option value="gender">Gender Distribution</option>
1484
+ <option value="age">Age Distribution</option>
1485
+ <option value="location">Geographic Distribution</option>
1486
+ </select>
1487
+ </div>
1488
 
1489
+ <div style="margin-bottom: 1.5rem;">
1490
+ <label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
1491
+ Privacy Level: <span id="property-privacy" style="color: #1976d2; font-weight: 600;">Medium</span>
1492
+ </label>
1493
+ <input type="range" class="demo-slider" id="property-privacy-slider" min="1" max="10" step="1" value="5" style="width: 100%; margin-top: 0.5rem;">
1494
+ <div style="display: flex; justify-content: space-between; font-size: 0.75rem; color: #999; margin-top: 0.5rem;">
1495
+ <span style="text-align: left;">🔒 High Privacy<br/>(ε = 1.0)</span>
1496
+ <span style="text-align: right;">🔓 Low Privacy<br/>(ε = 10.0)</span>
1497
+ </div>
1498
+ </div>
1499
+
1500
+ <div style="margin-bottom: 1.5rem;">
1501
+ <label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
1502
+ Model Access: <span id="property-access" style="color: #1976d2; font-weight: 600;">Black-box</span>
1503
+ </label>
1504
+ <input type="range" class="demo-slider" id="property-access-slider" min="1" max="3" step="1" value="1" style="width: 100%; margin-top: 0.5rem;">
1505
+ <div style="display: flex; justify-content: space-between; font-size: 0.75rem; color: #999; margin-top: 0.5rem;">
1506
+ <span>Black-box</span>
1507
+ <span>Gray-box</span>
1508
+ <span>White-box</span>
1509
+ </div>
1510
+ </div>
1511
  </div>
1512
+ <button class="control-button" onclick="runPropertyAttack()" style="width: 100%; padding: 0.75rem; font-size: 1rem; margin-top: 1rem;">Infer Properties</button>
1513
  </div>
1514
 
1515
+ <div class="demo-panel" style="padding: 1.5rem;">
1516
+ <h3 style="margin-bottom: 1.5rem;">Inferred Properties</h3>
1517
  <div class="demo-result">
1518
+ <div style="font-size: 1rem; font-weight: 500; margin-bottom: 1rem;">Inferred Distribution:</div>
1519
+ <div style="margin: 1.5rem 0;">
1520
+ <div style="display: flex; justify-content: space-between; margin: 0.75rem 0; padding: 0.5rem; background: #f5f5f5; border-radius: 4px;">
1521
+ <span style="font-weight: 500;">Male:</span> <span id="property-male" style="font-weight: 600; color: #1976d2;">52% ± 8%</span>
1522
  </div>
1523
+ <div style="display: flex; justify-content: space-between; margin: 0.75rem 0; padding: 0.5rem; background: #f5f5f5; border-radius: 4px;">
1524
+ <span style="font-weight: 500;">Female:</span> <span id="property-female" style="font-weight: 600; color: #1976d2;">48% ± 8%</span>
1525
  </div>
1526
  </div>
1527
+ <div style="font-size: 0.85rem; color: #666; font-style: italic;">
1528
  Confidence intervals show attack uncertainty
1529
  </div>
1530
  </div>
1531
+
1532
+ <div class="privacy-explanation" style="margin-top: 1.5rem; padding: 1.25rem; background: #fff3e0; border-radius: 6px; font-size: 0.9rem; line-height: 1.5; border-left: 4px solid #ff9800;">
1533
+ <div id="property-explanation">
1534
+ ⚠️ Medium privacy. The attacker can infer dataset properties with moderate accuracy. Move the slider left for higher privacy!
1535
+ </div>
1536
+ </div>
1537
  </div>
1538
  </div>
1539
 
1540
  <div class="chart-container">
1541
  <canvas id="property-chart"></canvas>
1542
  </div>
1543
+
1544
+ <!-- Defense against this attack -->
1545
+ <div style="margin-top: 2rem; padding: 1.5rem; background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); border-radius: 8px; color: white;">
1546
+ <h3 style="margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem;">
1547
+ <span style="font-size: 1.5rem;">🛡️</span> How DP-SGD Defends Against This Attack
1548
+ </h3>
1549
+ <p style="font-size: 1rem; line-height: 1.6; margin: 0;">
1550
+ <strong>Privacy budget tracking:</strong> DP-SGD keeps a "privacy budget" (ε) - like a bank account that tracks how much information leaked. When the budget runs low, training stops to prevent dataset statistics (like gender distribution or age ranges) from being exposed.
1551
+ </p>
1552
+ </div>
1553
  </div>
1554
  </div>
1555
 
 
1625
  <div class="chart-container">
1626
  <canvas id="linkage-chart"></canvas>
1627
  </div>
1628
+
1629
+ <!-- Defense against this attack -->
1630
+ <div style="margin-top: 2rem; padding: 1.5rem; background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); border-radius: 8px; color: white;">
1631
+ <h3 style="margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem;">
1632
+ <span style="font-size: 1.5rem;">🛡️</span> How DP-SGD Defends Against This Attack
1633
+ </h3>
1634
+ <p style="font-size: 1rem; line-height: 1.6; margin: 0;">
1635
+ <strong>Output noise + bounded leakage:</strong> DP-SGD adds noise to model predictions AND tracks total privacy loss (ε). Even when attackers combine model outputs with external datasets (social media, public records), the mathematical privacy guarantee ensures they cannot reliably link individuals or infer sensitive attributes.
1636
+ </p>
1637
+ </div>
1638
  </div>
1639
  </div>
1640
 
 
1682
  </div>
1683
  </div>
1684
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1685
  </div>
1686
  {% endblock %}
1687