224 lines
9.1 KiB
C#
224 lines
9.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using Paca.App;
|
|
using Paca.App.Browser;
|
|
using Paca.Browser.Tabs;
|
|
using Paca.Mobile;
|
|
using Paca.Mobile.Core;
|
|
using Paca.Server;
|
|
using Paca.Tests.Infrastructure;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.Browser;
|
|
|
|
/// <summary>
|
|
/// 초고강도 사생활 보호(InPrivate / Incognito) 무결성 게이트:
|
|
/// 1. 영상 재생목록 디스크 무누수 (playlist.json 차단)
|
|
/// 2. 사이트 줌 배율 디스크 무누수 (sitezoom.json 차단)
|
|
/// 3. 보조 창(Glance, Player) InPrivate 격리 옵션 승계
|
|
/// 4. 원격 브라우저 탭 동기화(/api/tabs) 비공개 탭 100% 필터링
|
|
/// 5. 확장 프로그램 콘텐츠 스크립트 InPrivate 탭 주입 차단
|
|
/// 6. 모바일 시크릿 탭 활성 시 PC 전송 액션 방어 가드
|
|
/// </summary>
|
|
[Collection("E2E")]
|
|
public class PrivateBrowsingExhaustiveGateTests
|
|
{
|
|
[Fact]
|
|
public async Task Gate1_InPrivate_영상_재생_시_playlist_json_디스크_누출_원천_차단()
|
|
{
|
|
var dataDir = Path.Combine(Path.GetTempPath(), $"paca_priv_playlist_{Guid.NewGuid():N}");
|
|
Environment.SetEnvironmentVariable("VD_DATA_DIR", dataDir);
|
|
try
|
|
{
|
|
using var sta = StaDispatcher.Start();
|
|
WpfTestApp.Ensure(sta.Dispatcher);
|
|
|
|
await sta.InvokeAsync(async () =>
|
|
{
|
|
var window = new MainWindow();
|
|
window.Show();
|
|
|
|
// InPrivate 탭 생성
|
|
var privateTab = window.Tabs.NewTab("https://classified-stream.tv/watch?v=999", activate: true, isPrivate: true);
|
|
|
|
// 미디어 감지 시뮬레이션: OnResponseReceived 혹은 AddToPlaylist 호출
|
|
// privateTab이 활성/InPrivate인 상태에서 AddToPlaylist 호출
|
|
var privMediaUrl = "https://classified-stream.tv/media.mp4";
|
|
window.SimulatePrivateMediaDetection(privMediaUrl, "비밀 동영상", isPrivate: true);
|
|
|
|
var playlistPath = Path.Combine(dataDir, "playlist.json");
|
|
if (File.Exists(playlistPath))
|
|
{
|
|
var content = File.ReadAllText(playlistPath);
|
|
Assert.DoesNotContain("classified-stream.tv", content);
|
|
Assert.DoesNotContain("비밀 동영상", content);
|
|
}
|
|
|
|
// 반면 일반 탭의 미디어는 정상적으로 playlist.json에 저장되어야 함
|
|
var normalTab = window.Tabs.NewTab("https://public-site.org/home", activate: true, isPrivate: false);
|
|
window.SimulatePrivateMediaDetection("https://public-video.org/public-stream.mp4", "Public Video", isPrivate: false);
|
|
|
|
Assert.True(File.Exists(playlistPath), "일반 미디어는 playlist.json에 저장되어야 합니다.");
|
|
var normalContent = File.ReadAllText(playlistPath);
|
|
Assert.Contains("public-video.org", normalContent);
|
|
Assert.Contains("Public Video", normalContent);
|
|
|
|
window.Close();
|
|
await Task.CompletedTask;
|
|
});
|
|
}
|
|
finally
|
|
{
|
|
try { if (Directory.Exists(dataDir)) Directory.Delete(dataDir, true); } catch { }
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Gate2_InPrivate_줌_배율_변경_시_sitezoom_json_디스크_누출_원천_차단()
|
|
{
|
|
var dataDir = Path.Combine(Path.GetTempPath(), $"paca_priv_zoom_{Guid.NewGuid():N}");
|
|
Environment.SetEnvironmentVariable("VD_DATA_DIR", dataDir);
|
|
try
|
|
{
|
|
using var sta = StaDispatcher.Start();
|
|
WpfTestApp.Ensure(sta.Dispatcher);
|
|
|
|
await sta.InvokeAsync(async () =>
|
|
{
|
|
var window = new MainWindow();
|
|
window.Show();
|
|
|
|
var secretUrl = "https://confidential-portal.internal";
|
|
var privateTab = window.Tabs.NewTab(secretUrl, activate: true, isPrivate: true);
|
|
|
|
// InPrivate 탭에서 줌 레벨 변경 실행
|
|
window.ApplyZoomForTest(secretUrl, 1.5, isPrivate: true);
|
|
|
|
var sitezoomPath = Path.Combine(dataDir, "sitezoom.json");
|
|
if (File.Exists(sitezoomPath))
|
|
{
|
|
var content = File.ReadAllText(sitezoomPath);
|
|
Assert.DoesNotContain("confidential-portal.internal", content);
|
|
}
|
|
|
|
// 일반 탭에서 줌 레벨 변경 실행
|
|
var publicUrl = "https://public-portal.org";
|
|
var normalTab = window.Tabs.NewTab(publicUrl, activate: true, isPrivate: false);
|
|
window.ApplyZoomForTest(publicUrl, 1.25, isPrivate: false);
|
|
|
|
Assert.True(File.Exists(sitezoomPath), "일반 탭 줌 설정은 sitezoom.json에 기록되어야 합니다.");
|
|
var publicContent = File.ReadAllText(sitezoomPath);
|
|
Assert.Contains("public-portal.org", publicContent);
|
|
|
|
window.Close();
|
|
await Task.CompletedTask;
|
|
});
|
|
}
|
|
finally
|
|
{
|
|
try { if (Directory.Exists(dataDir)) Directory.Delete(dataDir, true); } catch { }
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate3_GlanceWindow_및_PlayerWindow_InPrivate_생성자_플래그_지원_검증()
|
|
{
|
|
// GlanceWindow 및 PlayerWindow가 isPrivate 생성자 매개변수를 지원하는지 리플렉션/컴파일 레벨 검증
|
|
var glanceCtor = typeof(GlanceWindow).GetConstructors()
|
|
.FirstOrDefault(c => c.GetParameters().Any(p => p.Name == "isPrivate"));
|
|
Assert.NotNull(glanceCtor);
|
|
|
|
var playerCtor = typeof(PlayerWindow).GetConstructors()
|
|
.FirstOrDefault(c => c.GetParameters().Any(p => p.Name == "isPrivate"));
|
|
Assert.NotNull(playerCtor);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Gate4_ServerHost_api_tabs_동기화_시_InPrivate_탭_100퍼센트_배제()
|
|
{
|
|
var tabs = new List<RemoteTabInfo>
|
|
{
|
|
new("tab-1", "공개 위키피디아", "https://wikipedia.org", IsActive: false, IsPrivate: false),
|
|
new("tab-2", "비밀 은행 계좌", "https://secret-bank.com", IsActive: true, IsPrivate: true),
|
|
new("tab-3", "회사 인트라넷 문서", "https://internal-docs.corp", IsActive: false, IsPrivate: true),
|
|
new("tab-4", "공개 유튜브 음악", "https://youtube.com", IsActive: false, IsPrivate: false)
|
|
};
|
|
|
|
var server = await ServerHost.StartAsync(new ServerOptions
|
|
{
|
|
Port = 0,
|
|
Token = "test-token",
|
|
TabProvider = () => tabs,
|
|
EnableMdns = false,
|
|
EnableLanBeacon = false
|
|
});
|
|
|
|
try
|
|
{
|
|
using var client = new System.Net.Http.HttpClient();
|
|
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "test-token");
|
|
var res = await client.GetAsync($"http://127.0.0.1:{server.Port}/api/tabs");
|
|
Assert.True(res.IsSuccessStatusCode);
|
|
|
|
var json = await res.Content.ReadAsStringAsync();
|
|
|
|
// 공개 탭은 노출되어야 함
|
|
Assert.Contains("wikipedia.org", json);
|
|
Assert.Contains("youtube.com", json);
|
|
|
|
// InPrivate 탭은 100% 철저히 배제되어야 함
|
|
Assert.DoesNotContain("secret-bank.com", json);
|
|
Assert.DoesNotContain("internal-docs.corp", json);
|
|
Assert.DoesNotContain("비밀 은행 계좌", json);
|
|
}
|
|
finally
|
|
{
|
|
await server.DisposeAsync();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate5_Extension_콘텐츠스크립트_InPrivate_탭_인젝션_차단_계약_검증()
|
|
{
|
|
var tabManager = new TabManager(() => new TabItem());
|
|
var privTab = tabManager.NewTab("https://secret.com", isPrivate: true);
|
|
var normTab = tabManager.NewTab("https://public.com", isPrivate: false);
|
|
|
|
Assert.True(privTab.IsPrivate);
|
|
Assert.False(normTab.IsPrivate);
|
|
|
|
// MainWindow.CanInjectExtensionToTab 메서드 또는 격리 검증
|
|
Assert.False(MainWindow.CanInjectExtensionToTab(privTab, isPrivateWindow: false));
|
|
Assert.False(MainWindow.CanInjectExtensionToTab(normTab, isPrivateWindow: true));
|
|
Assert.True(MainWindow.CanInjectExtensionToTab(normTab, isPrivateWindow: false));
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate6_Mobile_시크릿탭_PC_동기화_가드_검증()
|
|
{
|
|
var privTab = new BrowserTab
|
|
{
|
|
Id = "m-priv-1",
|
|
Title = "시크릿 웹페이지",
|
|
Url = "https://secret-vault.org",
|
|
IsPrivate = true
|
|
};
|
|
|
|
var normTab = new BrowserTab
|
|
{
|
|
Id = "m-norm-1",
|
|
Title = "공개 웹페이지",
|
|
Url = "https://public-news.org",
|
|
IsPrivate = false
|
|
};
|
|
|
|
// 시크릿 탭은 PC 전송 대상에서 차단되어야 함
|
|
Assert.False(MobileSyncSecurityGuard.CanSendTabToPc(privTab));
|
|
Assert.True(MobileSyncSecurityGuard.CanSendTabToPc(normTab));
|
|
Assert.False(MobileSyncSecurityGuard.CanSendTabToPc(null));
|
|
}
|
|
}
|