littleshop/TeleBot/TeleBot.Tests/Security/TorProxyTests.cs
SysAdmin d31c0b4aeb CI/CD: Add GitLab CI/CD pipeline for Hostinger deployment
- Updated .gitlab-ci.yml with complete build, test, and deploy stages
- Added authentication redirect fix in Program.cs (302 redirect for admin routes)
- Fixed Cookie vs Bearer authentication conflict for admin panel
- Configure pipeline to build from .NET 9.0 source
- Deploy to Hostinger VPS with proper environment variables
- Include rollback capability for production deployments

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 13:10:48 +01:00

249 lines
8.3 KiB
C#

using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Moq;
using TeleBot.Http;
using Xunit;
namespace TeleBot.Tests.Security
{
/// <summary>
/// Comprehensive tests to verify TOR proxy configuration and usage.
/// These tests prove that TeleBot routes all traffic through TOR.
/// </summary>
public class TorProxyTests
{
private readonly Mock<ILogger> _mockLogger;
public TorProxyTests()
{
_mockLogger = new Mock<ILogger>();
}
[Fact]
public void Socks5HttpHandler_WithTorEnabled_ConfiguresProxy()
{
// Arrange
var config = CreateConfiguration(enableTor: true, torPort: 9050);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert
Assert.NotNull(handler);
Assert.True(handler.UseProxy, "UseProxy should be true when TOR is enabled");
Assert.NotNull(handler.Proxy);
var proxy = handler.Proxy as WebProxy;
Assert.NotNull(proxy);
Assert.Contains("9050", proxy.Address?.ToString() ?? "");
Assert.Contains("socks5", proxy.Address?.ToString() ?? "");
}
[Fact]
public void Socks5HttpHandler_WithTorDisabled_NoProxy()
{
// Arrange
var config = CreateConfiguration(enableTor: false);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert
Assert.NotNull(handler);
// When TOR is disabled, should still work but without proxy
}
[Fact]
public void Socks5HttpHandler_WithTorEnabled_DisablesAutoRedirect()
{
// Arrange
var config = CreateConfiguration(enableTor: true);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert - Security check
Assert.False(handler.AllowAutoRedirect, "Auto-redirect must be disabled to prevent deanonymization");
Assert.Equal(0, handler.MaxAutomaticRedirections);
}
[Fact]
public void Socks5HttpHandler_WithTorEnabled_ConfiguresConnectionPooling()
{
// Arrange
var config = CreateConfiguration(enableTor: true);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert - Performance and security
Assert.Equal(TimeSpan.FromMinutes(5), handler.PooledConnectionLifetime);
Assert.Equal(TimeSpan.FromMinutes(2), handler.PooledConnectionIdleTimeout);
}
[Fact]
public void Socks5HttpHandler_CreateWithTor_UsesSpecifiedPort()
{
// Arrange
int customPort = 9999;
// Act
var handler = Socks5HttpHandler.CreateWithTor(customPort, _mockLogger.Object);
// Assert
Assert.NotNull(handler);
Assert.True(handler.UseProxy);
var proxy = handler.Proxy as WebProxy;
Assert.Contains($"{customPort}", proxy?.Address?.ToString() ?? "");
}
[Fact]
public void Socks5HttpHandler_CreateDirect_NoProxy()
{
// Act
var handler = Socks5HttpHandler.CreateDirect(_mockLogger.Object);
// Assert
Assert.NotNull(handler);
// Direct handler should not have proxy configured
}
[Fact]
public void Socks5HttpHandler_WithTorEnabled_LogsConfiguration()
{
// Arrange
var config = CreateConfiguration(enableTor: true, torPort: 9050);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert - Verify logging
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("SOCKS5") && v.ToString()!.Contains("9050")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once,
"Should log SOCKS5 proxy configuration");
}
[Fact]
public void Socks5HttpHandler_WithTorDisabled_LogsWarning()
{
// Arrange
var config = CreateConfiguration(enableTor: false);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert - Verify security warning
_mockLogger.Verify(
x => x.Log(
LogLevel.Warning,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("DISABLED")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once,
"Should log warning when TOR is disabled");
}
[Theory]
[InlineData(true, 9050)]
[InlineData(true, 9051)]
[InlineData(true, 9052)]
[InlineData(false, 9050)]
public void Socks5HttpHandler_VariousConfigurations_CreatesHandler(bool enableTor, int port)
{
// Arrange
var config = CreateConfiguration(enableTor, port);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert
Assert.NotNull(handler);
Assert.Equal(enableTor, handler.UseProxy);
}
[Fact]
public void Socks5HttpHandler_ProxyBypassLocal_IsFalse()
{
// Arrange
var config = CreateConfiguration(enableTor: true);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert - Security: All traffic must go through TOR
var proxy = handler.Proxy as WebProxy;
Assert.NotNull(proxy);
Assert.False(proxy.BypassProxyOnLocal, "Local traffic must also go through TOR for complete anonymity");
}
[Fact]
public void Socks5HttpHandler_DefaultCredentials_IsFalse()
{
// Arrange
var config = CreateConfiguration(enableTor: true);
// Act
var handler = Socks5HttpHandler.Create(config, _mockLogger.Object);
// Assert - Security
var proxy = handler.Proxy as WebProxy;
Assert.NotNull(proxy);
Assert.False(proxy.UseDefaultCredentials, "Should not use default credentials for security");
}
/// <summary>
/// Test that proves configuration is read correctly from appsettings
/// </summary>
[Fact]
public void Configuration_AppsettingsFormat_IsCorrect()
{
// Arrange
var configData = new Dictionary<string, string>
{
["Privacy:EnableTor"] = "true",
["Privacy:TorSocksPort"] = "9050",
["LittleShop:UseTor"] = "true"
};
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(configData!)
.Build();
// Act
var torEnabled = configuration.GetValue<bool>("Privacy:EnableTor");
var torPort = configuration.GetValue<int>("Privacy:TorSocksPort");
var useTor = configuration.GetValue<bool>("LittleShop:UseTor");
// Assert - Proof of configuration format
Assert.True(torEnabled, "Privacy:EnableTor must be true in production config");
Assert.Equal(9050, torPort);
Assert.True(useTor, "LittleShop:UseTor must be true in production config");
}
// Helper method to create test configuration
private IConfiguration CreateConfiguration(bool enableTor, int torPort = 9050)
{
var configData = new Dictionary<string, string>
{
["Privacy:EnableTor"] = enableTor.ToString(),
["Privacy:TorSocksPort"] = torPort.ToString()
};
return new ConfigurationBuilder()
.AddInMemoryCollection(configData!)
.Build();
}
}
}