diff --git a/CHANGELOG.md b/CHANGELOG.md index 20a8393..813890c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,19 @@ The format is inspired by Keep a Changelog and this project follows Semantic Ver ### Added -- (none yet) +- **Dashboard UX overhaul** (#99): 5-tab layout (Runs, Findings, Cases, Compare, Health) with SVG donut severity charts, clickable evidence chains, run comparison diff view, JSON/CSV export, real-time progress spinners, and case management panel. +- **Tool plugin API** (#102): extend WraithRun with external tool plugins via `tool.toml` manifests and subprocess JSON I/O. + - `--tools-dir` and `--allowed-plugins` CLI flags. + - Automatic plugin discovery, platform filtering, sandbox policy enforcement, and timeout support. + - Plugin tools appear in `--doctor` output and `/api/v1/runtime/status` endpoint. + - Example plugin in `examples/tools/hello_world/`. + - Full documentation in `docs/plugin-api.md`. +- **Security professional documentation** (#100): + - Four investigation playbooks: SSH key compromise, Windows triage, credential leak audit, persistence sweep. + - MITRE ATT&CK mapping for all 8 built-in tools. + - Threat model with attack surface, trust boundaries, and security controls. + - Two anonymized sample investigation reports. +- Added `io-util` feature to workspace tokio dependency for plugin subprocess I/O. ### Changed diff --git a/Cargo.lock b/Cargo.lock index 80a244b..a277d57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,7 +83,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "api_server" -version = "1.0.0" +version = "1.1.0" dependencies = [ "anyhow", "axum", @@ -308,7 +308,7 @@ dependencies = [ [[package]] name = "core_engine" -version = "1.0.0" +version = "1.1.0" dependencies = [ "anyhow", "async-trait", @@ -372,14 +372,16 @@ dependencies = [ [[package]] name = "cyber_tools" -version = "1.0.0" +version = "1.1.0" dependencies = [ "async-trait", "serde", "serde_json", "sha2", + "tempfile", "thiserror 1.0.69", "tokio", + "toml", "tracing", ] @@ -517,6 +519,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f" + [[package]] name = "filetime" version = "0.2.27" @@ -783,7 +791,7 @@ dependencies = [ [[package]] name = "inference_bridge" -version = "1.0.0" +version = "1.1.0" dependencies = [ "anyhow", "async-trait", @@ -1524,6 +1532,19 @@ dependencies = [ "xattr", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2096,7 +2117,7 @@ dependencies = [ [[package]] name = "wraithrun" -version = "1.0.0" +version = "1.1.0" dependencies = [ "anyhow", "api_server", diff --git a/Cargo.toml b/Cargo.toml index 0a9285d..4605618 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ serde_json = "1.0" toml = "0.8" sha2 = "0.10" thiserror = "1.0" -tokio = { version = "1.44", features = ["macros", "rt-multi-thread", "process", "time", "net"] } +tokio = { version = "1.44", features = ["macros", "rt-multi-thread", "process", "time", "net", "io-util"] } tower-http = { version = "0.6", features = ["cors", "trace", "limit"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } diff --git a/api_server/src/dashboard.html b/api_server/src/dashboard.html index 95eb7b9..4b293c9 100644 --- a/api_server/src/dashboard.html +++ b/api_server/src/dashboard.html @@ -63,7 +63,8 @@ .sev.info { background: #1f2937; color: var(--muted); } /* Findings */ -.finding { padding: 0.75rem; border-left: 3px solid var(--border); margin-bottom: 0.5rem; } +.finding { padding: 0.75rem; border-left: 3px solid var(--border); margin-bottom: 0.5rem; cursor: pointer; } +.finding:hover { background: rgba(88,166,255,0.05); } .finding.Critical { border-left-color: var(--red); } .finding.High { border-left-color: var(--orange); } .finding.Medium { border-left-color: var(--yellow); } @@ -85,6 +86,9 @@ .btn:hover { opacity: 0.9; } .btn:disabled { opacity: 0.5; cursor: not-allowed; } .btn-danger { background: var(--red); } +.btn-secondary { background: transparent; border: 1px solid var(--border); color: var(--text); } +.btn-secondary:hover { border-color: var(--accent); } +.btn-sm { padding: 0.25rem 0.625rem; font-size: 0.75rem; } /* Detail panel */ .detail-panel { position: fixed; top: 0; right: -500px; width: 500px; height: 100vh; @@ -105,6 +109,41 @@ .token-box h2 { margin-bottom: 1rem; font-size: 1.125rem; } .token-box input { width: 100%; padding: 0.5rem 0.75rem; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); font-size: 0.875rem; margin-bottom: 1rem; } + +/* Donut chart */ +.donut-container { display: flex; align-items: center; gap: 1rem; margin: 0.75rem 0; } +.donut-legend { font-size: 0.8125rem; } +.donut-legend div { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.25rem; } +.donut-legend .swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; } + +/* Evidence chain */ +.evidence-chain { background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 0.75rem; margin-top: 0.5rem; font-size: 0.8125rem; } +.evidence-chain .step { padding: 0.25rem 0; border-bottom: 1px solid var(--border); } +.evidence-chain .step:last-child { border-bottom: none; } +.evidence-chain .tool-name { color: var(--accent); font-weight: 600; } +.evidence-chain .turn-num { color: var(--muted); } + +/* Compare view */ +.compare-controls { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; } +.compare-controls select { padding: 0.375rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; + background: var(--surface); color: var(--text); font-size: 0.8125rem; } +.diff-new { border-left: 3px solid var(--green); padding-left: 0.5rem; margin-bottom: 0.5rem; } +.diff-resolved { border-left: 3px solid var(--muted); padding-left: 0.5rem; margin-bottom: 0.5rem; opacity: 0.7; text-decoration: line-through; } +.diff-changed { border-left: 3px solid var(--yellow); padding-left: 0.5rem; margin-bottom: 0.5rem; } + +/* Export bar */ +.export-bar { display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; } + +/* Case list */ +.case-row { display: grid; grid-template-columns: 1fr auto auto auto; gap: 1rem; align-items: center; padding: 0.75rem 1rem; border-bottom: 1px solid var(--border); cursor: pointer; } +.case-row:hover { background: rgba(88,166,255,0.05); } +.case-row:last-child { border-bottom: none; } + +/* Progress indicator */ +.progress-indicator { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; color: var(--accent); } +.spinner { width: 14px; height: 14px; border: 2px solid var(--border); border-top-color: var(--accent); + border-radius: 50%; animation: spin 0.8s linear infinite; display: inline-block; } +@keyframes spin { to { transform: rotate(360deg); } }
@@ -131,6 +170,8 @@