feat: DCC stripping in both directions to prevent IP leaks
Block all non-ACTION CTCP/DCC from client-to-server (outbound) and add security logging when inbound CTCP/DCC is stripped. Hard boundary with no config toggle -- DCC exposes the client's real IP which defeats the stealth proxy architecture. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -154,6 +154,16 @@ class TestSuppress:
|
||||
msg = _msg("TOPIC", ["#test", "new topic"], prefix="user!i@h")
|
||||
assert _suppress(msg) is False
|
||||
|
||||
def test_suppresses_dcc_send(self) -> None:
|
||||
msg = _msg("PRIVMSG", ["nick", "\x01DCC SEND file 3232235777 5000 1024\x01"],
|
||||
prefix="user!i@h")
|
||||
assert _suppress(msg) is True
|
||||
|
||||
def test_suppresses_dcc_chat(self) -> None:
|
||||
msg = _msg("PRIVMSG", ["nick", "\x01DCC CHAT chat 3232235777 5000\x01"],
|
||||
prefix="user!i@h")
|
||||
assert _suppress(msg) is True
|
||||
|
||||
|
||||
# -- Router._proxy_for ------------------------------------------------------
|
||||
|
||||
@@ -545,6 +555,46 @@ class TestRouteClientMessage:
|
||||
sent = net.send.call_args[0][0]
|
||||
assert sent.prefix == "me!u@h"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_blocks_outbound_dcc_send(self) -> None:
|
||||
router = Router(_config(), _backlog())
|
||||
net = _mock_network("libera")
|
||||
router.networks["libera"] = net
|
||||
|
||||
msg = _msg("PRIVMSG", ["user/libera", "\x01DCC SEND file 3232235777 5000 1024\x01"])
|
||||
await router.route_client_message(msg)
|
||||
net.send.assert_not_awaited()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_blocks_outbound_dcc_chat(self) -> None:
|
||||
router = Router(_config(), _backlog())
|
||||
net = _mock_network("libera")
|
||||
router.networks["libera"] = net
|
||||
|
||||
msg = _msg("PRIVMSG", ["user/libera", "\x01DCC CHAT chat 3232235777 5000\x01"])
|
||||
await router.route_client_message(msg)
|
||||
net.send.assert_not_awaited()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_passes_outbound_action(self) -> None:
|
||||
router = Router(_config(), _backlog())
|
||||
net = _mock_network("libera")
|
||||
router.networks["libera"] = net
|
||||
|
||||
msg = _msg("PRIVMSG", ["#test/libera", "\x01ACTION waves\x01"])
|
||||
await router.route_client_message(msg)
|
||||
net.send.assert_awaited_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_passes_outbound_normal_privmsg(self) -> None:
|
||||
router = Router(_config(), _backlog())
|
||||
net = _mock_network("libera")
|
||||
router.networks["libera"] = net
|
||||
|
||||
msg = _msg("PRIVMSG", ["#test/libera", "just a normal message"])
|
||||
await router.route_client_message(msg)
|
||||
net.send.assert_awaited_once()
|
||||
|
||||
|
||||
# -- _dispatch (inbound: network -> clients) ---------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user