From da06ecb97b43736544a57a7421d35068eea2ac85 Mon Sep 17 00:00:00 2001 From: pptx704 Date: Mon, 13 Apr 2026 02:09:50 +0600 Subject: [PATCH] Fix file browser crash on non-regular files and connection leaks - envd: reject non-regular files (devices, pipes, sockets) in GetFiles to prevent infinite reads from /dev/zero, /dev/urandom etc. - host agent: add context cancellation check in ReadFileStream loop with proper Connect error codes - frontend: abort in-flight file reads on file switch, directory navigation, and component teardown via AbortController - frontend: guard against abort errors surfacing in UI, use try/finally for fileLoading state --- envd/internal/api/download.go | 12 +++++ frontend/src/lib/api/files.ts | 20 ++++++-- frontend/src/lib/components/FilesTab.svelte | 57 ++++++++++++++------- internal/hostagent/server.go | 10 ++++ 4 files changed, 78 insertions(+), 21 deletions(-) diff --git a/envd/internal/api/download.go b/envd/internal/api/download.go index b90a8ac..0a2119e 100644 --- a/envd/internal/api/download.go +++ b/envd/internal/api/download.go @@ -1,4 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 +// Modifications by M/S Omukk package api @@ -106,6 +107,17 @@ func (a *API) GetFiles(w http.ResponseWriter, r *http.Request, params GetFilesPa return } + // Reject anything that isn't a regular file (devices, pipes, sockets, etc.). + // Reading device files like /dev/zero or /dev/urandom produces infinite data + // and will exhaust memory on all layers of the stack. + if !stat.Mode().IsRegular() { + errMsg = fmt.Errorf("path '%s' is not a regular file", resolvedPath) + errorCode = http.StatusBadRequest + jsonError(w, errorCode, errMsg) + + return + } + // Validate Accept-Encoding header encoding, err := parseAcceptEncoding(r) if err != nil { diff --git a/frontend/src/lib/api/files.ts b/frontend/src/lib/api/files.ts index e89daad..d7ac54b 100644 --- a/frontend/src/lib/api/files.ts +++ b/frontend/src/lib/api/files.ts @@ -72,7 +72,11 @@ export async function listDir(capsuleId: string, path: string, depth = 1): Promi } } -export async function readFile(capsuleId: string, path: string): Promise> { +export async function readFile( + capsuleId: string, + path: string, + signal?: AbortSignal, +): Promise> { try { const headers: Record = { 'Content-Type': 'application/json' }; if (auth.token) headers['Authorization'] = `Bearer ${auth.token}`; @@ -81,6 +85,7 @@ export async function readFile(capsuleId: string, path: string): Promise { +export async function downloadFile( + capsuleId: string, + path: string, + filename: string, + signal?: AbortSignal, +): Promise { const headers: Record = { 'Content-Type': 'application/json' }; if (auth.token) headers['Authorization'] = `Bearer ${auth.token}`; @@ -108,6 +121,7 @@ export async function downloadFile(capsuleId: string, path: string, filename: st method: 'POST', headers, body: JSON.stringify({ path }), + signal, }); if (!res.ok) throw new Error('Download failed'); diff --git a/frontend/src/lib/components/FilesTab.svelte b/frontend/src/lib/components/FilesTab.svelte index effce24..db7937f 100644 --- a/frontend/src/lib/components/FilesTab.svelte +++ b/frontend/src/lib/components/FilesTab.svelte @@ -1,4 +1,5 @@