feat: migrate opencode storage from JSON files to SQLite#168
Conversation
There was a problem hiding this comment.
🟠 [B42-QJU] Missing sessionId validation in CLI delete operation (src/index.ts:1289) (high confidence)
sessionId from CLI is passed directly to deleteOpencodeSession without validation or sanitization. Unlike claude-code sessions (line 1416 in router.ts), no input filtering is applied, allowing arbitrary session IDs to be targeted.
🟠 [X6S-LXT] Path traversal via unchecked homeDir parameter (src/sessions/agents/opencode-storage.ts:27) (high confidence)
homeDir parameter is used in path construction without validation, allowing potential access to arbitrary database files via path traversal (e.g., '../../malicious').
🟠 [9A5-YKA] Unsafe type assertion after JSON parse (src/sessions/agents/opencode-storage.ts:153) (high confidence)
safeParse uses 'as T' without validation. Malformed database content could cause runtime errors when code assumes properties exist.
🚨 [E3B-3X3] SQL Injection vulnerability in DELETE operation (src/sessions/agents/opencode-storage.ts:135) (high confidence)
The DELETE query uses string interpolation without parameterization. While sessionId comes from function params, lack of consistent parameterization creates injection risk.
🟠 [LZH-RE9] Silent error handling masks database failures (src/sessions/agents/opencode-storage.ts:71) (high confidence)
Empty catch blocks in listOpencodeSessions and getOpencodeSessionMessages return empty results without logging, making database errors invisible and hard to debug.
🟠 [ZM9-GJK] Silent error handling masks database failures (src/sessions/agents/opencode-storage.ts:124) (high confidence)
Empty catch blocks in listOpencodeSessions and getOpencodeSessionMessages return empty results without logging, making database errors invisible and hard to debug.
🟠 [3WH-V8X] Database connection leak on error (src/sessions/agents/opencode-storage.ts:32) (high confidence)
If withDb callback throws before queries execute, database connection may not close properly. While finally block exists, exceptions during db.close() could mask original error.
🟠 [NTL-86N] Stale hardcoded OpenCode storage paths in parser.ts (src/shared/constants.ts:456) (high confidence)
parser.ts lines 456 and 519 still reference old path '.local/share/opencode/storage' while constants.ts was updated to '.local/share/opencode'. These appear to be unused dead code functions, but if called will fail to find sessions.
🟠 [X97-FNX] DELETE operation does not cascade to child tables (src/worker/session-index.ts:135) (high confidence)
deleteOpencodeSession only deletes from the session table but leaves orphaned records in message and part tables. Without CASCADE constraints or explicit cleanup, deleted sessions leak data.
🟠 [HV3-3RR] Potential orphaned data from incomplete cascade deletion (src/worker/session-index.ts:135) (medium confidence)
DELETE FROM session WHERE id = ? only deletes the session row but doesn't explicitly delete related messages and parts. If DB lacks CASCADE constraints, orphaned data remains.
Identified by Warden via find-bugs
There was a problem hiding this comment.
🚨 [SV3-H7D] Command injection in searchHostSessions query parameter (src/agent/router.ts:1289) (high confidence)
The safeQuery sanitization at line 1289 only escapes quotes and backslashes but misses shell metacharacters like backticks, $(), |, &, ;. An attacker could inject commands via the search query.
🟠 [5ZM-7PC] OpenCode sessions no longer searchable after SQLite migration (src/agent/router.ts:1290) (high confidence)
Removed OpenCode storage path from searchPaths but didn't add SQL-based search. OpenCode is still a valid agentType in return signature but cannot be found by search.
src/agent/router.ts:1335) (high confidence)
The search functionality for OpenCode sessions was removed when migrating from JSON files to SQLite. The searchHostSessions function no longer includes the opencode path, and no SQLite-based search was added to replace it. Users cannot search OpenCode session content.
🟠 [B8L-D9H] Potential orphaned database records on session deletion (src/worker/session-index.ts:135) (medium confidence)
DELETE only removes session record, not related message/part records. May leak data unless schema has CASCADE DELETE.
src/worker/session-index.ts:456) (high confidence)
listOpenCodeSessions/getOpenCodeSessionDetail read from .local/share/opencode/storage/* (old path) instead of SQLite. Called by listAllSessions used in mobile app. Will return empty/stale data.
Identified by Warden via find-bugs
OpenCode 1.2 replaced flat JSON file storage with a SQLite database. Rewrites the worker's opencode-storage module to query the SQLite DB directly using bun:sqlite instead of walking JSON files on disk.
OpenCode sessions are now in SQLite, not flat files — ripgrep can't search them. Remove the dead opencode search paths and result parsing from both the container and host search functions.
The server integration test was creating JSON files for the old storage format. Uses bun -e inside the container to create a SQLite DB with test session data instead.
4fa2ec9 to
525671b
Compare
There was a problem hiding this comment.
🚨 [GYB-538] Command Injection in searchHostSessions via insufficient input sanitization (src/agent/router.ts:1309) (high confidence)
User-controlled query string is inserted into shell command with inadequate escaping. Only quotes and backslashes are escaped, but shell metacharacters like $, `, ;, |, & remain unescaped, allowing arbitrary command execution via execSync.
Identified by Warden via security-review
SQLite disables foreign key constraints by default. Without PRAGMA foreign_keys = ON, deleting a session leaves orphaned message and part rows.
|
|
||
| const partRows = db | ||
| .query<{ message_id: string; data: string }, [string]>( | ||
| `SELECT message_id, data FROM part WHERE session_id = ? ORDER BY message_id, id` |
There was a problem hiding this comment.
Bug: The SQL query in getOpencodeSessionMessages assumes a session_id column exists in the part table, which likely mismatches the actual OpenCode database schema, causing message loading to fail.
Severity: CRITICAL
Suggested Fix
The query for parts needs to be corrected. Instead of filtering the part table by session_id, first query all messages for the given sessionId from the message table. Then, use the collected message_id values to fetch the corresponding parts from the part table. This may require a JOIN or a second query using WHERE message_id IN (...). Additionally, the test suite should be updated to create and seed the part table to ensure this functionality is properly tested.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: src/sessions/agents/opencode-storage.ts#L90
Potential issue: The function `getOpencodeSessionMessages` queries a `part` table using
a `session_id` column to retrieve message parts. However, evidence from the previous
file-based storage structure and the lack of a `part` table in the test setup strongly
suggest the actual OpenCode 1.2 database schema does not contain a `session_id` column
in the `part` table, or that the table is structured differently. This mismatch will
cause the SQL query to fail with either a "no such table: part" or "no such column:
session_id" error, preventing any OpenCode session messages from being loaded.
There was a problem hiding this comment.
src/agent/router.ts:1310) (high confidence)
User input is passed to execSync with only quote/backslash escaping. Shell metacharacters like $() ` ; & | can execute arbitrary commands.
Identified by Warden via security-review
Summary
storage/{session,message,part}/*.json) with a single SQLite database (opencode.db). Rewrites the worker's opencode-storage module to query SQLite viabun:sqliteinstead of walking JSON files on disk.awaits from callers.