fix(electrobun): complete all 16 Codex #3 findings

CRITICAL:
- Message persistence race: snapshot batchEnd before async save
- Double-start guard: startingProjects Set prevents concurrent launches
- Symlink path traversal: fs.realpathSync() in path-guard.ts
- Relay false success: connect() returns { ok, machineId, error }

HIGH:
- Session restore skips if active session exists
- Remote remove: new RPC, cleans backend map
- Task board poll token: stale responses discarded after drag-drop
- Health concurrent tools: toolsInFlight counter (was boolean)
- bttask transactions: delete wraps comments+task, addComment validates
- PTY buffer cleared on reconnect
- PTY large paste: chunked String.fromCharCode (8KB chunks)
- Sidecar max line: 10MB limit prevents unbounded memory
- btmsg authorization: group validation, channel membership checks

MEDIUM:
- Session retention: max 5 per project, purgeSession/untrackProject
- Relay IPv6: URL parser replaces string split
- PTY schema: fixed misleading base64 comment
This commit is contained in:
Hibryda 2026-03-22 02:52:04 +01:00
parent c145e37316
commit 0f75cb8e32
12 changed files with 190 additions and 42 deletions

View file

@ -134,6 +134,8 @@ function findNodeRuntime(): string {
// ── Cleanup grace period ─────────────────────────────────────────────────────
const CLEANUP_GRACE_MS = 60_000; // 60s after done/error before removing session
// Fix #12 (Codex audit): Max NDJSON line size — prevent OOM on malformed output
const MAX_LINE_SIZE = 10 * 1024 * 1024; // 10 MB
// ── SidecarManager ───────────────────────────────────────────────────────────
@ -369,6 +371,13 @@ export class SidecarManager {
for await (const chunk of reader) {
buffer += decoder.decode(chunk, { stream: true });
// Fix #12 (Codex audit): Guard against unbounded buffer growth
if (buffer.length > MAX_LINE_SIZE && !buffer.includes("\n")) {
console.error(`[sidecar] Buffer exceeded ${MAX_LINE_SIZE} bytes without newline for ${sessionId}, truncating`);
buffer = "";
continue;
}
let newlineIdx: number;
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, newlineIdx).trim();
@ -379,7 +388,7 @@ export class SidecarManager {
}
}
// Fix #12: Parse any residual data left in the buffer after stream ends
// Parse any residual data left in the buffer after stream ends
const residual = buffer.trim();
if (residual) {
this.handleNdjsonLine(sessionId, session, residual);