diff --git a/README.md b/README.md index e2849c1..5c42522 100755 --- a/README.md +++ b/README.md @@ -73,6 +73,28 @@ Future: - React/TypeScript Enterprise UI. - Tauri Desktop Forensics. +## Pilot v1 demo + +Pilot v1 demo показывает AWatch-rus как рабочую платформу Workforce Analytics + +Security Analytics + Forensics для ролей `executive`, `manager`, `security`, +`forensics` и `admin`. + +Демо-материалы: + +- [сценарий Pilot v1 demo](docs/PILOT_DEMO_SCENARIO_RU.md); +- [demo seed data](docs/fixtures/pilot-v1-demo/demo-seed-data.json); +- [demo evidence pack](docs/fixtures/pilot-v1-demo/evidence-pack/); +- [преддемо-runbook](docs/DEMO_RUNBOOK_RU.md). + +Границы показа: + +- pfSense показывается только как `contract_only/readiness`, без заявления + production ingestion или SIEM; +- UEBA Score v1 является прозрачной rule-based моделью, без ML/LLM; +- demo fixtures не содержат реальных IP-адресов, hostname, логинов, ФИО, + подразделений заказчика или событий безопасности; +- planned/future providers не являются реализованными collectors. + ## Что видит оператор - Работал ли пользователь за компьютером или в удаленной сессии. @@ -172,6 +194,7 @@ collectors. - [Архитектурный baseline](docs/ARCHITECTURE_BASELINE_RU.md) - [Пакет пилота для заказчика](docs/CUSTOMER_PILOT_PACK_RU.md) - [Pilot v1.0](docs/PILOT_V1_RU.md) +- [Pilot v1 demo](docs/PILOT_DEMO_SCENARIO_RU.md) - [Pilot v1.0 acceptance checklist](docs/PILOT_V1_ACCEPTANCE_CHECKLIST_RU.md) - [Pilot v1.0 evidence](docs/PILOT_V1_EVIDENCE_RU.md) - [Позиционирование продукта](docs/PRODUCT_POSITIONING_RU.md) diff --git a/adk-rust/crates/detmir-portal/src/main.rs b/adk-rust/crates/detmir-portal/src/main.rs index 55c2e10..3bb59a2 100644 --- a/adk-rust/crates/detmir-portal/src/main.rs +++ b/adk-rust/crates/detmir-portal/src/main.rs @@ -3441,6 +3441,7 @@ fn role_filtered_report(report: Value, role: PortalRole) -> Value { "business_risk", "business_risk_history_summary", "risk_heatmap", + "security_events_summary", "workforce", "markdown", ] { @@ -10037,6 +10038,27 @@ mod tests { } } + #[test] + fn portal_pilot_demo_navigation_is_present() { + for marker in [ + "Pilot v1 demo", + "Executive demo", + "Manager demo", + "Security demo", + "Forensics demo", + "Admin demo", + "data-demo-view-mode=\"executive\"", + "data-demo-view-mode=\"security\"", + "data-demo-view-mode=\"forensics\"", + "data-demo-view-mode=\"admin\"", + ] { + assert!( + INDEX_HTML.contains(marker), + "demo navigation missing {marker}" + ); + } + } + #[test] fn api_contract_artifacts_are_valid_and_future_ui_ready() { let openapi: Value = @@ -10134,6 +10156,7 @@ mod tests { let executive = role_filtered_report(report.clone(), PortalRole::Executive); assert!(executive.get("workforce").is_some()); assert!(executive.get("executive_dashboard").is_some()); + assert!(executive.get("security_events_summary").is_some()); assert!(executive.get("risk_incident_candidates").is_none()); assert!(executive.get("security_correlation").is_none()); diff --git a/adk-rust/crates/detmir-portal/src/static/app.css b/adk-rust/crates/detmir-portal/src/static/app.css index 2c8daa5..16fde90 100644 --- a/adk-rust/crates/detmir-portal/src/static/app.css +++ b/adk-rust/crates/detmir-portal/src/static/app.css @@ -82,13 +82,17 @@ body.security-mode .small-button, body.security-mode a.button, body.security-mode .tab, body.security-mode .nav-link, +body.security-mode .view-button, +body.security-mode .demo-button, body.security-mode .control-strip select { background: #111827; color: var(--ink); } body.security-mode .tab.is-active, -body.security-mode .nav-link.is-active { +body.security-mode .nav-link.is-active, +body.security-mode .view-button.is-active, +body.security-mode .demo-button.is-active { background: #0f2a3a; color: var(--link); } @@ -145,14 +149,16 @@ h1 { .control-strip, .header-actions, -.view-switch { +.view-switch, +.demo-switch { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; } -.view-switch { +.view-switch, +.demo-switch { align-items: center; padding: 4px; border: 1px solid var(--line); @@ -160,14 +166,16 @@ h1 { background: var(--panel); } -.view-switch > span { +.view-switch > span, +.demo-switch > span { padding: 0 6px; color: var(--muted); font-size: 12px; font-weight: 700; } -.view-button { +.view-button, +.demo-button { min-height: 30px; border: 1px solid transparent; border-radius: 6px; @@ -180,18 +188,21 @@ h1 { cursor: pointer; } -.view-button.is-active { +.view-button.is-active, +.demo-button.is-active { border-color: #1d4ed8; background: #2563eb; color: #ffffff; } -.view-button:hover { +.view-button:hover, +.demo-button:hover { color: var(--ink); background: rgba(37, 99, 235, 0.08); } -.view-button.is-active:hover { +.view-button.is-active:hover, +.demo-button.is-active:hover { color: #ffffff; background: #1d4ed8; } @@ -1210,10 +1221,13 @@ pre { .topbar-side { justify-items: stretch; min-width: 0; width: 100%; } .control-strip, .header-actions, - .view-switch { justify-content: flex-start; } + .view-switch, + .demo-switch { justify-content: flex-start; } .control-strip label { min-width: 100%; } - .view-switch > span { flex-basis: 100%; } - .view-button { flex: 1 1 120px; } + .view-switch > span, + .demo-switch > span { flex-basis: 100%; } + .view-button, + .demo-button { flex: 1 1 120px; } h1 { font-size: 24px; } .page-head { flex-direction: column; } .period-banner { flex-direction: column; align-items: flex-start; } diff --git a/adk-rust/crates/detmir-portal/src/static/app.js b/adk-rust/crates/detmir-portal/src/static/app.js index d441be9..b75da0e 100644 --- a/adk-rust/crates/detmir-portal/src/static/app.js +++ b/adk-rust/crates/detmir-portal/src/static/app.js @@ -248,6 +248,9 @@ function updateViewModeButtons() { document.querySelectorAll("[data-view-mode]").forEach(button => { button.classList.toggle("is-active", button.dataset.viewMode === currentViewMode()); }); + document.querySelectorAll("[data-demo-view-mode]").forEach(button => { + button.classList.toggle("is-active", button.dataset.demoViewMode === currentViewMode()); + }); } function setViewMode(mode) { @@ -2298,9 +2301,7 @@ function renderSecurityEventsSummary(summary, options = {}) { const title = options.compact ? "События безопасности" : "События безопасности за 24 часа"; - const compactStateText = disabled && options.compact - ? "Детализация скрыта для роли" - : stateText; + const compactStateText = stateText; const compactModeText = disabled && options.compact ? "Без ИБ-детализации" : disabled @@ -3052,6 +3053,10 @@ document.querySelectorAll("[data-view-mode]").forEach(btn => { btn.addEventListener("click", () => setViewMode(btn.dataset.viewMode)); }); +document.querySelectorAll("[data-demo-view-mode]").forEach(btn => { + btn.addEventListener("click", () => setViewMode(btn.dataset.demoViewMode)); +}); + document.getElementById("periodFilter")?.addEventListener("change", event => { state.period = periodFromSelectValue(event.target.value); refresh({ stage: "Получение данных за выбранный период" }); diff --git a/adk-rust/crates/detmir-portal/src/static/index.html b/adk-rust/crates/detmir-portal/src/static/index.html index f491887..02012cd 100644 --- a/adk-rust/crates/detmir-portal/src/static/index.html +++ b/adk-rust/crates/detmir-portal/src/static/index.html @@ -58,6 +58,14 @@ +