Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(irc): restrict the start of usernames even more #198

Merged
merged 1 commit into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions dibridge/irc.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
# talk to someone if they are in an active conversation with them.
LEFT_WHILE_TALKING_TIMEOUT = 60 * 10

# By RFC, only these characters are allowed in a nickname.
REGEX_NICKNAME_FILTER = r"[^a-zA-Z0-9_\-\[\]\{\}\|]"
# By RFC, a nickname cannot start with a number or a dash.
REGEX_NICKNAME_START_FILTER = r"^[0-9\-]+"
# By implementation, a username is more strict than a nickname in what
# it can start with. This filter is in addition to the nickname filters.
REGEX_USERNAME_START_FILTER = r"^[_\[\]\{\}\|]+"


class IRCRelay(irc.client_aio.AioSimpleIRCClient):
def __init__(self, host, port, nickname, channel, puppet_ip_range, puppet_postfix, ignore_list, idle_timeout):
Expand Down Expand Up @@ -129,10 +137,8 @@ async def _pinger(self):

async def _connect(self):
while True:
username = self._nickname
# An additional constraints usernames have over nicknames, that they are
# also not allowed to start with an underscore.
username = re.sub(r"^_+", "", username)
# Additional constraints usernames have over nicknames.
username = re.sub(REGEX_USERNAME_START_FILTER, "", self._nickname)

try:
await self.connection.connect(
Expand Down Expand Up @@ -168,11 +174,15 @@ async def _send_message(self, discord_id, discord_username, message, is_action=F
sanitized_discord_username = self._sanitize_discord_username(discord_username)
ipv6_address = self._puppet_ip_range[self._generate_ipv6_bits(sanitized_discord_username)]

irc_nickname = f"{sanitized_discord_username}{self._puppet_postfix}"
irc_username = re.sub(REGEX_USERNAME_START_FILTER, "", irc_nickname)

self._puppets[discord_id] = IRCPuppet(
self._host,
self._port,
ipv6_address,
f"{sanitized_discord_username}{self._puppet_postfix}",
irc_nickname,
irc_username,
self._channel,
functools.partial(self._remove_puppet, discord_id),
self._idle_timeout,
Expand Down Expand Up @@ -211,10 +221,10 @@ def _sanitize_discord_username(self, discord_username):
original_discord_username = discord_username

discord_username = discord_username.strip()
# Remove all characters not allowed in IRC usernames.
discord_username = re.sub(r"[^a-zA-Z0-9_\-\[\]\{\}\|]", "", discord_username)
# Make sure a username doesn't start with a number or "-".
discord_username = re.sub(r"^[0-9\-]", "", discord_username)
# Remove all characters not allowed in IRC nicknames.
discord_username = re.sub(REGEX_NICKNAME_FILTER, "", discord_username)
# Make sure a nicknames doesn't start with an invalid character.
discord_username = re.sub(REGEX_NICKNAME_START_FILTER, "", discord_username)

# On Discord you can create usernames that don't contain any character valid
# on IRC, leaving an empty username. In that case we have no option but to
Expand Down
11 changes: 3 additions & 8 deletions dibridge/irc_puppet.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import asyncio
import irc.client_aio
import logging
import re
import socket


class IRCPuppet(irc.client_aio.AioSimpleIRCClient):
def __init__(self, irc_host, irc_port, ipv6_address, nickname, channel, remove_puppet_func, idle_timeout):
def __init__(self, irc_host, irc_port, ipv6_address, nickname, username, channel, remove_puppet_func, idle_timeout):
irc.client.SimpleIRCClient.__init__(self)

self.loop = asyncio.get_event_loop()
Expand All @@ -17,6 +16,7 @@ def __init__(self, irc_host, irc_port, ipv6_address, nickname, channel, remove_p
self._nickname = nickname
self._nickname_original = nickname
self._nickname_iteration = 0
self._username = username
self._joined = False
self._channel = channel
self._pinger_task = None
Expand Down Expand Up @@ -140,17 +140,12 @@ async def connect(self):
local_addr = (str(self._ipv6_address), 0)

while self._reconnect:
username = self._nickname
# An additional constraints usernames have over nicknames, that they are
# also not allowed to start with an underscore.
username = re.sub(r"^_+", "", username)

try:
await self.connection.connect(
self._irc_host,
self._irc_port,
self._nickname,
username=username,
username=self._username,
# We force an IPv6 connection, as we need that for the puppet source address.
connect_factory=irc.connection.AioFactory(
family=socket.AF_INET6, local_addr=local_addr, ssl=self._irc_port == 6697
Expand Down