Repository: python-discord/bot

Issue:
Filter bypass in !raw command, can make the bot @mention
The `!raw` command takes a link to a message in the discord, and then shows the message contents inside a code block.

This line tries to prevent the message from breaking the code block:
https://github.com/python-discord/bot/blob/76b3b5898dd4163d8bac6ff330209f27f437e3d6/bot/exts/info/information.py#L452

But consider the message: ` ````` ` (5 backticks). The first 3 will be replaced by the filter, but that's it. Finally giving this result: ` `` ``` `, and actually breaking out of the code block, since we got 3 backticks in a row.

POC with ` ````` <@...> ``` ` to have the bot mention someone:

![image](https://user-images.githubusercontent.com/26556598/112738402-cb509900-8f73-11eb-932d-cff48737fd79.png)

` ````` @everyone ``` ` might be possible, but I didn't try 😋

I suggest changing the filter to ```.replace('`', '\\`')```, so backticks will just show up "escaped" to users (they will actually see the backslash).
Another fix is to remove the `!raw` command, idk why it exists.


Discussion:
I wouldn't replace it with a backlash, because it will look like \ inside the codeblock. The best way would be to replace it with an invisible character (`\u200b`).
There's a [discord.py utility to escape mentions](https://discordpy.readthedocs.io/en/latest/api.html#discord.utils.escape_mentions), would that work here?
I'd also add something like this to escape mentions:
https://github.com/python-discord/bot/blob/main/bot/exts/utils/snekbox.py#L153-L157

And it looks like that's exactly what the function @HassanAbouelela suggested does:
https://github.com/Rapptz/discord.py/blob/a3f700c11f72202f6a7710ce07c7144a8b8c947d/discord/utils.py#L547

I'd submit a PR but I can't test this on actual Discord so I'll leave it
If you have some time to set up the bot, we have a [contributing guide](https://pythondiscord.com/pages/contributing/bot/) that walks you through the process. You can also ask in the dev-contrib channel for help. That being said, just reporting the issue is a significant contribution, thank you.
> There's a [discord.py utility to escape mentions](https://discordpy.readthedocs.io/en/latest/api.html#discord.utils.escape_mentions), would that work here?

Wouldn't allowed mentions be better than escaping them?
Is that something we can configure per command?
[`discord.abc.Messageable.send`](https://discordpy.readthedocs.io/en/stable/api.html#discord.abc.Messageable.send) takes an `allowed_mentions` kwarg.
We already use allowed mentions after the ping incident with !raw last year, rendering this vulnerability not hugely damaging. From my testing, it appears that only individual user pings work or the roles that we have whitelisted here:

https://github.com/python-discord/bot/blob/60f410e488e8212f34c56b9353df0c97912d369c/bot/bot.py#L112

Therefore:

| Ping type | Safe | Demo |
| --------- | ---- | ---- |
| User      | :x:  | <img width="1241" alt="Screenshot 2021-03-28 at 13 01 08" src="https://user-images.githubusercontent.com/20439493/112751384-ab49c580-8fc5-11eb-8922-2b46f07d2a57.png"> |
| Role      | ⚠️ (Only moderator roles)   | <img width="1216" alt="Screenshot 2021-03-28 at 13 02 56" src="https://user-images.githubusercontent.com/20439493/112751424-eba94380-8fc5-11eb-8ff3-65d86e738ad9.png"> |
| Everyone/here | ✅ | <img width="1197" alt="Screenshot 2021-03-28 at 13 03 46" src="https://user-images.githubusercontent.com/20439493/112751447-0976a880-8fc6-11eb-874d-f5d996c284dd.png"> |

We can just add an allowed_mentions override for this specific message send to disable all forms of pings.
My idea exactly. Can I take this one?
Go for it!

Resolving patch:
diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py
--- a/bot/exts/info/information.py
+++ b/bot/exts/info/information.py
@@ -6,7 +6,7 @@
 from typing import Any, DefaultDict, Dict, Mapping, Optional, Tuple, Union
 
 import fuzzywuzzy
-from discord import Colour, Embed, Guild, Message, Role
+from discord import AllowedMentions, Colour, Embed, Guild, Message, Role
 from discord.ext.commands import BucketType, Cog, Context, Paginator, command, group, has_any_role
 
 from bot import constants
@@ -447,9 +447,9 @@ async def raw(self, ctx: Context, *, message: Message, json: bool = False) -> No
 
         def add_content(title: str, content: str) -> None:
             paginator.add_line(f'== {title} ==\n')
-            # replace backticks as it breaks out of code blocks. Spaces seemed to be the most reasonable solution.
-            # we hope it's not close to 2000
-            paginator.add_line(content.replace('```', '`` `'))
+            # Replace backticks as it breaks out of code blocks.
+            # An invisible character seemed to be the most reasonable solution. We hope it's not close to 2000.
+            paginator.add_line(content.replace('`', '`\u200b'))
             paginator.close_page()
 
         if message.content:
@@ -468,7 +468,7 @@ def add_content(title: str, content: str) -> None:
                 add_content(title, transformer(item))
 
         for page in paginator.pages:
-            await ctx.send(page)
+            await ctx.send(page, allowed_mentions=AllowedMentions.none())
 
     @raw.command()
     async def json(self, ctx: Context, message: Message) -> None:


Test patch: