diff --git a/frontend-modern/src/components/Settings/SecurityPostureSummary.tsx b/frontend-modern/src/components/Settings/SecurityPostureSummary.tsx index 47c5e6c..7c00efc 100644 --- a/frontend-modern/src/components/Settings/SecurityPostureSummary.tsx +++ b/frontend-modern/src/components/Settings/SecurityPostureSummary.tsx @@ -1,6 +1,10 @@ -import { Component, For, Show } from 'solid-js'; +import { Component, For, Show, createMemo } from 'solid-js'; import { Card } from '@/components/shared/Card'; -import { SectionHeader } from '@/components/shared/SectionHeader'; +import Shield from 'lucide-solid/icons/shield'; +import ShieldCheck from 'lucide-solid/icons/shield-check'; +import ShieldAlert from 'lucide-solid/icons/shield-alert'; +import CheckCircle from 'lucide-solid/icons/check-circle'; +import XCircle from 'lucide-solid/icons/x-circle'; interface SecurityPostureSummaryProps { status: { @@ -26,24 +30,28 @@ export const SecurityPostureSummary: Component = (p label: 'Password login', enabled: props.status.hasAuthentication, description: props.status.hasAuthentication ? 'Active' : 'Not configured', + critical: true, // Critical security feature }, { key: 'oidc', label: 'Single sign-on', enabled: Boolean(props.status.oidcEnabled), description: props.status.oidcEnabled ? 'OIDC configured' : 'Not configured', + critical: false, }, { key: 'proxy', label: 'Proxy auth', enabled: Boolean(props.status.hasProxyAuth), description: props.status.hasProxyAuth ? 'Active' : 'Not configured', + critical: false, }, { key: 'token', label: 'API token', enabled: props.status.apiTokenConfigured, description: props.status.apiTokenConfigured ? 'Active' : 'Not configured', + critical: false, }, { key: 'export', @@ -52,79 +60,138 @@ export const SecurityPostureSummary: Component = (p description: props.status.unprotectedExportAllowed ? 'Unprotected' : 'Token + passphrase required', + critical: true, }, { key: 'https', label: 'HTTPS', enabled: Boolean(props.status.hasHTTPS), description: props.status.hasHTTPS ? 'Encrypted' : 'HTTP only', + critical: true, }, { key: 'audit', label: 'Audit log', enabled: props.status.hasAuditLogging, description: props.status.hasAuditLogging ? 'Active' : 'Not enabled', + critical: false, }, ]; - const badgeClasses = (enabled: boolean) => - enabled - ? 'inline-flex items-center gap-1 px-2.5 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300' - : 'inline-flex items-center gap-1 px-2.5 py-1 text-xs font-semibold rounded-full bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-200'; + // Calculate security score + const securityScore = createMemo(() => { + const list = items(); + const criticalItems = list.filter(i => i.critical); + const enabledCritical = criticalItems.filter(i => i.enabled).length; + const allItems = list.filter(i => i.enabled).length; + + // Weight critical items more heavily + const criticalWeight = 0.7; + const optionalWeight = 0.3; + + const criticalScore = criticalItems.length > 0 ? (enabledCritical / criticalItems.length) * criticalWeight : 0; + const optionalScore = list.length > 0 ? (allItems / list.length) * optionalWeight : 0; + + return Math.round((criticalScore + optionalScore) * 100); + }); + + const scoreColor = () => { + const score = securityScore(); + if (score >= 80) return 'from-green-500 to-emerald-600'; + if (score >= 50) return 'from-amber-500 to-orange-600'; + return 'from-red-500 to-rose-600'; + }; + + const scoreLabel = () => { + const score = securityScore(); + if (score >= 80) return 'Strong'; + if (score >= 50) return 'Moderate'; + return 'Weak'; + }; + + const ScoreIcon = () => { + const score = securityScore(); + if (score >= 80) return ShieldCheck; + if (score >= 50) return Shield; + return ShieldAlert; + }; return ( - -
-
- -
- - {props.status.publicAccess && !props.status.isPrivateNetwork - ? 'Public network access' - : 'Private network access'} - - - - + + {/* Header with Security Score */} +
+
+
+
+ {(() => { + const Icon = ScoreIcon(); + return ; + })()} +
+
+

Security Posture

+

+ {props.status.publicAccess && !props.status.isPrivateNetwork + ? 'Public network access detected' + : 'Private network access'} +

+
+
+
+
{securityScore()}%
+
{scoreLabel()}
+
- -
+ {/* Security Items Grid */} +
+
{(item) => ( -
+
{item.label} - - - - - {item.enabled ? 'On' : 'Off'} - + + }> + + +
+
+

+ {item.description} +

+ + + Critical + +
-

- {item.description} -

)}
+ + {/* Client IP Badge */} + +
+ + Your IP: {props.status.clientIP} + +
+
);