From 7bbd8f889a668896a3238ae1aaabdf81e6a22ca9 Mon Sep 17 00:00:00 2001 From: Dorian Date: Wed, 18 Mar 2026 22:37:08 +0000 Subject: [PATCH] security: RBAC viewer role, identity label length, error sanitization - RBAC: Viewer role changed from prefix "system." to explicit allowlist of safe read-only methods. Prevents Viewer access to system.factory-reset, system.shutdown, system.reboot, system.disk-cleanup. - identity.create: Name/label param now enforces max 100 chars. - sanitize_error_message: Changed from contains() to starts_with() for prefix matching, preventing internal errors that happen to contain user-facing keywords from leaking through. Co-Authored-By: Claude Opus 4.6 (1M context) --- core/archipelago/src/api/rpc/identity.rs | 7 +++++-- core/archipelago/src/api/rpc/mod.rs | 2 +- core/archipelago/src/auth.rs | 9 +++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/core/archipelago/src/api/rpc/identity.rs b/core/archipelago/src/api/rpc/identity.rs index e1a86fc9..eff4010d 100644 --- a/core/archipelago/src/api/rpc/identity.rs +++ b/core/archipelago/src/api/rpc/identity.rs @@ -60,8 +60,11 @@ impl RpcHandler { let name = params .get("name") .and_then(|v| v.as_str()) - .unwrap_or("Personal") - .to_string(); + .unwrap_or("Personal"); + if name.len() > 100 { + anyhow::bail!("Identity name must be 100 characters or fewer"); + } + let name = name.to_string(); let purpose_str = params .get("purpose") diff --git a/core/archipelago/src/api/rpc/mod.rs b/core/archipelago/src/api/rpc/mod.rs index 16b811c4..ec64a7c5 100644 --- a/core/archipelago/src/api/rpc/mod.rs +++ b/core/archipelago/src/api/rpc/mod.rs @@ -87,7 +87,7 @@ fn sanitize_error_message(msg: &str) -> String { "Session", ]; for prefix in &user_facing_prefixes { - if msg.starts_with(prefix) || msg.contains(prefix) { + if msg.starts_with(prefix) { // Truncate long messages and strip file paths let sanitized = msg.replace("/var/lib/archipelago/", "[data]/") .replace("/usr/local/bin/", "[bin]/") diff --git a/core/archipelago/src/auth.rs b/core/archipelago/src/auth.rs index 0c7760c8..f8c02005 100644 --- a/core/archipelago/src/auth.rs +++ b/core/archipelago/src/auth.rs @@ -32,8 +32,13 @@ impl UserRole { match self { UserRole::Admin => true, UserRole::Viewer => { - // Read-only methods - method.starts_with("system.") + // Read-only system methods (explicit allowlist — NOT prefix "system." + // which would grant access to system.factory-reset, system.shutdown, etc.) + method == "system.stats" + || method == "system.processes" + || method == "system.temperature" + || method == "system.disk-status" + || method == "system.detect-usb-devices" || method.starts_with("node.") || method.starts_with("federation.list") || method.starts_with("dwn.status")