using System; using System.IO; using Paca.Browser.Content; using Paca.Browser.Extensions; using Paca.Core.Net; using Xunit; namespace Paca.Tests.Core; public class SecurityAndRateLimitingExtremeRedTests { // ========================================== // 1. ExtensionCspValidator // ========================================== [Fact] public void ExtensionCspValidator_Validate_ManifestV3_UnsafeInlineInScriptSrc_ReturnsInvalid() { var result = ExtensionCspValidator.Validate("script-src 'self' 'unsafe-inline'; object-src 'self';", manifestVersion: 3); Assert.False(result.IsValid); Assert.NotEmpty(result.Violations); Assert.Contains(result.Violations, v => v.Contains("unsafe-inline", StringComparison.OrdinalIgnoreCase)); } [Theory] [InlineData("script-src 'self' data:;")] [InlineData("script-src 'self' blob:;")] public void ExtensionCspValidator_Validate_ManifestV3_DataOrBlobInScriptSrc_ReturnsInvalid(string csp) { var result = ExtensionCspValidator.Validate(csp, manifestVersion: 3); Assert.False(result.IsValid); Assert.NotEmpty(result.Violations); } [Theory] [InlineData(";; ; ;")] [InlineData(" ; ; ")] public void ExtensionCspValidator_Validate_EmptySemicolons_FallsBackToDefaultCsp(string csp) { var result = ExtensionCspValidator.Validate(csp, manifestVersion: 3); Assert.True(result.IsValid); Assert.Equal(ExtensionCspValidator.DefaultManifestV3Csp, result.NormalizedCsp); } // ========================================== // 2. SiteAutoMutePolicy // ========================================== [Fact] public void SiteAutoMutePolicy_AddAutoMutePattern_WildcardAsterisk_MutesAllSites() { var policy = new SiteAutoMutePolicy(); policy.AddAutoMutePattern("*"); Assert.True(policy.IsAutoMute("https://youtube.com/watch?v=123")); Assert.True(policy.IsAutoMute("https://news.naver.com")); Assert.True(policy.IsAutoMute("https://example.org")); } [Fact] public void SiteAutoMutePolicy_Save_InvalidOrReadOnlyPath_DoesNotThrowUnhandledException() { // Using an invalid directory path that cannot be created on Windows var policy = new SiteAutoMutePolicy("Z:\\nonexistent_drive_9999\\invalid\\mute.json"); var ex = Record.Exception(() => policy.AddAutoMutePattern("example.com")); Assert.Null(ex); } // ========================================== // 3. HostRateLimiter // ========================================== [Theory] [InlineData("example.com:8080/api/v1", "example.com")] [InlineData("https://sub.4cdn.org/image.png", "sub.4cdn.org")] [InlineData("user:pass@reddit.com:443/r/funny", "reddit.com")] [InlineData(null, "")] [InlineData(" ", "")] public void HostRateLimiter_NormalizeHost_VariousInputs_NormalizesCleanly(string? input, string expected) { var host = HostRateLimiter.NormalizeHost(input); Assert.Equal(expected, host); } [Theory] [InlineData("i.4cdn.org", 1200)] [InlineData("boards.4channel.org", 1200)] [InlineData("v.redd.it", 800)] [InlineData("www.reddit.com", 800)] [InlineData("generic-site.com", 200)] public void HostRateLimiter_GetIntervalMs_RecognizesDomainPresets(string host, int expectedMs) { var interval = HostRateLimiter.GetIntervalMs(host); Assert.Equal(expectedMs, interval); } }