From f086fb2bc65f1031542d9bfae231a31ffeaa8a43 Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 17:16:18 +0200 Subject: (Webhook Detection): Created cog. --- bot/cogs/webhook_remover.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 bot/cogs/webhook_remover.py diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py new file mode 100644 index 000000000..982359410 --- /dev/null +++ b/bot/cogs/webhook_remover.py @@ -0,0 +1,15 @@ +from discord.ext.commands import Cog + +from bot.bot import Bot + + +class WebhookRemover(Cog): + """Scan messages to detect Discord webhooks links.""" + + def __init__(self, bot: Bot): + self.bot = bot + + +def setup(bot: Bot) -> None: + """Load `WebhookRemover` cog.""" + bot.add_cog(WebhookRemover(bot)) -- cgit v1.2.3 From f4b5718225505b2b78e4cbf75c5599ab307455d2 Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 17:19:40 +0200 Subject: (Webhook Detection): Added webhook match regex. --- bot/cogs/webhook_remover.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index 982359410..49cf94de7 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -1,7 +1,11 @@ +import re + from discord.ext.commands import Cog from bot.bot import Bot +WEBHOOK_URL_RE = re.compile(r"discordapp\.com/api/webhooks/\d+/\S+/?") + class WebhookRemover(Cog): """Scan messages to detect Discord webhooks links.""" -- cgit v1.2.3 From e5c41faf826e4a29fd21986fc828034372b18863 Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 17:38:11 +0200 Subject: (Webhook Detection): Added cog loading to __main__.py, created `scan_message` helper function to detect Webhook URL. --- bot/__main__.py | 1 + bot/cogs/webhook_remover.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/bot/__main__.py b/bot/__main__.py index 3df477a6d..9e8b1bdce 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -64,6 +64,7 @@ bot.load_extension("bot.cogs.token_remover") bot.load_extension("bot.cogs.utils") bot.load_extension("bot.cogs.watchchannels") bot.load_extension("bot.cogs.wolfram") +bot.load_extension("bot.cogs.webhook_remover") # Apply `message_edited_at` patch if discord.py did not yet release a bug fix. if not hasattr(discord.message.Message, '_handle_edited_timestamp'): diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index 49cf94de7..a3025f19f 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -1,5 +1,6 @@ import re +from discord import Message from discord.ext.commands import Cog from bot.bot import Bot @@ -13,6 +14,14 @@ class WebhookRemover(Cog): def __init__(self, bot: Bot): self.bot = bot + async def scan_message(self, msg: Message) -> bool: + """Scan message content to detect Webhook URLs. Return `bool` about does this have webhook URL.""" + matches = WEBHOOK_URL_RE.search(msg.content) + if matches: + return True + else: + return False + def setup(bot: Bot) -> None: """Load `WebhookRemover` cog.""" -- cgit v1.2.3 From 7e34c5e62eeefe1f0b8a1bb7e03435b5d2998712 Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 17:41:35 +0200 Subject: (Webhook Detection): Added `ModLog` fetching property. --- bot/cogs/webhook_remover.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index a3025f19f..54222b007 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -4,6 +4,7 @@ from discord import Message from discord.ext.commands import Cog from bot.bot import Bot +from bot.cogs.moderation.modlog import ModLog WEBHOOK_URL_RE = re.compile(r"discordapp\.com/api/webhooks/\d+/\S+/?") @@ -14,6 +15,11 @@ class WebhookRemover(Cog): def __init__(self, bot: Bot): self.bot = bot + @property + def mod_log(self) -> ModLog: + """Get current instance of `ModLog`.""" + return self.bot.get_cog("ModLog") + async def scan_message(self, msg: Message) -> bool: """Scan message content to detect Webhook URLs. Return `bool` about does this have webhook URL.""" matches = WEBHOOK_URL_RE.search(msg.content) -- cgit v1.2.3 From 3a9494da375a7aedf5b2c8554ae1cdd0170ba7f1 Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 18:03:50 +0200 Subject: (Webhook Detection): Created `delete_and_respond` helper function to handle Webhook URLs. --- bot/cogs/webhook_remover.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index 54222b007..a19f9c196 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -1,13 +1,24 @@ +import logging import re -from discord import Message +from discord import Colour, Message from discord.ext.commands import Cog from bot.bot import Bot from bot.cogs.moderation.modlog import ModLog +from bot.constants import Channels, Colours, Event, Icons WEBHOOK_URL_RE = re.compile(r"discordapp\.com/api/webhooks/\d+/\S+/?") +ALERT_MESSAGE_TEMPLATE = ( + "{user}, looks like you posted Discord Webhook URL to chat. " + "I removed this, but we **strongly** suggest to change this now " + "to prevent any spam abuse to channel. Please avoid doing this in future. " + "If you believe this was mistake, please let us know." +) + +log = logging.getLogger(__name__) + class WebhookRemover(Cog): """Scan messages to detect Discord webhooks links.""" @@ -21,13 +32,41 @@ class WebhookRemover(Cog): return self.bot.get_cog("ModLog") async def scan_message(self, msg: Message) -> bool: - """Scan message content to detect Webhook URLs. Return `bool` about does this have webhook URL.""" + """Scan message content to detect Webhook URLs. Return `bool` about does this have Discord webhook URL.""" matches = WEBHOOK_URL_RE.search(msg.content) if matches: return True else: return False + async def delete_and_respond(self, msg: Message, url: str) -> None: + """Delete message and show warning when message contains Discord Webhook URL.""" + # Create URL that will be sent to logs, remove token + parts = url.split("/") + parts[-1] = "xxx" + url = "/".join(parts) + + # Don't log this, due internal delete, not by user. Will make different entry. + self.mod_log.ignore(Event.message_delete, msg.id) + await msg.delete() + await msg.channel.send(ALERT_MESSAGE_TEMPLATE.format(user=msg.author.mention)) + + message = ( + f"{msg.author} ({msg.author.id}) posted Discord Webhook URL " + f"to {msg.channel}. Webhook URL was {url}" + ) + log.debug(message) + + # Send entry to moderation alerts. + await self.mod_log.send_log_message( + icon_url=Icons.token_removed, + colour=Colour(Colours.soft_red), + title="Discord Webhook URL removed!", + text=message, + thumbnail=msg.author.avatar_url_as(static_format="png"), + channel_id=Channels.mod_alerts + ) + def setup(bot: Bot) -> None: """Load `WebhookRemover` cog.""" -- cgit v1.2.3 From 3482471cd013bfc0102cc3b80c71e04dfc30349c Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 18:05:26 +0200 Subject: (Webhook Detection): Added URL returning to `scan_message` helper function. --- bot/cogs/webhook_remover.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index a19f9c196..d0d604bc7 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -1,5 +1,6 @@ import logging import re +import typing as t from discord import Colour, Message from discord.ext.commands import Cog @@ -31,13 +32,13 @@ class WebhookRemover(Cog): """Get current instance of `ModLog`.""" return self.bot.get_cog("ModLog") - async def scan_message(self, msg: Message) -> bool: + async def scan_message(self, msg: Message) -> t.Tuple[bool, t.Optional[str]]: """Scan message content to detect Webhook URLs. Return `bool` about does this have Discord webhook URL.""" matches = WEBHOOK_URL_RE.search(msg.content) if matches: - return True + return True, matches[0] else: - return False + return False, None async def delete_and_respond(self, msg: Message, url: str) -> None: """Delete message and show warning when message contains Discord Webhook URL.""" -- cgit v1.2.3 From 27efaf8414ec0211c0c1b3bba4b16a969eb01c0b Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 18:10:27 +0200 Subject: (Webhook Detection): Alert message formatting changes, added `on_message` listener. --- bot/cogs/webhook_remover.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index d0d604bc7..d6569a72b 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -53,8 +53,8 @@ class WebhookRemover(Cog): await msg.channel.send(ALERT_MESSAGE_TEMPLATE.format(user=msg.author.mention)) message = ( - f"{msg.author} ({msg.author.id}) posted Discord Webhook URL " - f"to {msg.channel}. Webhook URL was {url}" + f"{msg.author} (`{msg.author.id}`) posted Discord Webhook URL " + f"to #{msg.channel}. Webhook URL was `{url}`" ) log.debug(message) @@ -68,6 +68,13 @@ class WebhookRemover(Cog): channel_id=Channels.mod_alerts ) + @Cog.listener() + async def on_message(self, msg: Message) -> None: + """Check is Discord Webhook URL in sent message.""" + is_url_in, url = await self.scan_message(msg) + if is_url_in: + await self.delete_and_respond(msg, url) + def setup(bot: Bot) -> None: """Load `WebhookRemover` cog.""" -- cgit v1.2.3 From 3f855231a3da94efe0e73448feaeb8f15d2799fc Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 18:57:03 +0200 Subject: (Webhook Detection): Added `on_message_edit` listener for Discord Webhooks detecting. --- bot/cogs/webhook_remover.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index d6569a72b..1f758f8e6 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -75,6 +75,13 @@ class WebhookRemover(Cog): if is_url_in: await self.delete_and_respond(msg, url) + @Cog.listener() + async def on_message_edit(self, before: Message, after: Message) -> None: + """Check is Discord Webhook URL in new message content when message changed.""" + is_url_in, url = await self.scan_message(after) + if is_url_in: + await self.delete_and_respond(after, url) + def setup(bot: Bot) -> None: """Load `WebhookRemover` cog.""" -- cgit v1.2.3 From b2b9353c8b775ffa687b8bafc875786815b173ce Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 19:28:38 +0200 Subject: (Webhook Detection): Fixed order of cog loading. --- bot/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/__main__.py b/bot/__main__.py index 9e8b1bdce..8c3ae02e3 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -63,8 +63,8 @@ bot.load_extension("bot.cogs.tags") bot.load_extension("bot.cogs.token_remover") bot.load_extension("bot.cogs.utils") bot.load_extension("bot.cogs.watchchannels") -bot.load_extension("bot.cogs.wolfram") bot.load_extension("bot.cogs.webhook_remover") +bot.load_extension("bot.cogs.wolfram") # Apply `message_edited_at` patch if discord.py did not yet release a bug fix. if not hasattr(discord.message.Message, '_handle_edited_timestamp'): -- cgit v1.2.3 From 6a410521025299f0e1795cc9c6d756ff48caf20d Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 19:31:58 +0200 Subject: (Webhook Detection): Call `on_message` instead repeating code. --- bot/cogs/webhook_remover.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index 1f758f8e6..5fb676045 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -78,9 +78,7 @@ class WebhookRemover(Cog): @Cog.listener() async def on_message_edit(self, before: Message, after: Message) -> None: """Check is Discord Webhook URL in new message content when message changed.""" - is_url_in, url = await self.scan_message(after) - if is_url_in: - await self.delete_and_respond(after, url) + await self.on_message(after) def setup(bot: Bot) -> None: -- cgit v1.2.3 From 2544670fa38cea1f53147307b6b1e1134265a74f Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 19:42:31 +0200 Subject: (Webhook Detection): Added grouping to RegEx compilation, removed unnecessary function `scan_message`, moved this content to `on_message` event. --- bot/cogs/webhook_remover.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index 5fb676045..afa88ce89 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -1,6 +1,5 @@ import logging import re -import typing as t from discord import Colour, Message from discord.ext.commands import Cog @@ -9,7 +8,7 @@ from bot.bot import Bot from bot.cogs.moderation.modlog import ModLog from bot.constants import Channels, Colours, Event, Icons -WEBHOOK_URL_RE = re.compile(r"discordapp\.com/api/webhooks/\d+/\S+/?") +WEBHOOK_URL_RE = re.compile(r"(discordapp\.com/api/webhooks/)(\d+/)(\S+/?)") ALERT_MESSAGE_TEMPLATE = ( "{user}, looks like you posted Discord Webhook URL to chat. " @@ -32,14 +31,6 @@ class WebhookRemover(Cog): """Get current instance of `ModLog`.""" return self.bot.get_cog("ModLog") - async def scan_message(self, msg: Message) -> t.Tuple[bool, t.Optional[str]]: - """Scan message content to detect Webhook URLs. Return `bool` about does this have Discord webhook URL.""" - matches = WEBHOOK_URL_RE.search(msg.content) - if matches: - return True, matches[0] - else: - return False, None - async def delete_and_respond(self, msg: Message, url: str) -> None: """Delete message and show warning when message contains Discord Webhook URL.""" # Create URL that will be sent to logs, remove token @@ -71,9 +62,9 @@ class WebhookRemover(Cog): @Cog.listener() async def on_message(self, msg: Message) -> None: """Check is Discord Webhook URL in sent message.""" - is_url_in, url = await self.scan_message(msg) - if is_url_in: - await self.delete_and_respond(msg, url) + matches = WEBHOOK_URL_RE.search(msg.content) + if matches: + await self.delete_and_respond(msg, "".join(matches.groups()[:-1]) + "xxx") @Cog.listener() async def on_message_edit(self, before: Message, after: Message) -> None: -- cgit v1.2.3 From 3e342882a927298cea919c33678cd39c4a71c67e Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 19:44:25 +0200 Subject: (Webhook Detection): Removed unnecessary URL hiding in `delete_and_respond`. --- bot/cogs/webhook_remover.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index afa88ce89..9f6243b3c 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -33,11 +33,6 @@ class WebhookRemover(Cog): async def delete_and_respond(self, msg: Message, url: str) -> None: """Delete message and show warning when message contains Discord Webhook URL.""" - # Create URL that will be sent to logs, remove token - parts = url.split("/") - parts[-1] = "xxx" - url = "/".join(parts) - # Don't log this, due internal delete, not by user. Will make different entry. self.mod_log.ignore(Event.message_delete, msg.id) await msg.delete() -- cgit v1.2.3 From 2532c55239a1563b34ed475bffa330e1670de6e0 Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 19:45:51 +0200 Subject: (Webhook Detection): Fixed docstrings. --- bot/cogs/webhook_remover.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index 9f6243b3c..cbece321d 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -56,14 +56,14 @@ class WebhookRemover(Cog): @Cog.listener() async def on_message(self, msg: Message) -> None: - """Check is Discord Webhook URL in sent message.""" + """Check if a Discord webhook URL is in `message`.""" matches = WEBHOOK_URL_RE.search(msg.content) if matches: await self.delete_and_respond(msg, "".join(matches.groups()[:-1]) + "xxx") @Cog.listener() async def on_message_edit(self, before: Message, after: Message) -> None: - """Check is Discord Webhook URL in new message content when message changed.""" + """Check if a Discord webhook URL is in the edited message `after`.""" await self.on_message(after) -- cgit v1.2.3 From bf20911cb7f9310450293e93babff9bea8a177f9 Mon Sep 17 00:00:00 2001 From: ks123 Date: Sat, 28 Mar 2020 20:24:28 +0200 Subject: (Webhook Detection): Renamed `url` variable to `redacted_url` to avoid confusion in `delete_and_respond` function. --- bot/cogs/webhook_remover.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index cbece321d..b4606eb59 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -31,7 +31,7 @@ class WebhookRemover(Cog): """Get current instance of `ModLog`.""" return self.bot.get_cog("ModLog") - async def delete_and_respond(self, msg: Message, url: str) -> None: + async def delete_and_respond(self, msg: Message, redacted_url: str) -> None: """Delete message and show warning when message contains Discord Webhook URL.""" # Don't log this, due internal delete, not by user. Will make different entry. self.mod_log.ignore(Event.message_delete, msg.id) @@ -40,7 +40,7 @@ class WebhookRemover(Cog): message = ( f"{msg.author} (`{msg.author.id}`) posted Discord Webhook URL " - f"to #{msg.channel}. Webhook URL was `{url}`" + f"to #{msg.channel}. Webhook URL was `{redacted_url}`" ) log.debug(message) -- cgit v1.2.3 From bf18e1ca460427e5a973be0b15c51e1c7b5b6e60 Mon Sep 17 00:00:00 2001 From: Karlis S <45097959+ks129@users.noreply.github.com> Date: Sat, 28 Mar 2020 22:17:07 +0200 Subject: (Webhook Detection): Fixed grouping of regex, alert message content, docstrings, string formatting and URL hiding to show in logs. Co-Authored-By: Mark --- bot/cogs/webhook_remover.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bot/cogs/webhook_remover.py b/bot/cogs/webhook_remover.py index b4606eb59..49692113d 100644 --- a/bot/cogs/webhook_remover.py +++ b/bot/cogs/webhook_remover.py @@ -8,13 +8,13 @@ from bot.bot import Bot from bot.cogs.moderation.modlog import ModLog from bot.constants import Channels, Colours, Event, Icons -WEBHOOK_URL_RE = re.compile(r"(discordapp\.com/api/webhooks/)(\d+/)(\S+/?)") +WEBHOOK_URL_RE = re.compile(r"((?:https?://)?discordapp\.com/api/webhooks/\d+/)\S+/?", re.I) ALERT_MESSAGE_TEMPLATE = ( - "{user}, looks like you posted Discord Webhook URL to chat. " - "I removed this, but we **strongly** suggest to change this now " - "to prevent any spam abuse to channel. Please avoid doing this in future. " - "If you believe this was mistake, please let us know." + "{user}, looks like you posted a Discord webhook URL. Therefore, your " + "message has been removed. Your webhook may have been **compromised** so " + "please re-create the webhook **immediately**. If you believe this was " + "mistake, please let us know." ) log = logging.getLogger(__name__) @@ -32,14 +32,14 @@ class WebhookRemover(Cog): return self.bot.get_cog("ModLog") async def delete_and_respond(self, msg: Message, redacted_url: str) -> None: - """Delete message and show warning when message contains Discord Webhook URL.""" + """Delete `msg` and send a warning that it contained the Discord webhook `redacted_url`.""" # Don't log this, due internal delete, not by user. Will make different entry. self.mod_log.ignore(Event.message_delete, msg.id) await msg.delete() await msg.channel.send(ALERT_MESSAGE_TEMPLATE.format(user=msg.author.mention)) message = ( - f"{msg.author} (`{msg.author.id}`) posted Discord Webhook URL " + f"{msg.author} (`{msg.author.id}`) posted a Discord webhook URL " f"to #{msg.channel}. Webhook URL was `{redacted_url}`" ) log.debug(message) @@ -48,7 +48,7 @@ class WebhookRemover(Cog): await self.mod_log.send_log_message( icon_url=Icons.token_removed, colour=Colour(Colours.soft_red), - title="Discord Webhook URL removed!", + title="Discord webhook URL removed!", text=message, thumbnail=msg.author.avatar_url_as(static_format="png"), channel_id=Channels.mod_alerts @@ -59,7 +59,7 @@ class WebhookRemover(Cog): """Check if a Discord webhook URL is in `message`.""" matches = WEBHOOK_URL_RE.search(msg.content) if matches: - await self.delete_and_respond(msg, "".join(matches.groups()[:-1]) + "xxx") + await self.delete_and_respond(msg, matches[1] + "xxx") @Cog.listener() async def on_message_edit(self, before: Message, after: Message) -> None: -- cgit v1.2.3