import React, { useState, useEffect } from 'react'; import { config } from '../config'; const STATIC_SCENARIOS = [ { id: 1, title: 'GHOST LINK INFILTRATION', domain: 'NETWORK', difficulty: 'LEVEL 4', difficultyNum: 4, description: 'Decrypt encrypted command-and-control channels within an enterprise mesh network.', accent: 'primary', icon: 'shield', tag: 'CRITICAL' }, { id: 2, title: 'ORACLE BREACH TRACE', domain: 'DATABASE', difficulty: 'LEVEL 2', difficultyNum: 2, description: 'Perform forensic analysis on a SQL injection attempt. Identify exfiltrated datasets.', accent: 'secondary', icon: 'database', tag: 'COMPLIANCE' }, { id: 3, title: 'NEBULA API COLLAPSE', domain: 'CLOUD FRAMEWORK', difficulty: 'LEVEL 5', difficultyNum: 5, description: 'Massive service degradation across regional clusters. Investigate DDoS vs Zero-Day.', accent: 'primary', icon: 'cloud', tag: null }, { id: 4, title: 'PHANTOM CREDENTIAL HARVEST', domain: 'IDENTITY', difficulty: 'LEVEL 3', difficultyNum: 3, description: 'Trace a credential stuffing attack across microservices. Recover compromised tokens.', accent: 'secondary', icon: 'fingerprint', tag: 'CRITICAL' }, { id: 5, title: 'SILENT EXFIL TRACE', domain: 'ENDPOINT', difficulty: 'LEVEL 4', difficultyNum: 4, description: 'Detect anomalous data outflow from a compromised workstation. Time is critical.', accent: 'primary', icon: 'leak_remove', tag: 'COMPLIANCE' } ]; const DIFFICULTY_MAP = { easy: 2, medium: 3, hard: 5 }; const DOMAIN_ICON_MAP = { API: 'api', BACKEND: 'settings', INFRASTRUCTURE: 'dns', NETWORK: 'lan', DATABASE: 'database', CLOUD: 'cloud', CUSTOM: 'extension' }; function mapBackendScenario(s, index) { const diffNum = DIFFICULTY_MAP[s.difficulty] || 3; const domain = (s.domain || 'BACKEND').toUpperCase(); return { id: `backend-${s.id}`, backendId: s.id, title: (s.title || s.id).toUpperCase(), domain, difficulty: `LEVEL ${diffNum}`, difficultyNum: diffNum, description: s.description || '', accent: index % 2 === 0 ? 'primary' : 'secondary', icon: DOMAIN_ICON_MAP[domain] || 'bug_report', tag: s.difficulty === 'hard' ? 'CRITICAL' : s.difficulty === 'medium' ? 'COMPLIANCE' : null, isBackend: true, backendTask: s.id.includes('business') ? 'business-process-failure' : s.id.includes('cascade') ? 'cascade-system-failure' : 'software-incident', }; } const FILTERS = ['ALL', 'CRITICAL', 'COMPLIANCE', 'CUSTOM']; import { useApp } from '../context/AppContext'; const ScenarioBrowserView = () => { const { globalMaxSteps } = useApp(); const [backendScenarios, setBackendScenarios] = useState([]); const [loadingBackend, setLoadingBackend] = useState(true); const [customScenarios, setCustomScenarios] = useState(() => { try { const saved = localStorage.getItem('nexus_custom_scenarios'); return saved ? JSON.parse(saved) : []; } catch { return []; } }); const [activeFilter, setActiveFilter] = useState('ALL'); const [sortBy, setSortBy] = useState('id'); const [isCreating, setIsCreating] = useState(false); useEffect(() => { fetch(`${config.API_BASE}/scenarios`) .then(r => r.json()) .then(data => { const allBackend = [ ...(data.easy || []), ...(data.medium || []), ...(data.hard || []), ].map((s, i) => mapBackendScenario(s, i)); setBackendScenarios(allBackend); }) .catch(() => setBackendScenarios([])) .finally(() => setLoadingBackend(false)); }, []); const allScenarios = [...STATIC_SCENARIOS, ...backendScenarios, ...customScenarios]; const defaultForm = { title: '', domain: 'CUSTOM', difficulty: 'LEVEL 3', difficultyNum: 3, description: '', accent: 'primary', icon: 'extension', tag: 'CUSTOM' }; const [form, setForm] = useState(defaultForm); const handleSave = () => { if (!form.title.trim() || !form.description.trim()) return; const newScenario = { ...form, id: Date.now(), isCustom: true }; const updated = [...customScenarios, newScenario]; setCustomScenarios(updated); localStorage.setItem('nexus_custom_scenarios', JSON.stringify(updated)); setIsCreating(false); setForm(defaultForm); }; const handleDelete = (e, id) => { e.stopPropagation(); const updated = customScenarios.filter(s => s.id !== id); setCustomScenarios(updated); localStorage.setItem('nexus_custom_scenarios', JSON.stringify(updated)); }; const injectScenario = async (scenario) => { try { const body = scenario.isBackend ? { task: scenario.backendTask, seed: 42, max_steps: globalMaxSteps } : { task: 'custom-incident', custom_scenario: { id: scenario.title, description: scenario.description, context: `Domain: ${scenario.domain}`, difficulty: scenario.difficulty, clue_map: {} }, max_steps: globalMaxSteps }; await fetch(`${config.API_BASE}/reset`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); window.location.hash = '#'; } catch (e) { console.error('Failed to inject', e); } }; const filtered = allScenarios .filter(s => activeFilter === 'ALL' || s.tag === activeFilter) .sort((a, b) => sortBy === 'difficulty' ? b.difficultyNum - a.difficultyNum : 0); const accentClasses = { primary: { text: 'text-primary', bg: 'bg-primary/10', border: 'border-primary/20' }, secondary: { text: 'text-secondary', bg: 'bg-secondary/10', border: 'border-secondary/20' }, }; return (
Scenario Registry v2.5 — {filtered.length} results {loadingBackend && · Fetching live scenarios...} {!loadingBackend && backendScenarios.length > 0 && ( · {backendScenarios.length} Live scenarios loaded )}

Nexus Scenario Browser

Select a tactical simulation to begin intelligence harvesting. Live scenarios are fetched from the backend engine in real-time.

{FILTERS.map(f => ( ))}
Sort:
{isCreating && (

Create Custom Scenario

setForm({ ...form, title: e.target.value })} /> setForm({ ...form, domain: e.target.value })} />
setForm({ ...form, difficultyNum: parseInt(e.target.value), difficulty: `LEVEL ${e.target.value}` })} className="w-32 accent-primary" />
)} {filtered.length === 0 ? (
filter_list_off

No scenarios match the filter

) : (
{filtered.map((s) => { const ac = accentClasses[s.accent] || accentClasses.primary; return (
{s.icon} {s.tag && (
{s.tag}
)} {s.isBackend && (
LIVE
)} {s.isCustom && ( )}
{s.domain}

{s.title}

{s.difficultyNum}/5

{s.description}

{s.difficulty}
); })}
)}
); }; export default ScenarioBrowserView;