Skip to content
Open
31 changes: 31 additions & 0 deletions apps/meteor/app/api/server/v1/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,22 @@ const chatEndpoints = API.v1
return API.v1.failure(`No message found with the id of "${bodyParams.msgId}".`);
}

// Prevent editing of soft-deleted messages (Zombie Edits fix)
if (msg.t === 'rm') {
return API.v1.failure('Cannot edit a deleted message.');
}

// --- Fix : Strict Input Sanitization for updates ---
if ('text' in bodyParams && typeof bodyParams.text === 'string') {
const sanitizedText = bodyParams.text.replace(/\u0000/g, '').trim();
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated

if (sanitizedText.length === 0) {
return API.v1.failure('Message cannot be empty or contain only invisible control characters.');
}

bodyParams.text = bodyParams.text.replace(/\u0000/g, '');
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (bodyParams.roomId !== msg.rid) {
return API.v1.failure('The room id provided does not match where the message is from.');
}
Expand Down Expand Up @@ -837,6 +853,21 @@ const chatEndpoints = API.v1
throw new Error("Cannot send system messages using 'chat.sendMessage'");
}

// --- Fix : Strict Input Sanitization (Invisible Character Bypass) ---
const msgPayload = this.bodyParams.message as { msg?: string; attachments?: any[] };
if (msgPayload && typeof msgPayload.msg === 'string') {
// Strip null bytes and trim
const sanitizedMsg = msgPayload.msg.replace(/\u0000/g, '').trim();

// If the message is completely empty after stripping, and has no attachments, reject it
if (sanitizedMsg.length === 0 && (!msgPayload.attachments || msgPayload.attachments.length === 0)) {
return API.v1.failure('Message cannot be empty or contain only invisible control characters.');
}

// Clean the actual payload sent to the database
msgPayload.msg = msgPayload.msg.replace(/\u0000/g, '');
}

const sent = await applyAirGappedRestrictionsValidation(() =>
executeSendMessage(this.user, this.bodyParams.message as Pick<IMessage, 'rid'>, { previewUrls: this.bodyParams.previewUrls }),
);
Expand Down