Files
Cockatrice/tests/trusted_sources_test.cpp
Zach H 46cdbac830 [Servatrice] Match trusted_sources by address, not substring (#7582)
trusted_sources was checked with QString::contains. A dual-stack listener
reports a local proxy as ::ffff:127.0.0.1, which never matches the default
"127.0.0.1,::1". Behind nginx the forwarded client IP header was then
ignored, every websocket user shared the proxy's address, and logins
failed with "too many concurrent connections" once max_users_per_address
was reached.

Add isTrustedSource(), which parses each entry as a QHostAddress and
compares exact addresses with IPv4-mapped addresses normalized, and use it
at all four call sites. The ban command also read server/trusted_sources
instead of the documented security/trusted_sources; it now uses the same
getter. Entries that are not IP addresses are logged at startup since they
no longer match anything.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:50:04 -04:00

59 lines
1.9 KiB
C++

#include "trusted_sources.h"
#include "gtest/gtest.h"
#include <QHostAddress>
namespace
{
const QString kDefault = QStringLiteral("127.0.0.1,::1");
TEST(TrustedSourcesTest, MatchesListedAddresses)
{
ASSERT_TRUE(isTrustedSource(kDefault, QHostAddress("127.0.0.1")));
ASSERT_TRUE(isTrustedSource(kDefault, QHostAddress("::1")));
ASSERT_FALSE(isTrustedSource(kDefault, QHostAddress("10.0.0.1")));
}
TEST(TrustedSourcesTest, MatchesIpv4MappedPeerAgainstPlainIpv4Entry)
{
// A dual-stack listener reports a local IPv4 proxy as ::ffff:127.0.0.1;
// it must still match the default 127.0.0.1 entry.
ASSERT_TRUE(isTrustedSource(kDefault, QHostAddress("::ffff:127.0.0.1")));
}
TEST(TrustedSourcesTest, MatchesPlainIpv4PeerAgainstMappedEntry)
{
ASSERT_TRUE(isTrustedSource("::ffff:192.0.2.7", QHostAddress("192.0.2.7")));
}
TEST(TrustedSourcesTest, DoesNotMatchSubstrings)
{
// Substring matching would let 27.0.0.1 or 0.0.1 pass as 127.0.0.1.
ASSERT_FALSE(isTrustedSource(kDefault, QHostAddress("27.0.0.1")));
ASSERT_FALSE(isTrustedSource("192.0.2.10", QHostAddress("192.0.2.1")));
ASSERT_FALSE(isTrustedSource(kDefault, QHostAddress(":")));
}
TEST(TrustedSourcesTest, ToleratesWhitespaceAndCase)
{
ASSERT_TRUE(isTrustedSource(" 192.0.2.7 , 2001:DB8::1 ", QHostAddress("2001:db8::1")));
ASSERT_TRUE(isTrustedSource(" 192.0.2.7 , 2001:DB8::1 ", QHostAddress("192.0.2.7")));
}
TEST(TrustedSourcesTest, IgnoresMalformedEntriesAndNullAddresses)
{
ASSERT_FALSE(isTrustedSource("not-an-ip,,", QHostAddress("127.0.0.1")));
ASSERT_FALSE(isTrustedSource("", QHostAddress("127.0.0.1")));
ASSERT_FALSE(isTrustedSource(kDefault, QHostAddress()));
}
TEST(TrustedSourcesTest, StringOverloadParsesAddress)
{
ASSERT_TRUE(isTrustedSource(kDefault, QStringLiteral("::ffff:127.0.0.1")));
ASSERT_FALSE(isTrustedSource(kDefault, QStringLiteral("27.0.0.1")));
ASSERT_FALSE(isTrustedSource(kDefault, QString()));
}
} // namespace