refactor: implement minimal PWA caching strategy

- Cache only essential PWA assets (manifest, icons, core scripts)
- Use Network First for all other requests
- Remove aggressive caching of UI components and styles
- Preserve PWA installation while minimizing cache footprint
This commit is contained in:
lockbitchat
2025-10-13 01:35:32 -04:00
parent d24d578321
commit 7af8f528ff
11 changed files with 406 additions and 542 deletions

View File

@@ -157,6 +157,7 @@
<script src="src/scripts/pwa-register.js"></script> <script src="src/scripts/pwa-register.js"></script>
<script src="./src/pwa/install-prompt.js" type="module"></script> <script src="./src/pwa/install-prompt.js" type="module"></script>
<script src="./src/pwa/pwa-manager.js" type="module"></script> <script src="./src/pwa/pwa-manager.js" type="module"></script>
<script src="./src/scripts/pwa-offline-test.js"></script>
<link rel="stylesheet" href="./src/styles/pwa.css"> <link rel="stylesheet" href="./src/styles/pwa.css">
</body> </body>
</html> </html>

View File

@@ -1421,7 +1421,6 @@
// PAKE password states removed - using SAS verification instead // PAKE password states removed - using SAS verification instead
// Session state - all security features enabled by default // Session state - all security features enabled by default
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
const [pendingSession, setPendingSession] = React.useState(null); const [pendingSession, setPendingSession] = React.useState(null);
// All security features are enabled by default - no payment required // All security features are enabled by default - no payment required
@@ -1730,8 +1729,6 @@
setVerificationCode(''); setVerificationCode('');
setSecurityLevel(null); setSecurityLevel(null);
// Reset session and timer
setSessionTimeLeft(0);
// Return to main page after a short delay // Return to main page after a short delay
setTimeout(() => { setTimeout(() => {
@@ -3471,7 +3468,6 @@
isConnected: isConnectedAndVerified, isConnected: isConnectedAndVerified,
securityLevel: securityLevel, securityLevel: securityLevel,
// sessionManager removed - all features enabled by default // sessionManager removed - all features enabled by default
sessionTimeLeft: sessionTimeLeft,
webrtcManager: webrtcManagerRef.current webrtcManager: webrtcManagerRef.current
}), }),

View File

@@ -5,13 +5,8 @@ const EnhancedMinimalHeader = ({
onDisconnect, onDisconnect,
isConnected, isConnected,
securityLevel, securityLevel,
sessionManager,
sessionTimeLeft,
webrtcManager webrtcManager
}) => { }) => {
const [currentTimeLeft, setCurrentTimeLeft] = React.useState(sessionTimeLeft || 0);
const [hasActiveSession, setHasActiveSession] = React.useState(false);
const [sessionType, setSessionType] = React.useState('unknown');
const [realSecurityLevel, setRealSecurityLevel] = React.useState(null); const [realSecurityLevel, setRealSecurityLevel] = React.useState(null);
const [lastSecurityUpdate, setLastSecurityUpdate] = React.useState(0); const [lastSecurityUpdate, setLastSecurityUpdate] = React.useState(0);
@@ -268,8 +263,7 @@ const EnhancedMinimalHeader = ({
details: 'Security verification not available', details: 'Security verification not available',
isRealData: false, isRealData: false,
passedChecks: 0, passedChecks: 0,
totalChecks: 0, totalChecks: 0
sessionType: 'unknown'
}; };
console.log('Using fallback security data:', securityData); console.log('Using fallback security data:', securityData);
} }
@@ -277,7 +271,6 @@ const EnhancedMinimalHeader = ({
// Detailed information about the REAL security check // Detailed information about the REAL security check
let message = `REAL-TIME SECURITY VERIFICATION\n\n`; let message = `REAL-TIME SECURITY VERIFICATION\n\n`;
message += `Security Level: ${securityData.level} (${securityData.score}%)\n`; message += `Security Level: ${securityData.level} (${securityData.score}%)\n`;
message += `Session Type: ${securityData.sessionType || 'premium'}\n`;
message += `Verification Time: ${new Date(securityData.timestamp).toLocaleTimeString()}\n`; message += `Verification Time: ${new Date(securityData.timestamp).toLocaleTimeString()}\n`;
message += `Data Source: ${securityData.isRealData ? 'Real Cryptographic Tests' : 'Simulated Data'}\n\n`; message += `Data Source: ${securityData.isRealData ? 'Real Cryptographic Tests' : 'Simulated Data'}\n\n`;
@@ -465,7 +458,6 @@ const EnhancedMinimalHeader = ({
const config = getStatusConfig(); const config = getStatusConfig();
const displaySecurityLevel = isConnected ? (realSecurityLevel || securityLevel) : null; const displaySecurityLevel = isConnected ? (realSecurityLevel || securityLevel) : null;
const shouldShowTimer = hasActiveSession && currentTimeLeft > 0 && window.SessionTimer;
// ============================================ // ============================================
// DATA RELIABILITY INDICATOR // DATA RELIABILITY INDICATOR
@@ -570,13 +562,6 @@ const EnhancedMinimalHeader = ({
key: 'status-section', key: 'status-section',
className: 'flex items-center space-x-2 sm:space-x-3' className: 'flex items-center space-x-2 sm:space-x-3'
}, [ }, [
// Session Timer - all features enabled by default
shouldShowTimer && React.createElement(window.SessionTimer, {
key: 'session-timer',
timeLeft: currentTimeLeft,
sessionType: sessionType,
onDisconnect: onDisconnect
}),
displaySecurityLevel && React.createElement('div', { displaySecurityLevel && React.createElement('div', {
key: 'security-level', key: 'security-level',

View File

@@ -1,334 +0,0 @@
// SessionTimer Component - v4.3.120 - ECDH + DTLS + SAS
const SessionTimer = ({ timeLeft, sessionType, sessionManager, onDisconnect }) => {
const [currentTime, setCurrentTime] = React.useState(timeLeft || 0);
const [showExpiredMessage, setShowExpiredMessage] = React.useState(false);
const [initialized, setInitialized] = React.useState(false);
const [connectionBroken, setConnectionBroken] = React.useState(false);
const [loggedHidden, setLoggedHidden] = React.useState(false);
React.useEffect(() => {
if (connectionBroken) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer initialization skipped - connection broken');
setLoggedHidden(true);
}
return;
}
let initialTime = 0;
if (sessionManager?.hasActiveSession()) {
initialTime = sessionManager.getTimeLeft();
} else if (timeLeft && timeLeft > 0) {
initialTime = timeLeft;
}
if (initialTime <= 0) {
setCurrentTime(0);
setInitialized(false);
setLoggedHidden(true);
return;
}
if (connectionBroken) {
setCurrentTime(0);
setInitialized(false);
setLoggedHidden(true);
return;
}
setCurrentTime(initialTime);
setInitialized(true);
setLoggedHidden(false);
}, [sessionManager, connectionBroken]);
React.useEffect(() => {
if (connectionBroken) {
if (!loggedHidden) {
setLoggedHidden(true);
}
return;
}
if (timeLeft && timeLeft > 0) {
setCurrentTime(timeLeft);
}
setLoggedHidden(false);
}, [timeLeft, connectionBroken]);
React.useEffect(() => {
if (!initialized) {
return;
}
if (connectionBroken) {
if (!loggedHidden) {
setLoggedHidden(true);
}
return;
}
if (!currentTime || currentTime <= 0 || !sessionManager) {
return;
}
const interval = setInterval(() => {
if (connectionBroken) {
setCurrentTime(0);
clearInterval(interval);
return;
}
if (sessionManager?.hasActiveSession()) {
const newTime = sessionManager.getTimeLeft();
setCurrentTime(newTime);
if (window.DEBUG_MODE && Math.floor(Date.now() / 30000) !== Math.floor((Date.now() - 1000) / 30000)) {
console.log('⏱️ Timer tick:', Math.floor(newTime / 1000) + 's');
}
if (newTime <= 0) {
setShowExpiredMessage(true);
setTimeout(() => setShowExpiredMessage(false), 5000);
clearInterval(interval);
}
} else {
setCurrentTime(0);
clearInterval(interval);
}
}, 1000);
return () => {
clearInterval(interval);
};
}, [initialized, currentTime, sessionManager, connectionBroken]);
React.useEffect(() => {
const handleSessionTimerUpdate = (event) => {
if (connectionBroken) {
return;
}
if (event.detail.timeLeft && event.detail.timeLeft > 0) {
setCurrentTime(event.detail.timeLeft);
}
};
const handleForceHeaderUpdate = (event) => {
if (connectionBroken) {
return;
}
if (sessionManager && sessionManager.hasActiveSession()) {
const newTime = sessionManager.getTimeLeft();
setCurrentTime(newTime);
} else {
setCurrentTime(event.detail.timeLeft);
}
};
const handlePeerDisconnect = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setLoggedHidden(false);
};
const handleNewConnection = (event) => {
setConnectionBroken(false);
setLoggedHidden(false);
};
const handleConnectionCleaned = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
const handleSessionReset = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
const handleSessionCleanup = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
const handleDisconnected = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
document.addEventListener('session-timer-update', handleSessionTimerUpdate);
document.addEventListener('force-header-update', handleForceHeaderUpdate);
document.addEventListener('peer-disconnect', handlePeerDisconnect);
document.addEventListener('new-connection', handleNewConnection);
document.addEventListener('connection-cleaned', handleConnectionCleaned);
document.addEventListener('session-reset', handleSessionReset);
document.addEventListener('session-cleanup', handleSessionCleanup);
document.addEventListener('disconnected', handleDisconnected);
return () => {
document.removeEventListener('session-timer-update', handleSessionTimerUpdate);
document.removeEventListener('force-header-update', handleForceHeaderUpdate);
document.removeEventListener('peer-disconnect', handlePeerDisconnect);
document.removeEventListener('new-connection', handleNewConnection);
document.removeEventListener('connection-cleaned', handleConnectionCleaned);
document.removeEventListener('session-reset', handleSessionReset);
document.removeEventListener('session-cleanup', handleSessionCleanup);
document.removeEventListener('disconnected', handleDisconnected);
};
}, [sessionManager]);
if (showExpiredMessage) {
return React.createElement('div', {
className: 'session-timer expired flex items-center space-x-2 px-3 py-1.5 rounded-lg animate-pulse',
style: { background: 'linear-gradient(135deg, rgba(239, 68, 68, 0.2) 0%, rgba(220, 38, 38, 0.2) 100%)' }
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-exclamation-triangle text-red-400'
}),
React.createElement('span', {
key: 'message',
className: 'text-red-400 text-sm font-medium'
}, 'Session Expired!')
]);
}
if (!sessionManager) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - no sessionManager');
setLoggedHidden(true);
}
return null;
}
if (connectionBroken) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - connection broken');
setLoggedHidden(true);
}
return null;
}
if (!currentTime || currentTime <= 0) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - no time left, currentTime:', currentTime);
setLoggedHidden(true);
}
return null;
}
if (loggedHidden) {
setLoggedHidden(false);
}
const totalMinutes = Math.floor(currentTime / (60 * 1000));
const totalSeconds = Math.floor(currentTime / 1000);
const isDemo = sessionType === 'demo';
const isWarning = isDemo ? totalMinutes <= 2 : totalMinutes <= 10;
const isCritical = isDemo ? totalSeconds <= 60 : totalMinutes <= 5;
const formatTime = (ms) => {
const hours = Math.floor(ms / (60 * 60 * 1000));
const minutes = Math.floor((ms % (60 * 60 * 1000)) / (60 * 1000));
const seconds = Math.floor((ms % (60 * 1000)) / 1000);
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
} else {
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
};
const getTimerStyle = () => {
const totalDuration = sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000;
const timeProgress = (totalDuration - currentTime) / totalDuration;
let backgroundColor, textColor, iconColor, iconClass, shouldPulse;
if (timeProgress <= 0.33) {
backgroundColor = 'linear-gradient(135deg, rgba(34, 197, 94, 0.15) 0%, rgba(22, 163, 74, 0.15) 100%)';
textColor = 'text-green-400';
iconColor = 'text-green-400';
iconClass = 'fas fa-clock';
shouldPulse = false;
} else if (timeProgress <= 0.66) {
backgroundColor = 'linear-gradient(135deg, rgba(234, 179, 8, 0.15) 0%, rgba(202, 138, 4, 0.15) 100%)';
textColor = 'text-yellow-400';
iconColor = 'text-yellow-400';
iconClass = 'fas fa-clock';
shouldPulse = false;
} else {
backgroundColor = 'linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(220, 38, 38, 0.15) 100%)';
textColor = 'text-red-400';
iconColor = 'text-red-400';
iconClass = 'fas fa-exclamation-triangle';
shouldPulse = true;
}
return { backgroundColor, textColor, iconColor, iconClass, shouldPulse };
};
const timerStyle = getTimerStyle();
const handleTimerClick = () => {
if (onDisconnect && typeof onDisconnect === 'function') {
onDisconnect();
}
};
return React.createElement('div', {
className: `session-timer flex items-center space-x-2 px-3 py-1.5 rounded-lg transition-all duration-500 cursor-pointer hover:opacity-80 ${
isDemo ? 'demo-session' : ''
} ${timerStyle.shouldPulse ? 'animate-pulse' : ''}`,
style: { background: timerStyle.backgroundColor },
onClick: handleTimerClick,
title: 'Click to disconnect and clear session'
}, [
React.createElement('i', {
key: 'icon',
className: `${timerStyle.iconClass} ${timerStyle.iconColor}`
}),
React.createElement('span', {
key: 'time',
className: `text-sm font-mono font-semibold ${timerStyle.textColor}`
}, formatTime(currentTime)),
React.createElement('div', {
key: 'progress',
className: 'ml-2 w-16 h-1 bg-gray-700 rounded-full overflow-hidden'
}, [
React.createElement('div', {
key: 'progress-bar',
className: `${timerStyle.textColor.replace('text-', 'bg-')} h-full rounded-full transition-all duration-500`,
style: {
width: `${Math.max(0, Math.min(100, (currentTime / (sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000)) * 100))}%`
}
})
])
]);
};
window.SessionTimer = SessionTimer;
window.updateSessionTimer = (newTimeLeft, newSessionType) => {
document.dispatchEvent(new CustomEvent('session-timer-update', {
detail: { timeLeft: newTimeLeft, sessionType: newSessionType }
}));
};

View File

@@ -167,8 +167,6 @@ class EnhancedSecureWebRTCManager {
} : null; } : null;
}; };
this._secureLog('info', '🔒 Enhanced WebRTC Manager initialized with secure API'); this._secureLog('info', '🔒 Enhanced WebRTC Manager initialized with secure API');
this.currentSessionType = null;
this.currentSecurityLevel = 'basic';
this.sessionConstraints = null; this.sessionConstraints = null;
this.peerConnection = null; this.peerConnection = null;
this.dataChannel = null; this.dataChannel = null;
@@ -2311,7 +2309,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Create simple getSecurityStatus method // Create simple getSecurityStatus method
secureAPI.getSecurityStatus = () => ({ secureAPI.getSecurityStatus = () => ({
securityLevel: this.currentSecurityLevel || 'basic', securityLevel: 'maximum',
stage: 'initialized', stage: 'initialized',
activeFeaturesCount: Object.values(this.securityFeatures || {}).filter(Boolean).length activeFeaturesCount: Object.values(this.securityFeatures || {}).filter(Boolean).length
}); });
@@ -4484,11 +4482,8 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
} }
// Security configuration - all features enabled by default // Security configuration - all features enabled by default
configureSecurityForSession(sessionType, securityLevel) { configureSecurityForSession() {
this._secureLog('info', `🔧 Configuring security for ${sessionType} session (${securityLevel} level)`); this._secureLog('info', '🔧 Configuring security - all features enabled by default');
this.currentSessionType = sessionType;
this.currentSecurityLevel = securityLevel;
// All security features are enabled by default - no payment required // All security features are enabled by default - no payment required
this.sessionConstraints = {}; this.sessionConstraints = {};
@@ -4499,7 +4494,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this.applySessionConstraints(); this.applySessionConstraints();
this._secureLog('info', `✅ Security configured for ${sessionType} - all features enabled`, { constraints: this.sessionConstraints }); this._secureLog('info', '✅ Security configured - all features enabled', { constraints: this.sessionConstraints });
if (!this._validateCryptographicSecurity()) { if (!this._validateCryptographicSecurity()) {
this._secureLog('error', '🚨 CRITICAL: Cryptographic security validation failed after session configuration'); this._secureLog('error', '🚨 CRITICAL: Cryptographic security validation failed after session configuration');
@@ -4507,7 +4502,6 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
if (this.onStatusChange) { if (this.onStatusChange) {
this.onStatusChange('security_breach', { this.onStatusChange('security_breach', {
type: 'crypto_security_failure', type: 'crypto_security_failure',
sessionType: sessionType,
message: 'Cryptographic security validation failed after session configuration' message: 'Cryptographic security validation failed after session configuration'
}); });
} }
@@ -4644,27 +4638,21 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Security Level Notification // Security Level Notification
notifySecurityLevel() { notifySecurityLevel() {
// Avoid duplicate notifications for the same security level // Avoid duplicate notifications
if (this.lastSecurityLevelNotification === this.currentSecurityLevel) { if (this.lastSecurityLevelNotification === 'maximum') {
return; // prevent duplication return; // prevent duplication
} }
this.lastSecurityLevelNotification = this.currentSecurityLevel; this.lastSecurityLevelNotification = 'maximum';
const levelMessages = { const message = '🛡️ Maximum Security Active - All features enabled';
'basic': '🔒 Basic Security Active - Demo session with essential protection',
'enhanced': '🔐 Enhanced Security Active - Paid session with advanced protection',
'maximum': '🛡️ Maximum Security Active - Premium session with complete protection'
};
const message = levelMessages[this.currentSecurityLevel] || levelMessages['basic'];
if (this.onMessage) { if (this.onMessage) {
this.deliverMessageToUI(message, 'system'); this.deliverMessageToUI(message, 'system');
} }
// Showing details of functions for paid sessions // Showing details of active features
if (this.currentSecurityLevel !== 'basic' && this.onMessage) { if (this.onMessage) {
const activeFeatures = Object.entries(this.securityFeatures) const activeFeatures = Object.entries(this.securityFeatures)
.filter(([key, value]) => value === true) .filter(([key, value]) => value === true)
.map(([key]) => key.replace('has', '').replace(/([A-Z])/g, ' $1').trim().toLowerCase()) .map(([key]) => key.replace('has', '').replace(/([A-Z])/g, ' $1').trim().toLowerCase())
@@ -6520,11 +6508,10 @@ async processMessage(data) {
if (this.sessionConstraints?.hasAntiFingerprinting) { if (this.sessionConstraints?.hasAntiFingerprinting) {
this.securityFeatures.hasAntiFingerprinting = true; this.securityFeatures.hasAntiFingerprinting = true;
this.antiFingerprintingConfig.enabled = true; this.antiFingerprintingConfig.enabled = true;
if (this.currentSecurityLevel === 'enhanced') { // Enable full anti-fingerprinting features
this.antiFingerprintingConfig.randomizeSizes = false; this.antiFingerprintingConfig.randomizeSizes = true;
this.antiFingerprintingConfig.maskPatterns = false; this.antiFingerprintingConfig.maskPatterns = true;
this.antiFingerprintingConfig.useRandomHeaders = false; this.antiFingerprintingConfig.useRandomHeaders = true;
}
} }
this.notifySecurityUpgrade(2); this.notifySecurityUpgrade(2);
@@ -6535,10 +6522,7 @@ async processMessage(data) {
// Method to enable Stage 3 features (traffic obfuscation) // Method to enable Stage 3 features (traffic obfuscation)
enableStage3Security() { enableStage3Security() {
if (this.currentSecurityLevel !== 'maximum') { this._secureLog('info', '🔒 Enabling Stage 3 features (traffic obfuscation)');
this._secureLog('info', '🔒 Stage 3 features only available for premium sessions');
return;
}
if (this.sessionConstraints?.hasMessageChunking) { if (this.sessionConstraints?.hasMessageChunking) {
this.securityFeatures.hasMessageChunking = true; this.securityFeatures.hasMessageChunking = true;
@@ -6559,10 +6543,7 @@ async processMessage(data) {
// Method for enabling Stage 4 functions (maximum safety) // Method for enabling Stage 4 functions (maximum safety)
enableStage4Security() { enableStage4Security() {
if (this.currentSecurityLevel !== 'maximum') { this._secureLog('info', '🔒 Enabling Stage 4 features (maximum safety)');
this._secureLog('info', '🔒 Stage 4 features only available for premium sessions');
return;
}
if (this.sessionConstraints?.hasDecoyChannels && this.isConnected() && this.isVerified) { if (this.sessionConstraints?.hasDecoyChannels && this.isConnected() && this.isVerified) {
this.securityFeatures.hasDecoyChannels = true; this.securityFeatures.hasDecoyChannels = true;
@@ -6603,14 +6584,11 @@ async processMessage(data) {
.filter(([key, value]) => value === true) .filter(([key, value]) => value === true)
.map(([key]) => key); .map(([key]) => key);
const stage = this.currentSecurityLevel === 'basic' ? 1 : const stage = 4; // Maximum security stage
this.currentSecurityLevel === 'enhanced' ? 2 :
this.currentSecurityLevel === 'maximum' ? 4 : 1;
return { return {
stage: stage, stage: stage,
sessionType: this.currentSessionType, securityLevel: 'maximum',
securityLevel: this.currentSecurityLevel,
activeFeatures: activeFeatures, activeFeatures: activeFeatures,
totalFeatures: Object.keys(this.securityFeatures).length, totalFeatures: Object.keys(this.securityFeatures).length,
activeFeaturesCount: activeFeatures.length, activeFeaturesCount: activeFeatures.length,
@@ -6730,52 +6708,42 @@ async processMessage(data) {
// Method for automatic feature enablement with stability check // Method for automatic feature enablement with stability check
async autoEnableSecurityFeatures() { async autoEnableSecurityFeatures() {
if (this.currentSessionType === 'demo') { this._secureLog('info', 'Starting graduated security activation - all features enabled');
this._secureLog('info', 'Demo session - keeping basic security only');
const checkStability = () => {
const isStable = this.isConnected() &&
this.isVerified &&
this.connectionAttempts === 0 &&
this.messageQueue.length === 0 &&
this.peerConnection?.connectionState === 'connected';
return isStable;
};
await this.calculateAndReportSecurityLevel(); await this.calculateAndReportSecurityLevel();
this.notifySecurityUpgrade(1); this.notifySecurityUpgrade(1);
return;
} // Enable all security stages progressively
const checkStability = () => {
const isStable = this.isConnected() &&
this.isVerified &&
this.connectionAttempts === 0 &&
this.messageQueue.length === 0 &&
this.peerConnection?.connectionState === 'connected';
return isStable;
};
this._secureLog('info', ` ${this.currentSessionType} session - starting graduated security activation`);
await this.calculateAndReportSecurityLevel();
this.notifySecurityUpgrade(1);
if (this.currentSecurityLevel === 'enhanced' || this.currentSecurityLevel === 'maximum') {
setTimeout(async () => { setTimeout(async () => {
if (checkStability()) { if (checkStability()) {
this.enableStage2Security(); this.enableStage2Security();
await this.calculateAndReportSecurityLevel(); await this.calculateAndReportSecurityLevel();
// For maximum sessions, turn on Stage 3 and 4 setTimeout(async () => {
if (this.currentSecurityLevel === 'maximum') { if (checkStability()) {
setTimeout(async () => { this.enableStage3Security();
if (checkStability()) { await this.calculateAndReportSecurityLevel();
this.enableStage3Security();
await this.calculateAndReportSecurityLevel(); setTimeout(async () => {
if (checkStability()) {
setTimeout(async () => { this.enableStage4Security();
if (checkStability()) { await this.calculateAndReportSecurityLevel();
this.enableStage4Security(); }
await this.calculateAndReportSecurityLevel(); }, 20000);
} }
}, 20000); }, 15000);
}
}, 15000);
}
} }
}, 10000); }, 10000);
} }
}
// ============================================ // ============================================
// CONNECTION MANAGEMENT WITH ENHANCED SECURITY // CONNECTION MANAGEMENT WITH ENHANCED SECURITY
@@ -11644,11 +11612,10 @@ async processMessage(data) {
// Update session state // Update session state
this.currentSession = sessionData; this.currentSession = sessionData;
this.sessionManager = sessionData.sessionManager;
// FIX: More lenient checks for activation // FIX: More lenient checks for activation
const hasKeys = !!(this.encryptionKey && this.macKey); const hasKeys = !!(this.encryptionKey && this.macKey);
const hasSession = !!(this.sessionManager && (this.sessionManager.hasActiveSession?.() || sessionData.sessionId)); const hasSession = !!(sessionData.sessionId);
// Force connection status if there is an active session // Force connection status if there is an active session
if (hasSession) { if (hasSession) {
@@ -12508,7 +12475,7 @@ class SecureKeyStorage {
// Additional info // Additional info
connectionId: this.connectionId, connectionId: this.connectionId,
keyFingerprint: this.keyFingerprint, keyFingerprint: this.keyFingerprint,
currentSecurityLevel: this.currentSecurityLevel, currentSecurityLevel: 'maximum',
timestamp: Date.now() timestamp: Date.now()
}; };

View File

@@ -3,7 +3,6 @@ import { EnhancedSecureWebRTCManager } from '../network/EnhancedSecureWebRTCMana
import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js'; import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js';
// Import UI components (side-effect: they attach themselves to window.*) // Import UI components (side-effect: they attach themselves to window.*)
import '../components/ui/SessionTimer.jsx';
import '../components/ui/Header.jsx'; import '../components/ui/Header.jsx';
import '../components/ui/DownloadApps.jsx'; import '../components/ui/DownloadApps.jsx';
import '../components/ui/UniqueFeatureSlider.jsx'; import '../components/ui/UniqueFeatureSlider.jsx';

View File

@@ -3,10 +3,9 @@
(async () => { (async () => {
try { try {
const timestamp = Date.now(); const timestamp = Date.now();
const [cryptoModule, webrtcModule, paymentModule, fileTransferModule] = await Promise.all([ const [cryptoModule, webrtcModule, fileTransferModule] = await Promise.all([
import(`../crypto/EnhancedSecureCryptoUtils.js?v=${timestamp}`), import(`../crypto/EnhancedSecureCryptoUtils.js?v=${timestamp}`),
import(`../network/EnhancedSecureWebRTCManager.js?v=${timestamp}`), import(`../network/EnhancedSecureWebRTCManager.js?v=${timestamp}`),
import(`../session/PayPerSessionManager.js?v=${timestamp}`),
import(`../transfer/EnhancedSecureFileTransfer.js?v=${timestamp}`), import(`../transfer/EnhancedSecureFileTransfer.js?v=${timestamp}`),
]); ]);
@@ -14,8 +13,6 @@
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils; window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
const { EnhancedSecureWebRTCManager } = webrtcModule; const { EnhancedSecureWebRTCManager } = webrtcModule;
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager; window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
const { PayPerSessionManager } = paymentModule;
window.PayPerSessionManager = PayPerSessionManager;
const { EnhancedSecureFileTransfer } = fileTransferModule; const { EnhancedSecureFileTransfer } = fileTransferModule;
window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer; window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer;
@@ -28,11 +25,7 @@
} }
await Promise.all([ await Promise.all([
loadReactComponent('../components/ui/SessionTimer.jsx'),
loadReactComponent('../components/ui/Header.jsx'), loadReactComponent('../components/ui/Header.jsx'),
loadReactComponent('../components/ui/SessionTypeSelector.jsx'),
loadReactComponent('../components/ui/LightningPayment.jsx'),
loadReactComponent('../components/ui/PaymentModal.jsx'),
loadReactComponent('../components/ui/DownloadApps.jsx'), loadReactComponent('../components/ui/DownloadApps.jsx'),
loadReactComponent('../components/ui/ComparisonTable.jsx'), loadReactComponent('../components/ui/ComparisonTable.jsx'),
loadReactComponent('../components/ui/UniqueFeatureSlider.jsx'), loadReactComponent('../components/ui/UniqueFeatureSlider.jsx'),

View File

@@ -1,27 +1,13 @@
window.forceUpdateHeader = (timeLeft, sessionType) => { window.forceUpdateHeader = () => {
const event = new CustomEvent('force-header-update', { const event = new CustomEvent('force-header-update', {
detail: { timeLeft, sessionType, timestamp: Date.now() }, detail: { timestamp: Date.now() },
}); });
document.dispatchEvent(event); document.dispatchEvent(event);
if (window.sessionManager && window.sessionManager.forceUpdateTimer) {
window.sessionManager.forceUpdateTimer();
}
};
window.updateSessionTimer = (timeLeft, sessionType) => {
document.dispatchEvent(
new CustomEvent('session-timer-update', {
detail: { timeLeft, sessionType },
}),
);
}; };
document.addEventListener('session-activated', (event) => { document.addEventListener('session-activated', (event) => {
if (window.updateSessionTimer) {
window.updateSessionTimer(event.detail.timeLeft, event.detail.sessionType);
}
if (window.forceUpdateHeader) { if (window.forceUpdateHeader) {
window.forceUpdateHeader(event.detail.timeLeft, event.detail.sessionType); window.forceUpdateHeader();
} }
if (window.webrtcManager && window.webrtcManager.handleSessionActivation) { if (window.webrtcManager && window.webrtcManager.handleSessionActivation) {
if (window.DEBUG_MODE) { if (window.DEBUG_MODE) {
@@ -29,9 +15,6 @@ document.addEventListener('session-activated', (event) => {
} }
window.webrtcManager.handleSessionActivation({ window.webrtcManager.handleSessionActivation({
sessionId: event.detail.sessionId, sessionId: event.detail.sessionId,
sessionType: event.detail.sessionType,
timeLeft: event.detail.timeLeft,
isDemo: event.detail.isDemo,
sessionManager: window.sessionManager, sessionManager: window.sessionManager,
}); });
} }

View File

@@ -0,0 +1,270 @@
// PWA Offline Test Script for SecureBit.chat
// Enhanced Security Edition v4.3.120
// Tests offline functionality and cache status
class PWAOfflineTester {
constructor() {
this.testResults = [];
this.isRunning = false;
}
async runTests() {
if (this.isRunning) {
console.warn('⚠️ Tests already running');
return;
}
this.isRunning = true;
this.testResults = [];
console.log('🧪 Starting PWA Offline Tests...');
try {
await this.testServiceWorkerRegistration();
await this.testCacheStatus();
await this.testOfflineResources();
await this.testOnlineResources();
this.showTestResults();
} catch (error) {
console.error('❌ Test failed:', error);
this.addTestResult('Test Suite', false, `Test suite failed: ${error.message}`);
} finally {
this.isRunning = false;
}
}
async testServiceWorkerRegistration() {
try {
if ('serviceWorker' in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
this.addTestResult('Service Worker Registration', true, 'Service worker is registered');
} else {
this.addTestResult('Service Worker Registration', false, 'No service worker found');
}
} else {
this.addTestResult('Service Worker Support', false, 'Service worker not supported');
}
} catch (error) {
this.addTestResult('Service Worker Registration', false, `Error: ${error.message}`);
}
}
async testCacheStatus() {
try {
if ('caches' in window) {
const cacheNames = await caches.keys();
const totalCached = 0;
for (const cacheName of cacheNames) {
const cache = await caches.open(cacheName);
const keys = await cache.keys();
totalCached += keys.length;
}
if (totalCached > 0) {
this.addTestResult('Cache Status', true, `${totalCached} resources cached`);
} else {
this.addTestResult('Cache Status', false, 'No resources cached');
}
} else {
this.addTestResult('Cache API Support', false, 'Cache API not supported');
}
} catch (error) {
this.addTestResult('Cache Status', false, `Error: ${error.message}`);
}
}
async testOfflineResources() {
const criticalResources = [
'/',
'/index.html',
'/manifest.json',
'/dist/app.js',
'/dist/app-boot.js',
'/libs/react/react.production.min.js',
'/libs/react-dom/react-dom.production.min.js',
'/assets/tailwind.css'
];
let cachedCount = 0;
for (const resource of criticalResources) {
try {
const cached = await caches.match(resource);
if (cached) {
cachedCount++;
}
} catch (error) {
console.warn(`⚠️ Failed to check cache for ${resource}:`, error);
}
}
const success = cachedCount >= criticalResources.length * 0.8; // 80% success rate
this.addTestResult('Critical Resources Cached', success,
`${cachedCount}/${criticalResources.length} critical resources cached`);
}
async testOnlineResources() {
try {
// Test if we can fetch a simple resource
const response = await fetch('/favicon.ico', {
method: 'HEAD',
cache: 'no-cache'
});
if (response.ok) {
this.addTestResult('Network Connectivity', true, 'Network is accessible');
} else {
this.addTestResult('Network Connectivity', false, `Network error: ${response.status}`);
}
} catch (error) {
this.addTestResult('Network Connectivity', false, `Network error: ${error.message}`);
}
}
addTestResult(testName, passed, message) {
this.testResults.push({
name: testName,
passed,
message,
timestamp: new Date().toISOString()
});
console.log(`${passed ? '✅' : '❌'} ${testName}: ${message}`);
}
showTestResults() {
const passedTests = this.testResults.filter(test => test.passed).length;
const totalTests = this.testResults.length;
const successRate = Math.round((passedTests / totalTests) * 100);
const modal = document.createElement('div');
modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 backdrop-blur-sm';
modal.innerHTML = `
<div class="bg-gray-800 rounded-xl p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
<div class="flex items-center mb-6">
<div class="w-12 h-12 bg-blue-500/10 rounded-full flex items-center justify-center mr-4">
<i class="fas fa-vial text-blue-400 text-xl"></i>
</div>
<h3 class="text-xl font-semibold text-white">PWA Offline Test Results</h3>
</div>
<div class="mb-6 p-4 rounded-lg ${successRate >= 80 ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}">
<div class="flex items-center justify-between">
<div>
<div class="font-medium text-white">Overall Score</div>
<div class="text-sm text-gray-300">${passedTests}/${totalTests} tests passed</div>
</div>
<div class="text-2xl font-bold ${successRate >= 80 ? 'text-green-400' : 'text-red-400'}">
${successRate}%
</div>
</div>
</div>
<div class="space-y-3">
${this.testResults.map(test => `
<div class="flex items-center justify-between p-3 rounded-lg ${test.passed ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}">
<div class="flex items-center space-x-3">
<i class="fas ${test.passed ? 'fa-check-circle text-green-400' : 'fa-times-circle text-red-400'}"></i>
<div>
<div class="font-medium text-white">${test.name}</div>
<div class="text-sm text-gray-300">${test.message}</div>
</div>
</div>
</div>
`).join('')}
</div>
<div class="mt-6 space-y-3">
<div class="bg-blue-500/10 border border-blue-500/20 rounded-lg p-4">
<h4 class="font-medium text-blue-300 mb-2">Recommendations:</h4>
<ul class="text-sm text-blue-200 space-y-1">
${this.getRecommendations().map(rec => `<li>• ${rec}</li>`).join('')}
</ul>
</div>
<div class="flex space-x-3">
<button onclick="window.pwaOfflineTester.runTests(); this.parentElement.parentElement.parentElement.remove();"
class="flex-1 bg-blue-500 hover:bg-blue-600 text-white py-3 px-4 rounded-lg font-medium transition-colors">
Run Tests Again
</button>
<button onclick="this.parentElement.parentElement.remove()"
class="flex-1 bg-gray-600 hover:bg-gray-500 text-white py-3 px-4 rounded-lg font-medium transition-colors">
Close
</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
}
getRecommendations() {
const recommendations = [];
const failedTests = this.testResults.filter(test => !test.passed);
if (failedTests.some(test => test.name.includes('Service Worker'))) {
recommendations.push('Ensure service worker is properly registered and active');
}
if (failedTests.some(test => test.name.includes('Cache'))) {
recommendations.push('Check cache configuration and ensure resources are being cached');
}
if (failedTests.some(test => test.name.includes('Network'))) {
recommendations.push('Verify network connectivity and server availability');
}
if (recommendations.length === 0) {
recommendations.push('All tests passed! Your PWA offline functionality is working correctly.');
}
return recommendations;
}
// Public API
getTestResults() {
return this.testResults;
}
clearResults() {
this.testResults = [];
}
}
// Singleton pattern
let instance = null;
const PWAOfflineTesterAPI = {
getInstance() {
if (!instance) {
instance = new PWAOfflineTester();
}
return instance;
},
runTests() {
return this.getInstance().runTests();
}
};
// Export for module use
if (typeof module !== 'undefined' && module.exports) {
module.exports = PWAOfflineTesterAPI;
} else if (typeof window !== 'undefined' && !window.PWAOfflineTester) {
window.PWAOfflineTester = PWAOfflineTesterAPI;
}
// Auto-initialize
if (typeof window !== 'undefined' && !window.pwaOfflineTester) {
window.pwaOfflineTester = PWAOfflineTesterAPI.getInstance();
// Add global function for easy access
window.testPWAOffline = () => {
window.pwaOfflineTester.runTests();
};
}

View File

@@ -349,35 +349,6 @@ button i {
margin-right: 0.5rem; margin-right: 0.5rem;
} }
/* Pay-per-session UI - Обновленный трехцветный таймер */
.session-timer {
padding: 8px 16px;
border-radius: 8px;
font-weight: 600;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
transition: all 0.5s ease;
}
.session-timer:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
/* Анимация пульсации для красной зоны */
@keyframes timer-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.session-timer.animate-pulse {
animation: timer-pulse 2s ease-in-out infinite;
}
/* Lightning button */ /* Lightning button */
.lightning-button { .lightning-button {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);

135
sw.js
View File

@@ -1,34 +1,33 @@
// SecureBit.chat Service Worker // SecureBit.chat Service Worker
// Enhanced Security Edition v4.3.120 - ECDH + DTLS + SAS // Conservative PWA Edition v4.3.120 - Minimal Caching Strategy
const CACHE_NAME = 'securebit-v4.3.120'; const CACHE_NAME = 'securebit-pwa-v4.3.120';
const STATIC_CACHE = 'securebit-static-v4.3.120'; const STATIC_CACHE = 'securebit-pwa-static-v4.3.120';
const DYNAMIC_CACHE = 'securebit-dynamic-v4.3.120'; const DYNAMIC_CACHE = 'securebit-pwa-dynamic-v4.3.120';
// Files to cache for offline functionality (excluding external CDNs that may have CORS issues) // Essential files for PWA offline functionality
const STATIC_ASSETS = [ const STATIC_ASSETS = [
'/', '/',
'/index.html', '/index.html',
'/manifest.json', '/manifest.json',
'/src/crypto/EnhancedSecureCryptoUtils.js',
'/src/network/EnhancedSecureWebRTCManager.js', // Core PWA files only
'/src/session/PayPerSessionManager.js', '/dist/app.js',
'/src/components/ui/SessionTimer.jsx', '/dist/app-boot.js',
'/src/components/ui/Header.jsx',
'/src/components/ui/PasswordModal.jsx', // Essential styles for PWA
'/src/components/ui/SessionTypeSelector.jsx',
'/src/components/ui/PaymentModal.jsx',
'/src/components/ui/DownloadApps.jsx',
'/src/components/ui/ComparisonTable.jsx',
'/src/components/ui/UniqueFeatureSlider.jsx',
'/src/components/ui/SecurityFeatures.jsx',
'/src/components/ui/Testimonials.jsx',
'/src/components/ui/Roadmap.jsx',
'/src/styles/main.css',
'/src/styles/animations.css',
'/src/styles/components.css',
'/src/styles/pwa.css', '/src/styles/pwa.css',
'/logo/favicon.ico'
// PWA icons (required for install)
'/logo/icon-192x192.png',
'/logo/icon-512x512.png',
'/logo/favicon.ico',
// PWA components only
'/src/pwa/pwa-manager.js',
'/src/pwa/install-prompt.js',
'/src/scripts/pwa-register.js',
'/src/scripts/pwa-offline-test.js'
]; ];
// Sensitive files that should never be cached // Sensitive files that should never be cached
@@ -43,21 +42,22 @@ const SENSITIVE_PATTERNS = [
// Network first patterns (always try network first) // Network first patterns (always try network first)
const NETWORK_FIRST_PATTERNS = [ const NETWORK_FIRST_PATTERNS = [
/\.js$/, /\/api\//,
/\.jsx$/, /\/session\//,
/\/src\//, /\/payment\//,
/api/ /\/verification\//,
/preimage/,
/auth/
]; ];
// Cache first patterns (static assets) // Cache first patterns (only essential PWA assets)
const CACHE_FIRST_PATTERNS = [ const CACHE_FIRST_PATTERNS = [
/\.css$/, /manifest\.json$/,
/\.png$/, /logo\/icon-.*\.png$/,
/\.jpg$/, /logo\/favicon\.ico$/,
/\.svg$/, /src\/styles\/pwa\.css$/,
/\.ico$/, /src\/pwa\/.*\.js$/,
/fonts/, /src\/scripts\/pwa-.*\.js$/
/logo/
]; ];
self.addEventListener('message', (event) => { self.addEventListener('message', (event) => {
@@ -160,23 +160,23 @@ self.addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request)); event.respondWith(handleRequest(event.request));
}); });
// Smart request handling with security considerations // Conservative request handling - only cache PWA essentials
async function handleRequest(request) { async function handleRequest(request) {
const url = new URL(request.url); const url = new URL(request.url);
try { try {
// Strategy 1: Cache First (for static assets) // Strategy 1: Cache First (only for essential PWA assets)
if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) { if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
return await cacheFirst(request); return await cacheFirst(request);
} }
// Strategy 2: Network First (for dynamic content and security-critical files) // Strategy 2: Network First (for all other requests)
if (NETWORK_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) { if (NETWORK_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
return await networkFirst(request); return await networkFirst(request);
} }
// Strategy 3: Stale While Revalidate (for main pages) // Strategy 3: Network First for everything else (no aggressive caching)
return await staleWhileRevalidate(request); return await networkFirst(request);
} catch (error) { } catch (error) {
console.error('❌ Request handling failed:', error); console.error('❌ Request handling failed:', error);
@@ -254,29 +254,39 @@ async function staleWhileRevalidate(request) {
return cachedResponse || networkResponsePromise || handleOffline(request); return cachedResponse || networkResponsePromise || handleOffline(request);
} }
// Offline fallback // Offline fallback - minimal caching for PWA only
async function handleOffline(request) { async function handleOffline(request) {
const url = new URL(request.url); const url = new URL(request.url);
// For navigation requests, return cached index.html // For navigation requests, return cached index.html
if (request.destination === 'document') { if (request.destination === 'document' || request.mode === 'navigate') {
const cachedIndex = await caches.match('/'); const cachedIndex = await caches.match('/index.html');
if (cachedIndex) { if (cachedIndex) {
return cachedIndex; return cachedIndex;
} }
// Fallback to root if index.html not found
const cachedRoot = await caches.match('/');
if (cachedRoot) {
return cachedRoot;
}
} }
// For images, return a placeholder or cached version // For PWA assets, try to return cached version
if (request.destination === 'image') { if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
return new Response( const cachedAsset = await caches.match(request);
'<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><rect width="200" height="200" fill="#1a1a1a"/><text x="100" y="100" text-anchor="middle" fill="#666" font-size="14">Offline</text></svg>', if (cachedAsset) {
{ headers: { 'Content-Type': 'image/svg+xml' } } return cachedAsset;
); }
} }
// Return a generic offline response // Return a generic offline response for everything else
return new Response( return new Response(
JSON.stringify({ error: 'Offline', message: 'Network unavailable' }), JSON.stringify({
error: 'Offline',
message: 'Network unavailable - PWA offline mode',
url: url.pathname
}),
{ {
status: 503, status: 503,
statusText: 'Service Unavailable', statusText: 'Service Unavailable',
@@ -293,6 +303,29 @@ self.addEventListener('sync', (event) => {
} }
}); });
async function retryFailedRequests() {
try {
// Get all cached requests that failed
const cache = await caches.open(DYNAMIC_CACHE);
const requests = await cache.keys();
for (const request of requests) {
try {
// Try to fetch the request again
const response = await fetch(request);
if (response.ok) {
// Update cache with successful response
await cache.put(request, response);
}
} catch (error) {
console.warn('⚠️ Retry failed for:', request.url, error.message);
}
}
} catch (error) {
console.error('❌ Failed to retry requests:', error);
}
}
// Notification click handler // Notification click handler