Additional Fixes for Reverse Shell UI

This commit is contained in:
2025-12-06 03:03:58 -07:00
parent cc8a1fade0
commit d522515c04
3 changed files with 27 additions and 9 deletions

View File

@@ -179,5 +179,8 @@ class PowershellChannel:
pass pass
# Include exit code in the close reason for debugging. # Include exit code in the close reason for debugging.
exit_suffix = f" (exit={self._exit_code})" if self._exit_code is not None else "" exit_suffix = f" (exit={self._exit_code})" if self._exit_code is not None else ""
await self._send_close(code, (reason or "powershell_exit") + exit_suffix) close_reason = (reason or "powershell_exit") + exit_suffix
self.role._log(f"reverse_tunnel ps channel stopped channel={self.channel_id} reason={reason or 'exit'}") await self._send_close(code, close_reason)
self.role._log(
f"reverse_tunnel ps channel stopped channel={self.channel_id} reason={close_reason}"
)

View File

@@ -521,6 +521,14 @@ class Role:
self._log(f"reverse_tunnel connection failed tunnel_id={tunnel.tunnel_id}: {exc}", error=True) self._log(f"reverse_tunnel connection failed tunnel_id={tunnel.tunnel_id}: {exc}", error=True)
await self._emit_status({"tunnel_id": tunnel.tunnel_id, "agent_id": self.ctx.agent_id, "status": "error", "reason": "connect_failed"}) await self._emit_status({"tunnel_id": tunnel.tunnel_id, "agent_id": self.ctx.agent_id, "status": "error", "reason": "connect_failed"})
finally: finally:
try:
ws = tunnel.websocket
self._log(
f"reverse_tunnel ws closing tunnel_id={tunnel.tunnel_id} "
f"code={getattr(ws, 'close_code', None)} reason={getattr(ws, 'close_reason', None)}"
)
except Exception:
pass
await self._shutdown_tunnel(tunnel) await self._shutdown_tunnel(tunnel)
async def _pump_sender(self, tunnel: ActiveTunnel) -> None: async def _pump_sender(self, tunnel: ActiveTunnel) -> None:
@@ -562,6 +570,7 @@ class Role:
f"reverse_tunnel websocket closed tunnel_id={tunnel.tunnel_id} " f"reverse_tunnel websocket closed tunnel_id={tunnel.tunnel_id} "
f"code={ws.close_code} reason={ws.close_reason}" f"code={ws.close_code} reason={ws.close_reason}"
) )
tunnel.stop_reason = ws.close_reason or "ws_closed"
break break
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass

View File

@@ -242,7 +242,12 @@ export default function ReverseTunnelPowershell({ device }) {
pollTimerRef.current = setTimeout(async () => { pollTimerRef.current = setTimeout(async () => {
const resp = await emitAsync(socket, "ps_poll", {}); const resp = await emitAsync(socket, "ps_poll", {});
if (resp?.error) { if (resp?.error) {
// Suppress warming/errors in UI; rely on session chips. stopPolling();
disconnectSocket();
setPsStatus({});
setTunnel(null);
setSessionState("error");
return;
} }
if (Array.isArray(resp?.output) && resp.output.length) { if (Array.isArray(resp?.output) && resp.output.length) {
appendOutput(resp.output.join("")); appendOutput(resp.output.join(""));
@@ -251,8 +256,7 @@ export default function ReverseTunnelPowershell({ device }) {
setPsStatus(resp.status); setPsStatus(resp.status);
if (resp.status.closed) { if (resp.status.closed) {
setSessionState("closed"); setSessionState("closed");
setStatusSeverity("warning"); setTunnel(null);
setStatusMessage(resp.status.close_reason || "Session closed");
stopPolling(); stopPolling();
return; return;
} }
@@ -263,7 +267,7 @@ export default function ReverseTunnelPowershell({ device }) {
pollLoop(socket, tunnelId); pollLoop(socket, tunnelId);
}, 520); }, 520);
}, },
[appendOutput, emitAsync, stopPolling] [appendOutput, emitAsync, stopPolling, disconnectSocket]
); );
const handleDisconnect = useCallback(() => { const handleDisconnect = useCallback(() => {
@@ -280,9 +284,8 @@ export default function ReverseTunnelPowershell({ device }) {
} }
stopPolling(); stopPolling();
disconnectSocket(); disconnectSocket();
setTunnel(null);
setSessionState("closed"); setSessionState("closed");
setStatusSeverity("info");
setStatusMessage("Session closed by operator.");
}, [disconnectSocket, stopPolling, tunnel?.tunnel_id]); }, [disconnectSocket, stopPolling, tunnel?.tunnel_id]);
const handleResize = useCallback(() => { const handleResize = useCallback(() => {
@@ -328,6 +331,8 @@ export default function ReverseTunnelPowershell({ device }) {
socket.on("connect_error", () => { socket.on("connect_error", () => {
setStatusSeverity("warning"); setStatusSeverity("warning");
setStatusMessage("Tunnel namespace unavailable."); setStatusMessage("Tunnel namespace unavailable.");
setTunnel(null);
setSessionState("error");
}); });
socket.on("disconnect", () => { socket.on("disconnect", () => {
@@ -336,6 +341,7 @@ export default function ReverseTunnelPowershell({ device }) {
setSessionState("disconnected"); setSessionState("disconnected");
setStatusSeverity("warning"); setStatusSeverity("warning");
setStatusMessage("Socket disconnected."); setStatusMessage("Socket disconnected.");
setTunnel(null);
} }
}); });