A reply delivered before its parent message renders as an orphaned response with no visible context. Users see Re: [message not found] or a floating reply with no thread anchor, which destroys comprehension and trust in the threading system. CWE-362 (Concurrent Execution Using Shared Resource with Improper Synchronization) applies when two related writes (parent then child) can arrive out of order due to race conditions in asynchronous handlers. Threading is a key value-add for community platforms; broken ordering negates that value.
Low because parent-before-reply ordering failures corrupt thread readability, but they surface only under concurrent send conditions rather than on every message.
Validate that the parent message exists server-side before accepting a reply. Reject the reply with an error if the parent is not found.
socket.on('send_reply', async (data: { parentId: string; content: string; channel: string }) => {
const parent = await db.query.messages.findFirst({
where: eq(messages.id, data.parentId),
});
if (!parent) {
socket.emit('error', { code: 422, message: 'Parent message not found' });
return;
}
const reply = {
id: nanoid(),
parentId: data.parentId,
content: data.content,
userId: socket.userId,
seq: await nextSeq(data.channel),
timestamp: Date.now(),
};
await db.insert(messages).values(reply);
io.to(data.channel).emit('message', reply);
});
ID: community-realtime.realtime-ux.causal-ordering
Severity: low
What to look for: Enumerate all threading or reply mechanisms. For each, check that the server enforces parent-before-child ordering. Count the message types that support threading: direct replies, nested threads, quoted messages.
Pass criteria: Messages maintain causal order — a reply is never rendered before its parent message. The server must validate that the parent message exists before accepting a reply. Fewer than 1 orphaned reply should be possible under normal operation.
Fail criteria: Replies can be sent or received before their parent message, creating orphaned replies.
Skip (N/A) when: The platform does not implement threaded messages or replies.
Detail on fail: "Reply to message ID 5 was emitted before message 5 itself. Reply appears orphaned in the UI."
Remediation: Enforce causal ordering for threaded messages:
interface Message {
id: string;
parentId?: string; // null for top-level, message ID for replies
content: string;
seq: number;
}
// When emitting a reply, ensure parent is in the client cache
socket.on('send_reply', (data: { parentId: string; content: string }) => {
const parent = messageCache.get(data.parentId);
if (!parent) {
socket.emit('error', { message: 'Parent message not found' });
return;
}
const reply: Message = {
id: nanoid(),
parentId: data.parentId,
content: data.content,
seq: incrementSeq(),
};
io.emit('message', reply);
});