1
0
Fork 0
security-lab/src/cases/Case4Desktop.tsx
2026-04-05 05:55:51 +03:00

402 lines
14 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import type { WallpaperType } from '../App';
import AmneziaVPN from '../apps/amneziavpn/AmneziaVPN';
interface Case4DesktopProps {
onComplete: (type: WallpaperType) => void;
}
type OpenWindow = 'vpn' | 'browser' | null;
const Case4Desktop: React.FC<Case4DesktopProps> = ({ onComplete }) => {
const [vpnConnected, setVpnConnected] = useState(
() => localStorage.getItem('amnezia_connected') === 'true'
);
const [openWindow, setOpenWindow] = useState<OpenWindow>(null);
const [showVpnWarning, setShowVpnWarning] = useState(false);
const [browserPage, setBrowserPage] = useState<'new-tab' | 'bank'>('new-tab');
const [completed, setCompleted] = useState(false);
const openBrowser = () => {
if (!vpnConnected) {
setShowVpnWarning(true);
} else {
setOpenWindow('browser');
}
};
const handleWarningContinue = () => {
setShowVpnWarning(false);
setOpenWindow('browser');
};
return (
<div style={s.root}>
{/* Фон — кафе */}
<div style={s.cafeBar}>
<span style={s.wifiIcon}>📶</span>
<span style={s.wifiText}>Подключено: <b>CafeNet_Free</b></span>
<span style={s.wifiUnsafe}> Публичная сеть</span>
</div>
{/* Легенда */}
<div style={s.story}>
<p>Вы пришли в кафе и подключились к бесплатному Wi-Fi. Ваш трафик может перехватываться.<br/>
Перед тем как открывать браузер рекомендуется включить VPN.</p>
</div>
{/* Иконки рабочего стола */}
<div style={s.desktop}>
<button style={s.icon} onClick={() => setOpenWindow('vpn')}>
<span style={s.iconImg}>🔒</span>
<span style={s.iconLabel}>AmneziaVPN</span>
</button>
<button style={s.icon} onClick={openBrowser}>
<span style={s.iconImg}>🌐</span>
<span style={s.iconLabel}>Браузер</span>
</button>
</div>
{/* Статус VPN */}
<div style={{ ...s.vpnStatus, ...(vpnConnected ? s.vpnOn : s.vpnOff) }}>
{vpnConnected ? '🛡 VPN включён — соединение защищено' : '⚡ VPN выключен — трафик не защищён'}
</div>
{/* Кнопка завершения */}
{vpnConnected && !completed && (
<button style={s.completeBtn} onClick={() => { setCompleted(true); onComplete('win10'); }}>
Завершить кейс
</button>
)}
{/* Окно VPN */}
{openWindow === 'vpn' && (
<div style={s.windowOverlay}>
<div style={s.window}>
<div style={s.windowBar}>
<span>AmneziaVPN</span>
<button style={s.closeBtn} onClick={() => setOpenWindow(null)}></button>
</div>
<AmneziaVPN onConnectedChange={setVpnConnected} />
</div>
</div>
)}
{/* Окно браузера */}
{openWindow === 'browser' && (
<div style={s.windowOverlay}>
<div style={{ ...s.window, width: 700, maxWidth: '95vw' }}>
<div style={s.windowBar}>
<span>Браузер</span>
<button style={s.closeBtn} onClick={() => { setOpenWindow(null); setBrowserPage('new-tab'); }}></button>
</div>
<div style={s.browserChrome}>
<div style={s.urlBar}>
{vpnConnected && <span style={s.httpsIcon}>🔒</span>}
{!vpnConnected && <span style={s.httpIcon}></span>}
<span style={s.urlText}>
{browserPage === 'bank' ? 'http://bank-online.ru' : 'about:newtab'}
</span>
</div>
</div>
<div style={s.browserContent}>
{browserPage === 'new-tab' ? (
<div style={s.newTab}>
<div style={s.newTabTitle}>Новая вкладка</div>
{!vpnConnected && (
<div style={s.browserWarn}>
Вы используете незащищённое соединение. Ваш трафик виден другим пользователям сети.
</div>
)}
<div style={s.bookmarks}>
<button style={s.bookmark} onClick={() => setBrowserPage('bank')}>
🏦 Онлайн-банк
</button>
</div>
</div>
) : (
<div style={s.bankPage}>
{!vpnConnected && (
<div style={s.interceptAlert}>
<div style={s.interceptTitle}>🚨 ВНИМАНИЕ: Перехват трафика!</div>
<div style={s.interceptBody}>
Злоумышленник в сети перехватил ваш запрос к банку.<br/>
Логин, пароль и данные карты переданы в открытом виде через HTTP.<br/><br/>
<b>Если бы был включён VPN</b> весь трафик был бы зашифрован и недоступен для перехвата.
</div>
<button style={s.warnBtn} onClick={() => setBrowserPage('new-tab')}> Назад</button>
</div>
)}
{vpnConnected && (
<div style={s.safeBank}>
<div style={s.safeBankTitle}>🏦 Онлайн-банк</div>
<div style={s.safeBankBody}>
🛡 Соединение защищено VPN-шифрованием.<br/>
Ваши данные надёжно зашифрованы и недоступны другим пользователям сети.
</div>
<button style={s.warnBtn} onClick={() => setBrowserPage('new-tab')}> Назад</button>
</div>
)}
</div>
)}
</div>
</div>
</div>
)}
{/* Предупреждение при открытии браузера без VPN */}
{showVpnWarning && (
<div style={s.modalOverlay}>
<div style={s.modal}>
<div style={s.modalIcon}></div>
<div style={s.modalTitle}>VPN не подключён</div>
<div style={s.modalText}>
Вы подключены к публичной сети <b>CafeNet_Free</b>.<br/>
Без VPN ваш трафик может быть перехвачен злоумышленниками в этой же сети.<br/><br/>
Рекомендуется сначала включить AmneziaVPN.
</div>
<div style={{ display: 'flex', gap: 12, justifyContent: 'center' }}>
<button style={s.modalBtnSecondary} onClick={() => { setShowVpnWarning(false); setOpenWindow('vpn'); }}>
Включить VPN
</button>
<button style={s.modalBtnDanger} onClick={handleWarningContinue}>
Всё равно открыть
</button>
</div>
</div>
</div>
)}
</div>
);
};
const s: Record<string, React.CSSProperties> = {
root: {
minHeight: '100vh',
background: 'linear-gradient(135deg, #1a0a00 0%, #0d1117 50%, #001a1a 100%)',
color: '#ccc',
fontFamily: "'Share Tech Mono', monospace",
position: 'relative',
overflow: 'hidden',
},
cafeBar: {
background: 'rgba(0,0,0,0.6)',
borderBottom: '1px solid rgba(0,255,255,0.15)',
padding: '10px 24px',
display: 'flex',
alignItems: 'center',
gap: 12,
fontSize: 14,
},
wifiIcon: { fontSize: 18 },
wifiText: { color: '#ccc' },
wifiUnsafe: { color: '#f5a623', marginLeft: 8 },
story: {
maxWidth: 700,
margin: '24px auto 0',
padding: '16px 24px',
background: 'rgba(0,255,255,0.04)',
border: '1px solid rgba(0,255,255,0.15)',
borderRadius: 8,
fontSize: 14,
lineHeight: 1.7,
color: '#aaa',
textAlign: 'center',
},
desktop: {
display: 'flex',
gap: 40,
justifyContent: 'center',
marginTop: 48,
},
icon: {
background: 'rgba(255,255,255,0.04)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 12,
padding: '20px 28px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 10,
transition: 'all 0.2s',
color: '#ccc',
},
iconImg: { fontSize: 40 },
iconLabel: { fontSize: 13, fontFamily: "'Share Tech Mono', monospace" },
vpnStatus: {
textAlign: 'center',
marginTop: 32,
fontSize: 13,
padding: '8px 20px',
borderRadius: 6,
display: 'inline-block',
position: 'relative',
left: '50%',
transform: 'translateX(-50%)',
},
vpnOn: { background: 'rgba(0,255,65,0.1)', border: '1px solid #00FF41', color: '#00FF41' },
vpnOff: { background: 'rgba(245,166,35,0.1)', border: '1px solid #f5a623', color: '#f5a623' },
completeBtn: {
display: 'block',
margin: '24px auto 0',
padding: '12px 36px',
background: '#00FF41',
border: 'none',
borderRadius: 6,
color: '#000',
fontFamily: "'Orbitron', monospace",
fontSize: 14,
fontWeight: 700,
cursor: 'pointer',
},
windowOverlay: {
position: 'fixed',
inset: 0,
background: 'rgba(0,0,0,0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 100,
},
window: {
background: '#0d0d22',
border: '1px solid rgba(0,255,255,0.25)',
borderRadius: 10,
overflow: 'hidden',
width: 360,
boxShadow: '0 0 40px rgba(0,255,255,0.1)',
},
windowBar: {
background: '#080818',
borderBottom: '1px solid rgba(0,255,255,0.15)',
padding: '10px 16px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 13,
color: '#00FFFF',
fontFamily: "'Orbitron', monospace",
},
closeBtn: {
background: 'transparent',
border: '1px solid rgba(255,0,64,0.4)',
color: '#ff6b6b',
cursor: 'pointer',
borderRadius: 4,
padding: '2px 8px',
fontSize: 12,
},
browserChrome: {
background: '#080818',
borderBottom: '1px solid rgba(0,255,255,0.1)',
padding: '8px 12px',
},
urlBar: {
background: '#0a0a1a',
border: '1px solid rgba(0,255,255,0.2)',
borderRadius: 4,
padding: '6px 12px',
fontSize: 13,
display: 'flex',
alignItems: 'center',
gap: 8,
},
httpsIcon: { color: '#00FF41', fontSize: 14 },
httpIcon: { color: '#f5a623', fontSize: 14 },
urlText: { color: '#888', flex: 1 },
browserContent: { minHeight: 300, background: '#0a0a0a' },
newTab: { padding: 24 },
newTabTitle: { color: '#555', fontSize: 13, marginBottom: 16, fontFamily: "'Orbitron', monospace" },
browserWarn: {
background: 'rgba(245,166,35,0.1)',
border: '1px solid #f5a623',
borderRadius: 6,
padding: '10px 14px',
fontSize: 13,
color: '#f5a623',
marginBottom: 20,
},
bookmarks: { display: 'flex', gap: 12, flexWrap: 'wrap' },
bookmark: {
background: 'rgba(0,255,255,0.05)',
border: '1px solid rgba(0,255,255,0.2)',
borderRadius: 8,
padding: '12px 18px',
color: '#aaa',
cursor: 'pointer',
fontSize: 13,
fontFamily: "'Share Tech Mono', monospace",
},
bankPage: { padding: 24 },
interceptAlert: {
background: 'rgba(255,0,64,0.08)',
border: '1px solid #FF0040',
borderRadius: 8,
padding: 20,
},
interceptTitle: { color: '#FF0040', fontFamily: "'Orbitron', monospace", fontSize: 15, marginBottom: 12, fontWeight: 700 },
interceptBody: { color: '#ccc', fontSize: 13, lineHeight: 1.7, marginBottom: 16 },
safeBank: {
background: 'rgba(0,255,65,0.06)',
border: '1px solid #00FF41',
borderRadius: 8,
padding: 20,
},
safeBankTitle: { color: '#00FF41', fontFamily: "'Orbitron', monospace", fontSize: 15, marginBottom: 12, fontWeight: 700 },
safeBankBody: { color: '#ccc', fontSize: 13, lineHeight: 1.7, marginBottom: 16 },
warnBtn: {
background: 'transparent',
border: '1px solid rgba(0,255,255,0.3)',
color: '#888',
borderRadius: 6,
padding: '8px 16px',
cursor: 'pointer',
fontSize: 13,
fontFamily: "'Share Tech Mono', monospace",
},
modalOverlay: {
position: 'fixed',
inset: 0,
background: 'rgba(0,0,0,0.7)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 200,
},
modal: {
background: '#0d0d22',
border: '1px solid #f5a623',
borderRadius: 10,
padding: '32px 28px',
maxWidth: 420,
textAlign: 'center',
boxShadow: '0 0 30px rgba(245,166,35,0.2)',
},
modalIcon: { fontSize: 40, marginBottom: 12 },
modalTitle: { color: '#f5a623', fontFamily: "'Orbitron', monospace", fontSize: 16, marginBottom: 12, fontWeight: 700 },
modalText: { color: '#aaa', fontSize: 13, lineHeight: 1.7, marginBottom: 24 },
modalBtnSecondary: {
background: '#00FF41',
border: 'none',
borderRadius: 6,
padding: '10px 20px',
color: '#000',
cursor: 'pointer',
fontFamily: "'Orbitron', monospace",
fontSize: 12,
fontWeight: 700,
},
modalBtnDanger: {
background: 'transparent',
border: '1px solid rgba(255,0,64,0.5)',
borderRadius: 6,
padding: '10px 20px',
color: '#FF0040',
cursor: 'pointer',
fontFamily: "'Share Tech Mono', monospace",
fontSize: 12,
},
};
export default Case4Desktop;