diff --git a/core/archipelago/src/api/rpc/mod.rs b/core/archipelago/src/api/rpc/mod.rs index e658ff62..290cff39 100644 --- a/core/archipelago/src/api/rpc/mod.rs +++ b/core/archipelago/src/api/rpc/mod.rs @@ -217,8 +217,14 @@ impl RpcHandler { } // CSRF protection: validate X-CSRF-Token header via HMAC derivation from session token. - // Skip CSRF check if session was just auto-restored from remember-me. - if !is_unauthenticated && new_session_cookies.is_none() { + // Skip CSRF for read-only methods (polling, status) — CSRF prevents state-changing forgery. + // Skip when session was just auto-restored from remember-me (browser has stale CSRF cookie). + let csrf_exempt = matches!(rpc_req.method.as_str(), + "node-messages-received" | "server.echo" | "system.stats" | "tor.status" + | "tor.onion-addresses" | "federation.list-nodes" | "system.get-settings" + | "system.get-node-key" | "system.get-metrics" | "system.get-version" + ); + if !is_unauthenticated && new_session_cookies.is_none() && !csrf_exempt { let csrf_header = parts .headers .get("x-csrf-token") @@ -245,12 +251,24 @@ impl RpcHandler { }; if !csrf_valid { - tracing::warn!( - method = %rpc_req.method, - has_session = session_token.is_some(), - has_header = csrf_header.is_some(), - "403 CSRF validation failed — rejecting RPC call" - ); + // Debug: log expected vs received for diagnosis + if let (Some(token), Some(header)) = (&session_token, &csrf_header) { + let expected = derive_csrf_token(token).await; + tracing::warn!( + method = %rpc_req.method, + session_prefix = %&token[..8.min(token.len())], + csrf_prefix = %&header[..8.min(header.len())], + expected_prefix = %&expected[..8.min(expected.len())], + "403 CSRF mismatch — session/csrf/expected prefixes shown" + ); + } else { + tracing::warn!( + method = %rpc_req.method, + has_session = session_token.is_some(), + has_header = csrf_header.is_some(), + "403 CSRF validation failed — rejecting RPC call" + ); + } return Ok(self.error_response(403, "CSRF token missing or invalid", StatusCode::FORBIDDEN)); } } diff --git a/image-recipe/build-auto-installer-iso.sh b/image-recipe/build-auto-installer-iso.sh index e9c0d9f9..d9a1ddee 100755 --- a/image-recipe/build-auto-installer-iso.sh +++ b/image-recipe/build-auto-installer-iso.sh @@ -2601,29 +2601,18 @@ echo "" # Suppress kernel messages on console (SquashFS errors when USB is pulled) echo 1 > /proc/sys/kernel/printk 2>/dev/null || true -# Copy reboot script to tmpfs so it survives USB removal -cat > /tmp/archipelago-reboot.sh <<'REBOOTSCRIPT' -#!/bin/bash -# This script runs from tmpfs — safe after USB removal -O=$'\033[38;5;208m' -OD=$'\033[38;5;130m' -N=$'\033[0m' - -echo -e " ${O}>>> REMOVE THE USB DRIVE NOW <<<${N}" -echo "" -echo -e " ${OD}Press Enter to reboot (or wait 30 seconds)${N}" - -# Wait for Enter or timeout -read -t 30 -s 2>/dev/null || true +# Show completion message, unmount USB, then reboot +# All done inline — no separate script needed (avoids /bin/bash dependency on squashfs) echo "" -echo -e " ${OD}Rebooting...${N}" -sleep 1 -reboot -f -REBOOTSCRIPT -chmod +x /tmp/archipelago-reboot.sh +p "${ORANGE}>>> REMOVE THE USB DRIVE NOW <<<${NC}" +echo "" +p "${ORANGE_DIM}Press Enter to reboot (or wait 30 seconds)${NC}" -# Lazy-unmount live filesystem BEFORE telling user to pull USB +# Suppress kernel messages (squashfs errors when USB is pulled) +echo 1 > /proc/sys/kernel/printk 2>/dev/null || true + +# Lazy-unmount live filesystem exec 2>/dev/null umount -l /run/live/medium 2>/dev/null || true umount -l /lib/live/mount/medium 2>/dev/null || true @@ -2636,8 +2625,18 @@ if [ -n "$BOOT_DEV" ]; then fi exec 2>&1 -# Hand off to tmpfs-based script — survives USB removal -exec /bin/bash /tmp/archipelago-reboot.sh +# Wait for Enter or timeout +read -t 30 -s 2>/dev/null || true + +echo "" +p "${ORANGE_DIM}Rebooting...${NC}" +sleep 1 + +# Force reboot — multiple methods, first one that works wins +echo b > /proc/sysrq-trigger 2>/dev/null || \ +/sbin/reboot -f 2>/dev/null || \ +/usr/sbin/reboot -f 2>/dev/null || \ +kill -9 1 2>/dev/null INSTALLER_SCRIPT # For unbundled builds, patch the completion message to reflect no pre-loaded apps