From 9ea847923c792b80de568da8c4253327ee1d1c14 Mon Sep 17 00:00:00 2001 From: pptx704 Date: Thu, 16 Apr 2026 06:11:42 +0600 Subject: [PATCH] Fix concurrency, security, and correctness issues across backend and frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - C1: Add sync.RWMutex to vm.Manager to protect concurrent vms map access - H1: Fix IP arithmetic overflow in network slot addressing (byte truncation) - H5: Fix MultiplexedChannel.Fork() TOCTOU race (move exited check inside lock) - H8: Remove snapshot overwrite — return template_name_taken conflict instead - H9: Wrap DeleteAccount DB ops in a transaction, make team deletion fatal - H10: Sanitize serviceErrToHTTP to stop leaking internal error messages - H11: Add deleted_at IS NULL to GetUserByEmail/GetUserByID queries - H12: Add id DESC to audit log composite index for cursor pagination - H15: Delete dead AuthModal.svelte component - H17: Move JWT from WebSocket URL query param to first WS message - H18: Fix $derived to $derived.by in FilesTab breadcrumbs --- db/migrations/20260310094104_initial.sql | 2 +- db/queries/users.sql | 4 +- .../services/process/handler/multiplex.go | 10 +- frontend/src/app.css | 6 +- frontend/src/lib/components/AuthModal.svelte | 210 ------------------ frontend/src/lib/components/FilesTab.svelte | 6 +- .../src/lib/components/MetricsPanel.svelte | 3 +- .../src/lib/components/TerminalTab.svelte | 10 +- frontend/src/lib/components/Toaster.svelte | 1 + .../src/routes/admin/capsules/+page.svelte | 2 +- frontend/src/routes/admin/hosts/+page.svelte | 10 + frontend/src/routes/admin/teams/+page.svelte | 2 +- .../src/routes/admin/templates/+page.svelte | 11 + frontend/src/routes/admin/users/+page.svelte | 2 +- .../src/routes/dashboard/audit/+page.svelte | 10 +- .../src/routes/dashboard/billing/+page.svelte | 13 +- .../routes/dashboard/capsules/+page.svelte | 21 +- .../routes/dashboard/channels/+page.svelte | 19 +- .../src/routes/dashboard/hosts/+page.svelte | 10 +- .../src/routes/dashboard/keys/+page.svelte | 10 +- .../routes/dashboard/settings/+page.svelte | 10 +- .../src/routes/dashboard/team/+page.svelte | 57 ++--- .../routes/dashboard/templates/+page.svelte | 19 +- .../src/routes/dashboard/usage/+page.svelte | 13 +- .../src/routes/forgot-password/+page.svelte | 4 +- internal/api/handlers_exec_stream.go | 56 ++++- internal/api/handlers_me.go | 29 ++- internal/api/handlers_process.go | 66 ++++-- internal/api/handlers_pty.go | 58 ++++- internal/api/handlers_snapshots.go | 19 +- internal/api/helpers_ws.go | 109 +++++++++ internal/api/middleware.go | 20 +- internal/api/middleware_admin.go | 13 ++ internal/api/middleware_auth.go | 14 +- internal/api/middleware_jwt.go | 14 +- internal/api/server.go | 8 +- internal/network/setup.go | 17 +- internal/vm/manager.go | 20 +- pkg/db/users.sql.go | 4 +- 39 files changed, 532 insertions(+), 380 deletions(-) delete mode 100644 frontend/src/lib/components/AuthModal.svelte create mode 100644 internal/api/helpers_ws.go diff --git a/db/migrations/20260310094104_initial.sql b/db/migrations/20260310094104_initial.sql index 6c8afc4..22544a5 100644 --- a/db/migrations/20260310094104_initial.sql +++ b/db/migrations/20260310094104_initial.sql @@ -171,7 +171,7 @@ CREATE TABLE audit_logs ( metadata JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE INDEX idx_audit_logs_team_time ON audit_logs(team_id, created_at DESC); +CREATE INDEX idx_audit_logs_team_time ON audit_logs(team_id, created_at DESC, id DESC); CREATE INDEX idx_audit_logs_team_resource ON audit_logs(team_id, resource_type, created_at DESC); -- sandbox_metrics_snapshots diff --git a/db/queries/users.sql b/db/queries/users.sql index 1c902a3..783f22b 100644 --- a/db/queries/users.sql +++ b/db/queries/users.sql @@ -4,10 +4,10 @@ VALUES ($1, $2, $3, $4) RETURNING *; -- name: GetUserByEmail :one -SELECT * FROM users WHERE email = $1; +SELECT * FROM users WHERE email = $1 AND deleted_at IS NULL; -- name: GetUserByID :one -SELECT * FROM users WHERE id = $1; +SELECT * FROM users WHERE id = $1 AND deleted_at IS NULL; -- name: InsertUserOAuth :one INSERT INTO users (id, email, name) diff --git a/envd/internal/services/process/handler/multiplex.go b/envd/internal/services/process/handler/multiplex.go index a3f6ea3..88f0916 100644 --- a/envd/internal/services/process/handler/multiplex.go +++ b/envd/internal/services/process/handler/multiplex.go @@ -35,27 +35,27 @@ func NewMultiplexedChannel[T any](buffer int) *MultiplexedChannel[T] { c.mu.RUnlock() } + c.mu.Lock() c.exited.Store(true) - for _, cons := range c.channels { close(cons) } + c.mu.Unlock() }() return c } func (m *MultiplexedChannel[T]) Fork() (chan T, func()) { + m.mu.Lock() + defer m.mu.Unlock() + if m.exited.Load() { ch := make(chan T) close(ch) - return ch, func() {} } - m.mu.Lock() - defer m.mu.Unlock() - consumer := make(chan T, 4096) m.channels = append(m.channels, consumer) diff --git a/frontend/src/app.css b/frontend/src/app.css index 823f8ce..d76358b 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -180,9 +180,11 @@ body { 50% { transform: translateY(-6px); } } -/* CSS containment — isolate layout/paint for independent UI regions */ +/* CSS containment — isolate paint for independent UI regions. + Note: `contain: layout` is omitted because it creates a containing block + that breaks `position: fixed` popups rendered inside
. */ main { - contain: layout style; + contain: style; } /* Respect user motion preferences — covers both CSS class animations and inline style animations */ diff --git a/frontend/src/lib/components/AuthModal.svelte b/frontend/src/lib/components/AuthModal.svelte deleted file mode 100644 index fddedaa..0000000 --- a/frontend/src/lib/components/AuthModal.svelte +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - - - - - - -
- -
- - {title} - - - {subtitle} - -
- - - - - -
-
- or -
-
- - -
- {#if mode === 'signup'} -
-
- -
- -
- {/if} - -
-
- -
- -
- -
-
- -
- - -
- - {#if mode === 'signin'} -
- -
- {/if} - - -
- - -

- {switchText} - -

-
-
-
-
- - diff --git a/frontend/src/lib/components/FilesTab.svelte b/frontend/src/lib/components/FilesTab.svelte index bdc2a75..46c2d4a 100644 --- a/frontend/src/lib/components/FilesTab.svelte +++ b/frontend/src/lib/components/FilesTab.svelte @@ -81,7 +81,7 @@ ); // Breadcrumb segments from currentPath - const breadcrumbs = $derived(() => { + const breadcrumbs = $derived.by(() => { const parts = currentPath.split('/').filter(Boolean); const crumbs: { name: string; path: string }[] = [{ name: '/', path: '/' }]; for (let i = 0; i < parts.length; i++) { @@ -517,7 +517,7 @@ - {#each breadcrumbs() as crumb, i} + {#each breadcrumbs as crumb, i} {#if i > 0} @@ -526,7 +526,7 @@ {t.message}
+ + {#snippet skeletonRows()}
diff --git a/frontend/src/routes/admin/teams/+page.svelte b/frontend/src/routes/admin/teams/+page.svelte index a09b3d9..90a8db2 100644 --- a/frontend/src/routes/admin/teams/+page.svelte +++ b/frontend/src/routes/admin/teams/+page.svelte @@ -360,7 +360,7 @@ -
+
diff --git a/frontend/src/routes/admin/templates/+page.svelte b/frontend/src/routes/admin/templates/+page.svelte index 14514ae..ae678ed 100644 --- a/frontend/src/routes/admin/templates/+page.svelte +++ b/frontend/src/routes/admin/templates/+page.svelte @@ -365,6 +365,16 @@
+
+
+ + + + + All systems operational +
+
+ {#snippet skeletonRows(count: number, headers: string[])} @@ -838,6 +848,7 @@ {createForm.archive.name}