MessageLogger: fix "silent edit/delete" bypass (#4377)

It is possible to replace a message in place by creating a new message with the same nonce as an existing one.
This is not considered an edit since it's a new message. Thus it bypasses our edit logging and can be used to "delete" a message by replacing it with an empty one.
This fixes that bypass
This commit is contained in:
V 2026-07-15 09:37:22 +02:00 • committed by GitHub
parent e51821c740
commit b8fbed9287
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -30,7 +30,7 @@ import { classes } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types";
import { Message, MessageAttachment } from "@vencord/discord-types";
import { findCssClassesLazy } from "@webpack";
import { ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
import { AuthenticationStore, ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
import overlayStyle from "./deleteStyleOverlay.css?managed";
import textStyle from "./deleteStyleText.css?managed";
@ -338,6 +338,24 @@ export default definePlugin({
}
},
// It is possible to replace a message in place by creating a new message with the same nonce as an existing one.
// This is not considered an edit since it's a new message. Thus it bypasses our edit logging and can be used to "delete" a message by replacing it with an empty one.
// This fixes that bypass
normalizeNonce(msg: Message) {
try {
if (!msg.nonce || msg.author.id === AuthenticationStore.getId()) return;
const prevMsg = MessageStore.getMessage(msg.channel_id, msg.nonce);
if (!prevMsg || prevMsg.state !== "SENT") return;
if (prevMsg.id !== msg.id) {
delete msg.nonce;
}
} catch (e) {
console.error("[MessageLogger] Error normalizing nonce");
}
},
EditMarker({ message, className, children, ...props }: any) {
return (
<span
@ -555,6 +573,14 @@ export default definePlugin({
},
],
predicate: () => settings.store.collapseDeleted
},
{
find: "this.truncateTop",
replacement: {
match: /receiveMessage\((\i)\)\{/,
replace: "$& $self.normalizeNonce($1);"
}
}
]
});