Skip to content

Commit abb439c

Browse files
authored
fix(irc): restrict the start of usernames even more (#198)
Turns out, they have to start with [a-zA-Z0-9].
1 parent be03b1c commit abb439c

File tree

2 files changed

+22
-17
lines changed

2 files changed

+22
-17
lines changed

dibridge/irc.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
# talk to someone if they are in an active conversation with them.
2020
LEFT_WHILE_TALKING_TIMEOUT = 60 * 10
2121

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

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

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

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

177+
irc_nickname = f"{sanitized_discord_username}{self._puppet_postfix}"
178+
irc_username = re.sub(REGEX_USERNAME_START_FILTER, "", irc_nickname)
179+
171180
self._puppets[discord_id] = IRCPuppet(
172181
self._host,
173182
self._port,
174183
ipv6_address,
175-
f"{sanitized_discord_username}{self._puppet_postfix}",
184+
irc_nickname,
185+
irc_username,
176186
self._channel,
177187
functools.partial(self._remove_puppet, discord_id),
178188
self._idle_timeout,
@@ -211,10 +221,10 @@ def _sanitize_discord_username(self, discord_username):
211221
original_discord_username = discord_username
212222

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

219229
# On Discord you can create usernames that don't contain any character valid
220230
# on IRC, leaving an empty username. In that case we have no option but to

dibridge/irc_puppet.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import asyncio
22
import irc.client_aio
33
import logging
4-
import re
54
import socket
65

76

87
class IRCPuppet(irc.client_aio.AioSimpleIRCClient):
9-
def __init__(self, irc_host, irc_port, ipv6_address, nickname, channel, remove_puppet_func, idle_timeout):
8+
def __init__(self, irc_host, irc_port, ipv6_address, nickname, username, channel, remove_puppet_func, idle_timeout):
109
irc.client.SimpleIRCClient.__init__(self)
1110

1211
self.loop = asyncio.get_event_loop()
@@ -17,6 +16,7 @@ def __init__(self, irc_host, irc_port, ipv6_address, nickname, channel, remove_p
1716
self._nickname = nickname
1817
self._nickname_original = nickname
1918
self._nickname_iteration = 0
19+
self._username = username
2020
self._joined = False
2121
self._channel = channel
2222
self._pinger_task = None
@@ -140,17 +140,12 @@ async def connect(self):
140140
local_addr = (str(self._ipv6_address), 0)
141141

142142
while self._reconnect:
143-
username = self._nickname
144-
# An additional constraints usernames have over nicknames, that they are
145-
# also not allowed to start with an underscore.
146-
username = re.sub(r"^_+", "", username)
147-
148143
try:
149144
await self.connection.connect(
150145
self._irc_host,
151146
self._irc_port,
152147
self._nickname,
153-
username=username,
148+
username=self._username,
154149
# We force an IPv6 connection, as we need that for the puppet source address.
155150
connect_factory=irc.connection.AioFactory(
156151
family=socket.AF_INET6, local_addr=local_addr, ssl=self._irc_port == 6697

0 commit comments

Comments
 (0)