using System; using Paca.Mobile; using Paca.Mobile.Core; using Xunit; namespace Paca.Tests.Mobile; /// /// Gate 115: 모바일 웹 비디오 재생 엔진 무결성 감사 게이트 /// - 스마트 User-Agent 정제 (Version/4.0 및 ; wv 제거로 순수 모바일 Chrome 변환, 실제 기기명/크롬 버전 보존) /// - MediaPlaybackBypassScript 안전성 (비디오 click 이벤트 강제 play 제거, window.chrome loadTimes/csi 더미 보장) /// - SnifferScript 비침투성 (HTMLMediaElement.prototype.play 변조 배제, 안전한 이벤트 리스너 방식) /// public class MobileWebVideoPlaybackGateTests { [Fact] public void Gate115_UserAgentSanitizer_RemovesWebViewTokens_PreservesRealModelAndVersion() { // 일반적인 안드로이드 시스템 WebView 기본 User-Agent var rawWebViewUa = "Mozilla/5.0 (Linux; U; Android 14; ko-kr; SM-S928N Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.127 Mobile Safari/537.36"; var sanitized = MobileUserAgentSanitizer.SanitizeMobile(rawWebViewUa); // 1. 구글/유튜브가 인앱 브라우저로 감지하는 토큰 제거 확인 Assert.DoesNotContain("; wv", sanitized); Assert.DoesNotContain("Version/4.0", sanitized); // 2. 실제 디바이스 모델명 및 정확한 크롬 버전 보존 확인 (하드웨어 비디오 코덱 매핑 유지) Assert.Contains("SM-S928N", sanitized); Assert.Contains("Chrome/128.0.6613.127", sanitized); Assert.Contains("Mobile Safari/537.36", sanitized); } [Fact] public void Gate115_UserAgentSanitizer_HandlesFallbacksGracefully() { // 빈 문자열 또는 null 입력 시 안전한 기본 Chrome 모바일 UA 반환 var emptyResult = MobileUserAgentSanitizer.SanitizeMobile(""); Assert.StartsWith("Mozilla/5.0", emptyResult); Assert.Contains("Mobile Safari/537.36", emptyResult); Assert.DoesNotContain("Version/4.0", emptyResult); // 이미 순수 모바일 브라우저 UA인 경우 원형 유지 var cleanUa = "Mozilla/5.0 (Linux; Android 15; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36"; var result = MobileUserAgentSanitizer.SanitizeMobile(cleanUa); Assert.Equal(cleanUa, result); } [Fact] public void Gate115_MediaPlaybackBypassScript_HasNoHarmfulVideoClickInterference() { var js = MediaPlaybackBypassScript.BypassJs; // 1. 유튜브 플레이어의 상태 머신을 망가뜨리는 click -> this.play() 리스너가 없어야 함 Assert.DoesNotContain("this.paused", js); Assert.DoesNotContain("this.play()", js); // 2. 구글/유튜브 스크립트가 호출하는 window.chrome 메트릭 API(loadTimes, csi)가 더미로 제공되어야 함 Assert.Contains("loadTimes", js); Assert.Contains("csi", js); } [Fact] public void Gate115_SnifferScript_NonIntrusiveMediaObservation() { var js = SnifferScript.InjectionJs; // 1. HTMLMediaElement.prototype.play 를 직접 오버라이드하여 Promise/재생 체인을 망가뜨리지 않아야 함 Assert.DoesNotContain("HTMLMediaElement.prototype.play =", js); // 2. URL.createObjectURL 안전 바인딩 또는 호출 보호가 있어야 함 Assert.DoesNotContain("apply(this, arguments)", js); } }