@@ -32,11 +32,16 @@ pub enum ModZipError {
3232 InvalidModJson ( String ) ,
3333 #[ error( "Invalid binaries: {0}" ) ]
3434 InvalidBinaries ( String ) ,
35- #[ error( "{0}" ) ]
36- GenericError ( String ) ,
3735}
3836
3937pub fn extract_mod_logo ( file : & mut ZipFile < Cursor < Bytes > > ) -> Result < Vec < u8 > , ModZipError > {
38+ const FIVE_MEGABYTES : u64 = 5 * 1000 * 1000 ;
39+ if file. size ( ) > FIVE_MEGABYTES {
40+ return Err ( ModZipError :: InvalidLogo (
41+ "Logo size excedes max allowed size (5 MB)" . into ( ) ,
42+ ) ) ;
43+ }
44+
4045 let mut logo: Vec < u8 > = Vec :: with_capacity ( file. size ( ) as usize ) ;
4146 file. read_to_end ( & mut logo)
4247 . inspect_err ( |e| log:: error!( "logo.png read fail: {}" , e) ) ?;
@@ -83,6 +88,13 @@ pub fn extract_mod_logo(file: &mut ZipFile<Cursor<Bytes>>) -> Result<Vec<u8>, Mo
8388}
8489
8590pub fn validate_mod_logo ( file : & mut ZipFile < Cursor < Bytes > > ) -> Result < ( ) , ModZipError > {
91+ const FIVE_MEGABYTES : u64 = 5 * 1000 * 1000 ;
92+ if file. size ( ) > FIVE_MEGABYTES {
93+ return Err ( ModZipError :: InvalidLogo (
94+ "Logo size excedes max allowed size (5 MB)" . into ( ) ,
95+ ) ) ;
96+ }
97+
8698 let mut logo: Vec < u8 > = Vec :: with_capacity ( file. size ( ) as usize ) ;
8799 file. read_to_end ( & mut logo)
88100 . inspect_err ( |e| log:: error!( "logo.png read fail: {}" , e) ) ?;
@@ -133,23 +145,33 @@ pub fn bytes_to_ziparchive(bytes: Bytes) -> Result<ZipArchive<Cursor<Bytes>>, Mo
133145}
134146
135147async fn download ( url : & str , limit_mb : u32 ) -> Result < Bytes , ModZipError > {
136- let limit_bytes = limit_mb * 1_000_000 ;
137- let response = reqwest:: get ( url)
148+ let limit_bytes: u64 = limit_mb as u64 * 1_000_000 ;
149+ let mut response = reqwest:: get ( url)
138150 . await
139151 . inspect_err ( |e| log:: error!( "Failed to fetch .geode file: {e}" ) ) ?;
140152
141- let len = response . content_length ( ) . ok_or ( ModZipError :: GenericError (
142- "Couldn't determine .geode file size" . into ( ) ,
143- ) ) ? ;
153+ // Check Content-Length, but the server can lie about this, so we'll also stream the file
154+ // If the header is somehow unavailable, we'll just check the size when streaming
155+ let content_length = response . content_length ( ) . unwrap_or ( 0 ) ;
144156
145- if len > limit_bytes as u64 {
146- let len_mb = len / 1_000_000 ;
157+ if content_length > limit_bytes {
158+ let len_mb = content_length / 1_000_000 ;
147159 return Err ( ModZipError :: ModFileTooLarge ( len_mb, limit_mb. into ( ) ) ) ;
148160 }
149161
150- response
151- . bytes ( )
152- . await
153- . inspect_err ( |e| log:: error!( "Failed to get bytes from .geode: {}" , e) )
154- . map_err ( |e| e. into ( ) )
162+ let mut data: Vec < u8 > = Vec :: with_capacity ( content_length as usize ) ;
163+
164+ let mut streamed: u64 = 0 ;
165+ while let Some ( chunk) = response. chunk ( ) . await ? {
166+ streamed += chunk. len ( ) as u64 ;
167+
168+ if streamed > limit_bytes {
169+ let len_mb = streamed / 1_000_000 ;
170+ return Err ( ModZipError :: ModFileTooLarge ( len_mb, limit_mb. into ( ) ) ) ;
171+ }
172+
173+ data. extend_from_slice ( & chunk) ;
174+ }
175+
176+ Ok ( Bytes :: from ( data) )
155177}
0 commit comments