165 lines
6.3 KiB
C#
165 lines
6.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Paca.Mobile;
|
|
using Paca.Mobile.Core;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.Mobile;
|
|
|
|
/// <summary>
|
|
/// PACA Mobile 사생활 보호(InPrivate / Incognito) 세션 격리 및 수명주기 무결성 검증 게이트
|
|
/// </summary>
|
|
public class MobilePrivateBrowsingGateTests : IDisposable
|
|
{
|
|
private readonly string _testTempDir;
|
|
|
|
public MobilePrivateBrowsingGateTests()
|
|
{
|
|
_testTempDir = Path.Combine(Path.GetTempPath(), "paca_mobile_priv_tests_" + Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(_testTempDir);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
if (Directory.Exists(_testTempDir))
|
|
Directory.Delete(_testTempDir, recursive: true);
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate1_MobilePrivateBrowsingPolicy_사생활보호_정책_단정_검증()
|
|
{
|
|
var privTab = new BrowserTab
|
|
{
|
|
Id = "priv-1",
|
|
Url = "https://confidential-portal.org",
|
|
IsPrivate = true
|
|
};
|
|
|
|
var normTab = new BrowserTab
|
|
{
|
|
Id = "norm-1",
|
|
Url = "https://public-news.org",
|
|
IsPrivate = false
|
|
};
|
|
|
|
// 1. 방문 기록 저장 차단 여부
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldPersistHistory(privTab));
|
|
Assert.True(MobilePrivateBrowsingPolicy.ShouldPersistHistory(normTab));
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldPersistHistory(null));
|
|
|
|
// 2. 디스크 쿠키 Flush 차단 여부 (RAM에만 유지)
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldFlushCookies(privTab));
|
|
Assert.True(MobilePrivateBrowsingPolicy.ShouldFlushCookies(normTab));
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldFlushCookies(null));
|
|
|
|
// 3. 디스크 캐시 및 양식 자동저장 차단 여부
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldEnableDiskCache(privTab));
|
|
Assert.True(MobilePrivateBrowsingPolicy.ShouldEnableDiskCache(normTab));
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldSaveFormData(privTab));
|
|
Assert.True(MobilePrivateBrowsingPolicy.ShouldSaveFormData(normTab));
|
|
|
|
// 4. 최근 앱 섬네일 스크린샷 방어(FLAG_SECURE) 활성화 여부
|
|
Assert.True(MobilePrivateBrowsingPolicy.ShouldEnableSecureWindow(privTab));
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldEnableSecureWindow(normTab));
|
|
Assert.False(MobilePrivateBrowsingPolicy.ShouldEnableSecureWindow(null));
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate2_MobileTabManager_시크릿탭_카운트_및_CloseAllPrivateTabs_검증()
|
|
{
|
|
var manager = new MobileTabManager();
|
|
// manager starts with 1 normal tab
|
|
Assert.Equal(1, manager.NormalTabCount);
|
|
Assert.Equal(0, manager.PrivateTabCount);
|
|
Assert.False(manager.HasPrivateTabs);
|
|
|
|
var priv1 = manager.AddTab("https://secret1.com", isPrivate: true);
|
|
var priv2 = manager.AddTab("https://secret2.com", isPrivate: true);
|
|
var norm2 = manager.AddTab("https://public2.com", isPrivate: false);
|
|
|
|
Assert.Equal(4, manager.Count);
|
|
Assert.Equal(2, manager.NormalTabCount);
|
|
Assert.Equal(2, manager.PrivateTabCount);
|
|
Assert.True(manager.HasPrivateTabs);
|
|
|
|
bool allPrivateClosedFired = false;
|
|
manager.AllPrivateTabsClosed += () => allPrivateClosedFired = true;
|
|
|
|
// 모든 시크릿 탭 일괄 닫기
|
|
manager.CloseAllPrivateTabs();
|
|
|
|
Assert.True(allPrivateClosedFired, "AllPrivateTabsClosed 이벤트가 발생해야 합니다.");
|
|
Assert.Equal(2, manager.Count);
|
|
Assert.Equal(2, manager.NormalTabCount);
|
|
Assert.Equal(0, manager.PrivateTabCount);
|
|
Assert.False(manager.HasPrivateTabs);
|
|
Assert.All(manager.Tabs, t => Assert.False(t.IsPrivate));
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate3_마지막_시크릿탭_개별_종료_시_AllPrivateTabsClosed_이벤트_발생_검증()
|
|
{
|
|
var manager = new MobileTabManager();
|
|
var priv1 = manager.AddTab("https://secret-only.com", isPrivate: true);
|
|
|
|
BrowserTab? closedTab = null;
|
|
manager.TabClosed += t => closedTab = t;
|
|
|
|
bool allPrivateClosedFired = false;
|
|
manager.AllPrivateTabsClosed += () => allPrivateClosedFired = true;
|
|
|
|
manager.CloseTab(priv1.Id);
|
|
|
|
Assert.NotNull(closedTab);
|
|
Assert.Equal(priv1.Id, closedTab!.Id);
|
|
Assert.True(allPrivateClosedFired, "마지막 시크릿 탭이 닫히면 AllPrivateTabsClosed가 발행되어야 합니다.");
|
|
Assert.False(manager.HasPrivateTabs);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate4_MobilePrivateSessionManager_세션_종료_시_플랫폼_정화_훅_연동_검증()
|
|
{
|
|
bool cacheCleared = false;
|
|
bool cookiesCleared = false;
|
|
bool formDataCleared = false;
|
|
bool webStorageCleared = false;
|
|
|
|
var sessionManager = new MobilePrivateSessionManager(
|
|
clearCache: () => cacheCleared = true,
|
|
clearCookies: () => cookiesCleared = true,
|
|
clearFormData: () => formDataCleared = true,
|
|
clearWebStorage: () => webStorageCleared = true
|
|
);
|
|
|
|
sessionManager.PurgePrivateSessionData();
|
|
|
|
Assert.True(cacheCleared, "캐시 정화 훅이 실행되어야 합니다.");
|
|
Assert.True(cookiesCleared, "쿠키 정화 훅이 실행되어야 합니다.");
|
|
Assert.True(formDataCleared, "폼 데이터 정화 훅이 실행되어야 합니다.");
|
|
Assert.True(webStorageCleared, "웹 스토리지 정화 훅이 실행되어야 합니다.");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Gate5_MobileTabManager_세션_영속화_시_시크릿탭_누출_원천_차단()
|
|
{
|
|
var manager = new MobileTabManager();
|
|
manager.AddTab("https://public-portal.com", isPrivate: false);
|
|
manager.AddTab("https://confidential-bank-account.org/dashboard", isPrivate: true);
|
|
|
|
var sessionFile = Path.Combine(_testTempDir, "session_test.json");
|
|
await manager.SaveSessionAsync(sessionFile);
|
|
|
|
Assert.True(File.Exists(sessionFile));
|
|
var rawJson = await File.ReadAllTextAsync(sessionFile);
|
|
|
|
// 시크릿 URL이 디스크 파일에 일체 기록되지 않아야 함
|
|
Assert.DoesNotContain("confidential-bank-account", rawJson);
|
|
Assert.Contains("public-portal.com", rawJson);
|
|
}
|
|
}
|