Audit date: 2026-08-19
Scope: app/, components/, hooks/, lib/, root server/configuration files, server actions, package.json/lockfile, and all tracked Supabase SQL. This is a static code review; no application code or database state was changed. “Confirmed” means the behavior follows directly from the tracked code. Findings that depend on which SQL files were applied are labeled as such.
This repository is not safe to expose to untrusted users and does not currently implement the real-time collaborative editor described by the README. The most urgent issue is an unauthenticated Socket.IO endpoint that gives any network client control of a host shell. Separately, the editor contains no Yjs integration and never emits or consumes the server's code/cursor events, so concurrent editing is ordinary last-write-wins database saving rather than CRDT collaboration.
The database setup is also not reproducible: multiple mutually incompatible schemas and policy “fix” scripts are committed, including scripts that disable RLS or make all source code globally readable/editable. There is no ordered migration history that establishes one known production state.
Validation performed:
npm run build: passes when Google Fonts is reachable.tsc --noEmit: passes.npm run lint: fails before linting because the ESLint 9 CLI no longer accepts the configured--extoption.npm audit --omit=dev: 12 production findings (8 high, 3 moderate, 1 low).npm outdated: core versions are behind current releases; details are prioritized below.- Tracked-secret scan: no literal API key, service-role key, private key, password, or token was found. Runtime secrets are nevertheless exposed to the terminal process described below.
-
Remove or completely isolate the unauthenticated remote-shell endpoint.
server.ts:16-24accepts cross-origin Socket.IO clients withorigin: "*";server.ts:28-119has no authentication, authorization, origin enforcement, event schema validation, or rate limit. A caller chooses the executable and working directory (server.ts:42-66), the process receives the entire server environment (server.ts:66), and arbitrary input is written to the PTY (server.ts:88-93). This is direct remote code execution under the application OS account and exposes values such asANTHROPIC_API_KEYand deployment credentials available inprocess.env. The TODO atserver.ts:54-56explicitly confirms that the claimed directory whitelist does not exist. -
Fix cross-user terminal takeover and resource exhaustion. Terminal IDs are global, caller-controlled map keys (
server.ts:27,server.ts:42-48,server.ts:68). Every browser starts with terminal ID"1"(components/terminal/TerminalManager.tsx:27-35), so unrelated users attach to the same server shell, receive its output, and can send input, resize it, or kill it. Any anonymous client can also spawn unbounded PTYs; disconnect does not terminate them (server.ts:121-123). Dynamic per-terminal event names and unbounded event payloads add further memory/CPU denial-of-service paths. -
Implement the advertised collaboration system; it is currently absent. No application file imports
yjs,y-websocket,y-monaco, ory-protocols.CodeEditoris a controlled Monaco textarea (components/editor/CodeEditor.tsx:133-156), andRoomEditoronly performs a debounced Supabase save (components/editor/RoomEditor.tsx:53-77). The client never emitsjoin-room,code-change, orcursor-move, and never listens forcode-updateorcursor-update; those events exist only as an unused unauthenticated relay inserver.ts:33-39andserver.ts:111-119. There is therefore no Y.Doc, awareness/presence, CRDT merge, persistence, reconnection recovery, or Yjs WebSocket server. Concurrent edits overwrite each other. -
Close the room-membership policy bypass. The intended policies allow any authenticated user to insert a
room_membersrow for themselves in any room, without proving possession of the room code, an invitation, or public-room eligibility (supabase/SETUP.sql:162-170; repeated bysupabase/COMPREHENSIVE_RLS_FIX.sql:47-54). Because the anon key is intentionally public, a client can call Supabase directly and bypassjoinRoom()'s code lookup. Once inserted, the user satisfies the read/edit policies for that room. Enforce invitation/public-room rules atomically in a restricted database function and revoke direct insert access.
-
Replace the contradictory SQL collection with one ordered, idempotent migration chain.
supabase/SETUP.sqldrops all application tables (:5-17), createscode_sessions.file_name(:67-75) while the application requiresname,type, andparent_id, and does not create snippets, the storage bucket, orfind_room_by_code.supabase_schema.sqldefines different table/column names (chat_messages,message) and a different role model. Ad-hoc scripts repeatedly drop/recreate policies and triggers. It is impossible to infer or reproduce the deployed schema from version control, and applying files in the wrong order can destroy data or silently change the security model. -
Remove dangerous database scratch scripts from the deployable path.
supabase/disable_code_sessions_rls.sql:1-5disables RLS.supabase_schema.sql:61-74allows everyone to read all rooms, members, code, and chat, and allows every authenticated user to edit every code session.supabase/check_auth_state.sql:7-10,debug_disable_trigger.sql:4-6,disable_trigger_ver2.sql:4-8, andfix_signup_final.sql:5-24drop authentication triggers/functions.fix_and_verify_room.sqlcontains a hard-coded production-looking room UUID and assigns the first auth user as owner. Several of these files are tracked despite.gitignorepatterns intended to exclude debug/check scripts. -
Make the WebSocket layer authenticate every connection and authorize every room/event. Apart from the terminal RCE, any caller can join arbitrary Socket.IO room names and relay arbitrary code/cursor payloads (
server.ts:28-39,:111-119). Add Supabase JWT verification during the handshake, membership/role checks for each room, typed and size-bounded event schemas, origin restrictions, per-user/per-IP connection and message limits, room-size limits, and backpressure. Proxy is explicitly excluded from/socket.io, so HTTP route protection does not help (proxy.ts:21-23). -
Resolve the split development/production server architecture.
npm run devuses the custom Express/Socket.IO server, whilenpm startrunsnext startand never executesserver.ts(package.json:6-9). Thus terminal and socket features disappear in production.NEXT_PUBLIC_SITE_URLis undocumented and defaults tohttp://localhost:3000, which makes production browsers connect to their own localhost when unset (components/providers/socket-provider.tsx:28-35). A Vercel deployment cannot host these stateful PTYs.proxy.ts, on the other hand, is the valid Next.js 16 request-boundary convention and is not inherently redundant. Decide on two explicit services (recommended: standard Next.js plus a separately deployed, hardened collaboration service) or consistently build/start a hardened custom server. Next.js also documents that custom servers remove Automatic Static Optimization: Custom Server. -
Upgrade the vulnerable production dependency graph immediately. The lockfile's
npm audit --omit=devresult is 8 high, 3 moderate, and 1 low. Directly relevant issues include multiple Next.js 16.1.6 Proxy bypass, Server Action DoS/SSRF, and WebSocket-upgrade SSRF advisories, pluswsmemory-exhaustion and disclosure issues and vulnerable Socket.IO transport/parser dependencies.npm auditoffers a non-major Next upgrade; currentnpm outdatedreports Next/eslint-config-next16.3.1 andws8.21.3. At minimum, upgrade and regression-test these before deployment. Examples: Next Proxy bypass, Next custom-server Server Action SSRF, ws memory exhaustion, and Engine.IO connection exhaustion. -
Protect the Anthropic-backed Server Actions from key abuse.
getAICompletionandgetInlineSuggestionhave no authenticated-user check, authorization, input-size limit, usage quota, concurrency limit, or per-IP/user rate limit (app/actions/ai.ts:10-71). Anyone who can invoke the Server Actions can spend the project's Anthropic balance and submit arbitrarily large code/context. Log safe usage metadata, not prompts/code, and enforce server-side budgets. -
Update the retired Anthropic models; both AI paths are broken as of the audit date.
claude-3-5-sonnet-20241022was retired on 2025-10-28 andclaude-3-5-haiku-20241022on 2026-02-19, so requests atapp/actions/ai.ts:20and:55fail. Anthropic's current lifecycle table and replacements are documented in Model deprecations. The SDK is also far behind (0.71.2;npm outdatedreports0.117.1). -
Fix the
FileExplorerrequest/subscription loop. It creates a new Supabase client on every render (components/editor/FileExplorer/index.tsx:41), includes that identity inloadFilesdependencies (:44-59), and includes both in the effect (:61-84).loadFiles()callssetFiles, causing a render, new client/callback, cleanup, resubscribe, and another fetch. Memoize/singleton the browser client and make effect dependencies stable.RealtimeRoomsListenerhas the same unstable client pattern (components/rooms/RealtimeRoomsListener.tsx:7-36), though it does not itself set local state on initial subscription. -
Prevent editor data loss. Clearing a file cannot be saved because
handleCodeChangereturns whenvalueis an empty string (components/editor/RoomEditor.tsx:53-60). The debounced save marks a file clean infinallyeven when the save failed (:63-76), falsely telling the user data is persisted. Closing a dirty tab has no confirmation/flush, and there is no unload flush. With no CRDT/version check, stale clients overwrite newer content. -
Unify file state and constrain every mutation to the actual room.
RoomEditorowns an initial/open-file snapshot whileFileExplorerindependently refetches another file list; rename/delete/remote updates do not reconcile open tabs. Initial files are not filtered bytype, so folders can become Monaco tabs (components/editor/RoomEditor.tsx:26-36).saveFileContentchecks permission for caller-suppliedroomIdbut updates solely byfileId(actions/file-actions.ts:78-100). Version creation/restoration similarly accepts unrelatedroomId,fileId, andversionIdvalues (actions/version-actions.ts:6-37,:61-93). Correct RLS may block some cross-room writes, but the actions themselves do not establish object relationships; a mismatched version row can already be inserted because the foreign keys do not require the version room and file room to match. -
Make room lifecycle changes transactional and enforce invariants in the database. Room creation and owner membership are separate statements; membership failure is only logged and the user is still redirected (
actions/room-actions.ts:20-62). The committedfix_orphaned_rooms.sqldemonstrates this has already produced orphaned rooms.leaveRoomallows an owner to remove their own membership (actions/room-actions.ts:107-127), leavingowner_idintact but locking that owner out ofRoomPage, which requires membership. Last-owner and role-transition rules exist only in race-prone application checks and can be bypassed via direct Supabase calls. -
Restrict storage objects by tenant/room and validate uploads. The supposedly private
project-filesbucket allows every authenticated user to read every object and upload under arbitrary keys (supabase/storage_and_realtime.sql:10-24).ImportFilesDialoguploads source files there, then duplicates their content incode_sessions(components/editor/ImportFilesDialog.tsx:47-95). There are no file count/size/type limits, path ownership checks, content scanning, rollback when database creation fails, or cleanup of orphaned uploads. This is a cross-tenant source-code disclosure if those policies are deployed. -
Stop publishing users' email addresses globally. Profiles contain email and are selectable by everyone (
supabase/SETUP.sql:27-32,:126-131; repeated inmigrations/create_profiles.sql). This exposes the user directory through the public Supabase API. Split public display data from private identity data or scope profile reads to relevant room memberships.
-
Repair linting and make it a required check.
npm run lintexits with “Invalid option--ext” under ESLint 9 (package.json:10). The repo has legacy.eslintrc.json, while ESLint 9 expects flat configuration by default. As a result, no lint rules currently run and the manyany/suppression/debug patterns receive no automated review. -
Add tests and CI. There is no test script, test framework/configuration, test/spec file, or tracked CI workflow. Minimum production coverage should include RLS tests executed as anon/user/owner/editor/viewer; Server Action authorization/object-binding tests; WebSocket handshake/event authorization and abuse tests; two-client convergence/reconnect tests for Yjs; room/file/message/version lifecycle tests; and build/type/lint/security checks in CI.
-
Make Proxy failure closed and keep authorization at the data boundary.
proxy.ts:5-11catches every session-refresh error and continues the request.lib/supabase/middleware.ts:13-47also substitutes a fake client on configuration/runtime failure. Page-level checks reduce exposure on some pages, but a transient auth/config failure must not silently weaken the network boundary. Proxy should remain an optimistic redirect layer; every Server Action, route, Supabase policy, and socket event must independently authenticate and authorize. -
Validate environment at process/build startup. Supabase URL/key failures are caught and replaced with broad
anyfake clients in browser, server, and Proxy code (lib/supabase/client.ts:3-40,lib/supabase/server.ts:4-68,lib/supabase/middleware.ts:5-47), masking deployment mistakes as empty data or redirects.NEXT_PUBLIC_SITE_URLandANTHROPIC_API_KEYare not documented in README. Add a typed server/client environment schema, validate URL formats and required production values once, and fail startup with a clear error. Do not expose server-only values throughNEXT_PUBLIC_*. -
Fix public-room and invitation UX, which is currently nonfunctional. Public room cards link directly to
/room/:id, butRoomPagerejects non-members (app/room/[roomId]/page.tsx:20-31); users must separately copy the visible code and use Join.InviteDialogbuilds/rooms?join=...and offers a selected default role, but the rooms page never reads that query and the role is never submitted (components/permissions/InviteDialog.tsx:33-80). Both Invite and Import dialog state is permanently false with no opener (components/editor/RoomEditor.tsx:39-40,:134-149). The empty-room “Create a Room” event has no listener (components/rooms/RoomList.tsx:69-81). -
Fix chat real-time edge cases. Typing events are emitted but the server has no
typing/stop-typinghandlers and no UI consumes them (components/chat/MessageInput.tsx:33-45). “Delete” is a soft-delete UPDATE, but the realtime UPDATE handler merges the row instead of removing rows withdeleted_at, so deleted messages remain visible until reload (actions/chat-actions.ts:70-96;components/chat/MessageList.tsx:85-102). Pagination has no error UI, and the RadixScrollArearef/onScroll are attached to the Root rather than its scrolling Viewport (components/ui/scroll-area.tsx:8-22), so auto-scroll and bottom detection are unreliable. -
Make execution rate limiting atomic and fail closed.
checkRateLimitcounts rows, thenexecuteCodeinserts later (actions/execute-actions.ts:36-75,:92-119), so concurrent calls can all pass. Insert errors are ignored and execution proceeds, allowing unlimited execution if the rate-limit table/policy is missing or broken. Add an atomic database function/transaction, check its result, bound code/stdin sizes, validatelimit, add an outbound request timeout, and separately rate-limit by user/IP/room. -
Add framework-native error/loading/not-found states. A client class boundary exists (
components/error-boundary.tsx) but there is no App Routererror.tsx/global-error.tsxand it cannot catch errors from the parent server layout or all server rendering. Only/roomshasloading.tsx; room/editor/profile/settings/auth routes have no route loading state. Database query errors are frequently ignored and presented as empty data (app/(dashboard)/rooms/page.tsx:20-69,app/room/[roomId]/page.tsx:33-38). There is no deliberatenot-found.tsx, retry policy, socketconnect_errorUI, or production error reporting. -
Add request/input validation to every Server Action. Most actions accept raw strings/objects without Zod validation or length/shape bounds. Examples include blank/oversized room names and messages, arbitrary roles, unbounded code/stdin/AI prompts, unbounded pagination values, and arbitrary editor metadata. UI validation is bypassable. Database checks are inconsistent across the competing schemas;
SETUP.sql, for example, lacks the message length constraint present increate_messages_table.sql. -
Fix framework/auth route inconsistencies. Unauthenticated
RoomsPageredirects to nonexistent/auth(app/(dashboard)/rooms/page.tsx:16-18), and a failed auth callback redirects to nonexistent/auth/auth-code-error(app/auth/callback/route.ts:32). The callback trustsx-forwarded-hostin production and accepts an unconstrainednextstring (:5-27); normalizenextto an allowlisted internal path and use a trusted configured origin. Signup collectsfullNamebut discards it (app/(auth)/signup/page.tsx:44-51), and “remember me” is never used. -
Align React runtime and type packages and stop hiding peer conflicts. Runtime packages are React/React DOM 19.0.0 while
@types/reactand@types/react-domare version 18 (package.json:55-57,:73-74)..npmrcsetslegacy-peer-deps=true, which can conceal incompatible trees. Currentnpm outdatedreports React 19.2.8 and React type packages 19.x. Upgrade as a tested set with Next andeslint-config-next; do not mix majors. No specific hydration defect was proven in this review, butreactStrictMode: falseand rootsuppressHydrationWarningreduce detection rather than solving mismatches (next.config.ts:3-5,app/layout.tsx:51-54). -
Add operational controls. There is no graceful shutdown that kills PTYs/closes Socket.IO, health/readiness endpoint, structured logging, request/correlation IDs, metrics, alerting, audit log for membership/role changes, retention/cleanup policy for execution history and uploads, backup/restore procedure, or collaboration-room capacity/rate limit.
cleanup_old_rate_limits()is defined but never scheduled (supabase/migrations/create_execution_tables.sql:63-71). -
Add baseline HTTP security policy.
next.config.tsonly disables Strict Mode. There is no Content Security Policy, clickjacking protection, Referrer/Permissions policy, explicit HSTS strategy, or protection for remote assets. The application loads texture images from third-party origins in auth/home layouts, leaking client requests and complicating CSP. This is defense-in-depth, but particularly important around auth and user-authored code.
-
Remove tracked scratch/debug artifacts.
inspect_exports.jsonly requiresreact-resizable-panelsand logs its exports. The numerouscheck_*,debug_*,deep_debug_*, trigger-disable, one-off repair, and hard-coded room SQL files are operational notes/scratch work, not migrations. Move any still-useful diagnostics into documented, non-mutating admin tooling; delete or archive the rest outside the migration path. -
Prune unreachable/dead UI. Confirmed unreferenced components include
components/auth-status.tsx,user-auth-form.tsx,theme-toggle.tsx,landing/Testimonials.tsx,layout/AnimatedBackground.tsx,ui/GlassComponents.tsx,editor/EditorSettings.tsx,editor/LanguageSelector.tsx, and the entireExecutionPanelchain. The legacy Radix toast/toaster/use-toast chain is also disconnected because the app uses Sonner.ActivityBarexposeshistory, butSideBarhas no matching case and renders blank; search/settings are “coming soon.” Title-bar layout/settings buttons and AI quick-action buttons have no behavior. Decide which features are in scope and remove or wire them. -
Prune unused or accidentally satisfied dependencies. No code imports
cross-env,@radix-ui/react-toggle-group,vaul, directws, or any Yjs package;@types/wsis unused.@radix-ui/react-collapsible, hover-card, and toast are only referenced by otherwise unused wrapper files.@types/jszipis redundant with modern JSZip's bundled types.room-actions.tsimportsnanoidbut does not use it and does not declare it; the import happens to resolve only because a vulnerable Nano ID 3.x is hoisted transitively through PostCSS (actions/room-actions.ts:6-9). Remove the import or declare a current direct dependency if genuinely needed. -
Reduce dependency drift deliberately rather than bulk-upgrading.
npm outdatedreports many stale packages, notably Supabase JS 2.93.3 → 2.112.3, Supabase SSR 0.8.0 → 0.12.4, Anthropic SDK 0.71.2 → 0.117.1, Next 16.1.6 → 16.3.1, and React 19.0.0 → 19.2.8. Upgrade security/framework/auth packages first with focused tests; major upgrades such as Tailwind 3 → 4, ESLint 9 → 10, Xterm 5 → 6, and Framer Motion 12 → 13 require explicit migration work. -
Make builds hermetic.
app/layout.tsxuses twonext/font/googlefamilies despite bundling local Geist fonts. A clean production build fails whenfonts.googleapis.comis unavailable and succeeds when network access is granted. Self-host the required font files or explicitly guarantee/cache build-time network access. -
Clean up misleading debug and product copy. Production code contains many
console.log/console.debugcalls for socket IDs, room IDs, database realtime payloads, and terminal process details. The terminal labels an unauthenticated connection “Secure Link”; the panel displays hard-coded “Database connected”/“Workspace hydrated successfully”; README claims Yjs, multi-cursor presence, zero latency, and extreme robustness that the implementation does not provide. Logs should be structured and redacted, and product documentation should match deployed behavior.
- Disable network access to the custom Socket.IO/PTY server; rotate any secrets that may have been exposed through it.
- Establish one canonical Supabase migration state, test every RLS policy as each role, and close self-join/storage/profile leaks.
- Upgrade vulnerable Next/Socket.IO/WebSocket dependencies and protect all Server Actions, especially AI.
- Choose and implement the production collaboration architecture (authenticated Yjs service plus persistence/rate limits), then align production start/deployment scripts.
- Fix data-loss/state bugs and broken room/invite/chat/AI flows.
- Add CI, tests, framework-native failure states, observability, and dependency maintenance.