releases/tests/Paca.Tests/Core/SecurityAndRateLimitingExtremeRedTests.cs
Yun Chan 8677853822
Some checks failed
release / test (push) Failing after 14m5s
release / windows (push) Has been cancelled
release / android (push) Has been cancelled
release / linux (push) Has been cancelled
release / macos (push) Has been cancelled
test(core/browser/platform): 26 extreme RED/GREEN TDD cycles and full regression hardening (3,235 PASS)
2026-09-18 23:32:32 +09:00

96 lines
3.4 KiB
C#

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);
}
}