@@ -43,7 +43,7 @@ public ActionResult<ModStatus> GetStatus()
4343 private const string AppleChuAsset = "AppleChu.dll" ;
4444
4545 public record GitHubRelease ( string Tag_name , GitHubAsset [ ] Assets ) ;
46- public record GitHubAsset ( string Name , string Browser_download_url ) ;
46+ public record GitHubAsset ( string Name , string Browser_download_url , string ? Digest ) ;
4747 public record VersionInfo ( string Latest , string Installed , string DownloadUrl ) ;
4848
4949 private static readonly HttpClient Http = new ( ) ;
@@ -92,12 +92,16 @@ public async Task<ActionResult> InstallLoader([FromBody] InstallRequest? request
9292
9393 var binPath = Path . Combine ( gamePath , "bin" ) ;
9494 var loaderData = await Http . GetByteArrayAsync ( loaderUrl ) ;
95+ if ( ! VerifyDigest ( loaderData , FindAssetDigest ( release , loaderUrl ) ) )
96+ return BadRequest ( "version.dll 校验失败,文件可能已损坏或被篡改" ) ;
9597 await System . IO . File . WriteAllBytesAsync ( Path . Combine ( binPath , "version.dll" ) , loaderData ) ;
9698
9799 var proxyUrl = release ? . Assets . FirstOrDefault ( a => a . Name == ProxyAsset ) ? . Browser_download_url ;
98100 if ( ! string . IsNullOrEmpty ( proxyUrl ) )
99101 {
100102 var proxyData = await Http . GetByteArrayAsync ( proxyUrl ) ;
103+ if ( ! VerifyDigest ( proxyData , FindAssetDigest ( release , proxyUrl ) ) )
104+ return BadRequest ( "d3d9.dll 校验失败,文件可能已损坏或被篡改" ) ;
101105 await System . IO . File . WriteAllBytesAsync ( Path . Combine ( binPath , "d3d9.dll" ) , proxyData ) ;
102106 }
103107
@@ -113,19 +117,19 @@ public async Task<ActionResult> InstallAppleChu([FromBody] InstallRequest? reque
113117
114118 var binPath = Path . Combine ( gamePath , "bin" ) ;
115119
120+ var release = await GetLatestRelease ( AppleChuRepo , AppleChuAsset ) ;
116121 var url = request ? . Url ;
117122 if ( string . IsNullOrEmpty ( url ) )
118- {
119- var release = await GetLatestRelease ( AppleChuRepo , AppleChuAsset ) ;
120123 url = release ? . Assets . FirstOrDefault ( a => a . Name == AppleChuAsset ) ? . Browser_download_url ;
121- }
122124 if ( string . IsNullOrEmpty ( url ) )
123125 return NotFound ( "No release found" ) ;
124126
125127 if ( ! IsAllowedDownloadUrl ( url ) )
126128 return BadRequest ( "下载 URL 不在白名单内" ) ;
127129
128130 var data = await Http . GetByteArrayAsync ( url ) ;
131+ if ( ! VerifyDigest ( data , FindAssetDigest ( release , url ) ) )
132+ return BadRequest ( "AppleChu.dll 校验失败,文件可能已损坏或被篡改" ) ;
129133 var modsDir = Path . Combine ( binPath , "mods" ) ;
130134 Directory . CreateDirectory ( modsDir ) ;
131135 await System . IO . File . WriteAllBytesAsync ( Path . Combine ( modsDir , "AppleChu.dll" ) , data ) ;
@@ -151,6 +155,22 @@ private static bool IsAllowedDownloadUrl(string? url)
151155 && uri . AbsolutePath . StartsWith ( "/MuNET-OSS/" , StringComparison . OrdinalIgnoreCase ) ;
152156 }
153157
158+ private static string ? FindAssetDigest ( GitHubRelease ? release , string url )
159+ {
160+ return release ? . Assets . FirstOrDefault ( a => a . Browser_download_url == url ) ? . Digest ;
161+ }
162+
163+ private static bool VerifyDigest ( byte [ ] data , string ? digest )
164+ {
165+ // 旧 Release 资产没有 digest 字段时跳过校验
166+ if ( string . IsNullOrEmpty ( digest ) ) return true ;
167+ if ( ! digest . StartsWith ( "sha256:" , StringComparison . OrdinalIgnoreCase ) ) return true ;
168+
169+ var expected = digest [ "sha256:" . Length ..] ;
170+ var actual = Convert . ToHexString ( System . Security . Cryptography . SHA256 . HashData ( data ) ) ;
171+ return string . Equals ( actual , expected , StringComparison . OrdinalIgnoreCase ) ;
172+ }
173+
154174 private static bool IsValidModId ( string modId )
155175 {
156176 return ! string . IsNullOrEmpty ( modId ) && System . Text . RegularExpressions . Regex . IsMatch ( modId , @"^[A-Za-z0-9_-]+$" ) ;
0 commit comments