{"record":{"id":"ad5d33f6055c5493","repo":"ruvnet/ruflo","slug":"roomid-exceeds-128-chars-agentbbs-federation","errorCode":null,"errorMessage":"roomId exceeds 128 chars","messagePattern":"roomId exceeds 128 chars","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts","lineNumber":318,"sourceCode":"  if (next.length === peers.length) return false;\n  writePeers(basePath, next);\n  return true;\n}\n\nexport function readEnvelopes(basePath: string, roomId: string): SignedEnvelope[] {\n  const p = roomLogPath(basePath, roomId);\n  if (!existsSync(p)) return [];\n  const out: SignedEnvelope[] = [];\n  for (const line of readFileSync(p, 'utf-8').split(/\\r?\\n/)) {\n    if (!line.trim()) continue;\n    try { out.push(JSON.parse(line)); } catch { /* skip malformed */ }\n  }\n  return out;\n}\n\nexport function validateRoomId(roomId: string): string {\n  if (!roomId || typeof roomId !== 'string') throw new Error('roomId is required');\n  if (roomId.length > 128) throw new Error('roomId exceeds 128 chars');\n  // Also blocks path traversal: `.` is allowed but `/` segments cannot form\n  // `..` without tripping the explicit check below.\n  if (!ROOM_ID_RE.test(roomId)) throw new Error('roomId has invalid characters');\n  if (roomId.includes('..')) throw new Error('roomId must not contain ..');\n  return roomId;\n}\n\nexport interface MergeResult {\n  merged: number;\n  skippedDuplicate: number;\n  skippedUnverified: number;\n  skippedOversize: number;\n  skippedHopLimit: number;\n}\n\n/**\n * Union-merge verified envelopes from a peer into the local room log.\n *","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/ruvnet/ruflo/blob/2602b642d92234c710ffbe96bfb33007d481ceab/v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts#L300-L336","documentation":"validateRoomId() caps room identifiers at 128 characters to keep file names and index keys bounded, since roomIds map onto storage paths. A longer roomId is rejected before any filesystem work. Shorten the identifier or use a hash if you need long logical names.","triggerScenarios":"Calling mergeEnvelopes, syncRoomFromPeer, or server handlers with a roomId string whose .length > 128 — e.g. a room id derived from concatenating user IDs, a UUID plus long suffixes, or an unbounded user-supplied room name.","commonSituations":"Generating room ids from untrusted input without length clamping; a remote peer sending envelopes with oversized roomIds (possibly probing storage); schema drift where one system uses full URLs as roomIds.","solutions":["Truncate or hash long room names to <=128 chars before use, e.g. crypto.createHash('sha256').update(name).digest('hex').","Enforce a maxLength=128 on the room id field at API/UI input time.","Reject or quarantine oversized-roomId envelopes from peers instead of passing them to sync.","If a legacy system produced long ids, migrate stored rooms to hashed ids and update all producers."],"exampleFix":"// before\nconst roomId = `room-${tenant}-${user}-${session}-${payload}`; // >128 chars\n// after\nconst raw = `room-${tenant}-${user}-${session}`;\nconst roomId = raw.length > 128 ? crypto.createHash('sha256').update(raw).digest('hex') : raw;","handlingStrategy":"validation","validationCode":"if (typeof roomId === 'string' && roomId.length > 128) throw new Error('roomId exceeds 128 chars');","typeGuard":null,"tryCatchPattern":"try {\n  validateRoomId(roomId);\n} catch (e) {\n  if (/exceeds 128 chars/.test(e.message)) {\n    roomId = crypto.createHash('sha256').update(roomId).digest('hex');\n  } else throw e;\n}","preventionTips":["Cap roomId length at input time (UI maxLength, schema maxLength)","Prefer fixed-length ids (UUID or sha256 hex) over concatenated free-form names","Never derive roomIds from unbounded user input without clamping","Validate incoming peer envelopes' roomId length before syncing"],"tags":["validation","length-limit","room-id","input-validation"],"backgroundTag":"value-out-of-range","analyzedSha":"2602b642d92234c710ffbe96bfb33007d481ceab","analyzedAt":"2026-09-15T22:58:14.805Z","contentChangedAt":"2026-09-15T22:58:14.805Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}