mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-07 09:57:50 +08:00
ux: clean security warnings modal and overlays
This commit is contained in:
58
src/App.tsx
58
src/App.tsx
@@ -612,62 +612,68 @@ function App() {
|
|||||||
|
|
||||||
{/* Security Modal */}
|
{/* Security Modal */}
|
||||||
{showSecurityModal && (
|
{showSecurityModal && (
|
||||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
|
<div
|
||||||
<div className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl">
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50"
|
||||||
|
onClick={() => setShowSecurityModal(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
<h3 className="text-lg font-semibold text-white mb-4">Security Limitations</h3>
|
<h3 className="text-lg font-semibold text-white mb-4">Security Limitations</h3>
|
||||||
<div className="text-sm text-slate-300 space-y-2">
|
<div className="text-sm text-slate-300 space-y-2">
|
||||||
<SecurityWarnings />
|
<SecurityWarnings />
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className="mt-4 w-full py-2 bg-slate-700 hover:bg-slate-600 rounded-lg"
|
|
||||||
onClick={() => setShowSecurityModal(false)}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Storage Modal */}
|
{/* Storage Modal */}
|
||||||
{showStorageModal && (
|
{showStorageModal && (
|
||||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
|
<div
|
||||||
<div className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl">
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50"
|
||||||
|
onClick={() => setShowStorageModal(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
<h3 className="text-lg font-semibold text-white mb-4">Storage Details</h3>
|
<h3 className="text-lg font-semibold text-white mb-4">Storage Details</h3>
|
||||||
<div className="text-sm text-slate-300 space-y-2">
|
<div className="text-sm text-slate-300 space-y-2">
|
||||||
<StorageDetails localItems={localItems} sessionItems={sessionItems} />
|
<StorageDetails localItems={localItems} sessionItems={sessionItems} />
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className="mt-4 w-full py-2 bg-slate-700 hover:bg-slate-600 rounded-lg"
|
|
||||||
onClick={() => setShowStorageModal(false)}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Clipboard Modal */}
|
{/* Clipboard Modal */}
|
||||||
{showClipboardModal && (
|
{showClipboardModal && (
|
||||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
|
<div
|
||||||
<div className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl">
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50"
|
||||||
|
onClick={() => setShowClipboardModal(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
<h3 className="text-lg font-semibold text-white mb-4">Clipboard Activity</h3>
|
<h3 className="text-lg font-semibold text-white mb-4">Clipboard Activity</h3>
|
||||||
<div className="text-sm text-slate-300 space-y-2">
|
<div className="text-sm text-slate-300 space-y-2">
|
||||||
<ClipboardDetails events={clipboardEvents} onClear={clearClipboard} />
|
<ClipboardDetails events={clipboardEvents} onClear={clearClipboard} />
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className="mt-4 w-full py-2 bg-slate-700 hover:bg-slate-600 rounded-lg"
|
|
||||||
onClick={() => setShowClipboardModal(false)}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Lock Confirmation Modal */}
|
{/* Lock Confirmation Modal */}
|
||||||
{showLockConfirm && (
|
{showLockConfirm && (
|
||||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
|
<div
|
||||||
<div className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl">
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50"
|
||||||
|
onClick={() => setShowLockConfirm(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-slate-800 rounded-xl border border-slate-700 p-6 max-w-md w-full mx-4 shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
|
||||||
<Lock className="w-5 h-5 text-amber-500" />
|
<Lock className="w-5 h-5 text-amber-500" />
|
||||||
Lock Sensitive Data?
|
Lock Sensitive Data?
|
||||||
|
|||||||
@@ -1,81 +1,66 @@
|
|||||||
import { useState } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export const SecurityWarnings = () => {
|
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
|
||||||
|
|
||||||
|
export const SecurityWarnings: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-4 left-4 z-50 max-w-sm">
|
<div className="space-y-3">
|
||||||
<div className="bg-yellow-50 border-2 border-yellow-400 rounded-lg shadow-lg">
|
<Warning
|
||||||
|
icon="🧵"
|
||||||
|
title="JavaScript Strings are Immutable"
|
||||||
|
description="Strings cannot be overwritten in memory. Copies persist until garbage collection runs (timing unpredictable)."
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Header */}
|
<Warning
|
||||||
<div
|
icon="🗑️"
|
||||||
className="px-4 py-3 cursor-pointer flex items-center justify-between hover:bg-yellow-100 transition-colors rounded-t-lg"
|
title="No Guaranteed Memory Wiping"
|
||||||
onClick={() => setIsExpanded(!isExpanded)}
|
description="JavaScript has no secure memory clearing. Sensitive data may linger in RAM until GC or browser restart."
|
||||||
>
|
/>
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-lg">⚠️</span>
|
|
||||||
<span className="font-semibold text-sm text-yellow-900">Security Limitations</span>
|
|
||||||
</div>
|
|
||||||
<span className="text-yellow-600 text-sm">{isExpanded ? '▼' : '▶'}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Expanded Content */}
|
<Warning
|
||||||
{isExpanded && (
|
icon="📋"
|
||||||
<div className="px-4 py-3 border-t border-yellow-300 space-y-3 max-h-96 overflow-y-auto">
|
title="Clipboard Exposure"
|
||||||
|
description="Copied data is accessible to other tabs/apps. Browser extensions can read clipboard contents."
|
||||||
|
/>
|
||||||
|
|
||||||
<Warning
|
<Warning
|
||||||
icon="🧵"
|
icon="💾"
|
||||||
title="JavaScript Strings are Immutable"
|
title="Browser Storage Persistence"
|
||||||
description="Strings cannot be overwritten in memory. Copies persist until garbage collection runs (timing unpredictable)."
|
description="localStorage survives browser restart. sessionStorage survives page refresh. Both readable by any script on this domain."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Warning
|
<Warning
|
||||||
icon="🗑️"
|
icon="🔍"
|
||||||
title="No Guaranteed Memory Wiping"
|
title="DevTools Access"
|
||||||
description="JavaScript has no secure memory clearing. Sensitive data may linger in RAM until GC or browser restart."
|
description="All app state, memory, and storage visible in browser DevTools. Never use on untrusted devices."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Warning
|
<Warning
|
||||||
icon="📋"
|
icon="🌐"
|
||||||
title="Clipboard Exposure"
|
title="Network Risks (When Online)"
|
||||||
description="Copied data is accessible to other tabs/apps. Browser extensions can read clipboard contents."
|
description="If hosted online: DNS, HTTPS, CDN, and browser can see usage patterns. Use offline/local for maximum security."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Warning
|
<div className="pt-3 border-t border-slate-600 text-xs text-slate-400">
|
||||||
icon="💾"
|
<strong className="text-slate-300">Recommendation:</strong>{' '}
|
||||||
title="Browser Storage Persistence"
|
Use this tool on a dedicated offline device. Clear browser data after each use. Never use on shared/public computers.
|
||||||
description="localStorage survives browser restart. sessionStorage survives page refresh. Both readable by any script on this domain."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Warning
|
|
||||||
icon="🔍"
|
|
||||||
title="DevTools Access"
|
|
||||||
description="All app state, memory, and storage visible in browser DevTools. Never use on untrusted devices."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Warning
|
|
||||||
icon="🌐"
|
|
||||||
title="Network Risks (When Online)"
|
|
||||||
description="If hosted online: DNS, HTTPS, CDN, and browser can see usage patterns. Use offline/local for maximum security."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="pt-2 border-t border-yellow-300 text-xs text-yellow-800">
|
|
||||||
<strong>Recommendation:</strong> Use this tool on a dedicated offline device.
|
|
||||||
Clear browser data after each use. Never use on shared/public computers.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Warning = ({ icon, title, description }: { icon: string; title: string; description: string }) => (
|
const Warning = ({
|
||||||
<div className="flex gap-2 text-xs">
|
icon,
|
||||||
<span className="text-base flex-shrink-0">{icon}</span>
|
title,
|
||||||
|
description,
|
||||||
|
}: {
|
||||||
|
icon: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}) => (
|
||||||
|
<div className="flex gap-2 text-sm">
|
||||||
|
<span className="text-lg flex-shrink-0">{icon}</span>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-semibold text-yellow-900 mb-0.5">{title}</div>
|
<div className="font-semibold text-slate-200 mb-1">{title}</div>
|
||||||
<div className="text-yellow-800 leading-relaxed">{description}</div>
|
<div className="text-slate-400 leading-relaxed">{description}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user