🔏 fix: Strip Unnecessary Fields Across Write Paths in Conversation & Message Methods (#12498)

* fix: Exclude field from conversation and message updates

* fix: Remove tenantId from conversation and message update objects to prevent unintended data exposure.
* refactor: Adjust update logic in createConversationMethods and createMessageMethods to ensure tenantId is not included in the updates, maintaining data integrity.

* fix: Strip tenantId from all write paths in conversation and message methods

Extends the existing tenantId stripping to bulkSaveConvos, bulkSaveMessages,
recordMessage, and updateMessage — all of which previously passed caller-supplied
tenantId straight through to the update document. Renames discard alias from _t
to _tenantId for clarity. Adds regression tests for all six write paths.

* fix: Eliminate double-copy overhead and strengthen test assertions

Replace destructure-then-spread with spread-once-then-delete for saveConvo,
saveMessage, and recordMessage — removes one O(n) copy per call on hot paths.
Add missing not-null and positive data assertions to tenantId stripping tests.

* test: Add positive data assertions to bulkSaveMessages and recordMessage tests
This commit is contained in:
Danny Avila 2026-04-01 11:16:39 -04:00 committed by GitHub
parent 419613fdaf
commit 5e789f589f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 136 additions and 18 deletions

View file

@ -90,6 +90,7 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
user: userId,
messageId: params.newMessageId || params.messageId,
};
delete update.tenantId;
if (isTemporary) {
try {
@ -158,14 +159,17 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
) {
try {
const Message = mongoose.models.Message as Model<IMessage>;
const bulkOps = messages.map((message) => ({
updateOne: {
filter: { messageId: message.messageId },
update: message,
timestamps: !overrideTimestamp,
upsert: true,
},
}));
const bulkOps = messages.map((message) => {
const { tenantId: _tenantId, ...messageWithoutTenant } = message;
return {
updateOne: {
filter: { messageId: messageWithoutTenant.messageId },
update: messageWithoutTenant,
timestamps: !overrideTimestamp,
upsert: true,
},
};
});
const result = await tenantSafeBulkWrite(Message, bulkOps);
return result;
} catch (err) {
@ -194,7 +198,7 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
}) {
try {
const Message = mongoose.models.Message as Model<IMessage>;
const message = {
const message: Record<string, unknown> = {
user,
endpoint,
messageId,
@ -202,6 +206,7 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
parentMessageId,
...rest,
};
delete message.tenantId;
return await Message.findOneAndUpdate({ user, messageId }, message, {
upsert: true,
@ -239,7 +244,7 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
) {
try {
const Message = mongoose.models.Message as Model<IMessage>;
const { messageId, ...update } = message;
const { messageId, tenantId: _tenantId, ...update } = message;
const updatedMessage = await Message.findOneAndUpdate({ messageId, user: userId }, update, {
new: true,
});