33#[ cfg( unix) ]
44use std:: os:: unix:: fs:: PermissionsExt ;
55use std:: {
6- env,
76 io:: { self , prelude:: * } ,
87 path:: { Path , PathBuf } ,
98} ;
@@ -15,15 +14,18 @@ use same_file::Handle;
1514use time:: { OffsetDateTime , PrimitiveDateTime } ;
1615use zip:: { self , DateTime , ZipArchive , read:: ZipFile } ;
1716
17+ #[ cfg( unix) ]
18+ use crate :: utils:: sanitize_archive_mode;
1819use crate :: {
1920 Result ,
2021 error:: FinalError ,
2122 info, info_accessible,
2223 list:: { FileInArchive , ListFileType } ,
2324 utils:: {
24- BytesFmt , FileType , FileVisibilityPolicy , PathFmt , canonicalize, cd_into_same_dir_as, create_symlink,
25- ensure_parent_dir_exists, get_invalid_utf8_paths, is_same_file_as_output, pretty_format_list_of_paths,
26- read_file_type, strip_cur_dir,
25+ BytesFmt , FileType , FileVisibilityPolicy , LimitedReader , PathFmt , SanitizedStr , canonicalize,
26+ cd_into_same_dir_as, create_symlink, ensure_parent_dir_exists, get_invalid_utf8_paths, is_same_file_as_output,
27+ max_decompressed_bytes, pretty_format_list_of_paths, read_file_type, strip_cur_dir, validate_dest_inside_root,
28+ validate_symlink_target,
2729 } ,
2830 warning,
2931} ;
@@ -42,12 +44,17 @@ where
4244 Some ( password) => archive. by_index_decrypt ( idx, password) ?,
4345 None => archive. by_index ( idx) ?,
4446 } ;
45- let file_path = match file. enclosed_name ( ) {
47+ let relpath = match file. enclosed_name ( ) {
4648 Some ( path) => path. to_owned ( ) ,
47- None => continue ,
49+ None => {
50+ warning ! ( "skipping entry {} with unsafe name: {}" , idx, SanitizedStr ( file. name( ) ) ) ;
51+ continue ;
52+ }
4853 } ;
4954
50- let file_path = output_folder. join ( file_path) ;
55+ let file_path = output_folder. join ( & relpath) ;
56+
57+ validate_dest_inside_root ( output_folder, & file_path) ?;
5158
5259 display_zip_comment_if_exists ( & file) ;
5360
6269 let mut target = String :: new ( ) ;
6370 file. read_to_string ( & mut target) ?;
6471
72+ validate_symlink_target ( & relpath, std:: path:: Path :: new ( & target) ) ?;
6573 #[ cfg( unix) ]
6674 std:: os:: unix:: fs:: symlink ( & target, & file_path) ?;
6775 #[ cfg( windows) ]
@@ -81,12 +89,28 @@ where
8189 let mut target = String :: new ( ) ;
8290 file. read_to_string ( & mut target) ?;
8391
84- info ! ( "linking {} -> \" {}\" " , PathFmt ( file_path) , target) ;
92+ validate_symlink_target ( & relpath, std:: path:: Path :: new ( & target) ) ?;
93+ info ! ( "linking {} -> \" {}\" " , PathFmt ( file_path) , SanitizedStr ( & target) ) ;
8594
8695 create_symlink ( Path :: new ( & target) , file_path) ?;
8796 } else {
97+ #[ cfg( unix) ]
98+ let mut output_file = {
99+ use fs_err:: os:: unix:: fs:: OpenOptionsExt ;
100+ let mode = file. unix_mode ( ) . map ( sanitize_archive_mode) . unwrap_or ( 0o644 ) ;
101+ fs:: OpenOptions :: new ( )
102+ . write ( true )
103+ . create ( true )
104+ . truncate ( true )
105+ . mode ( mode)
106+ . open ( file_path) ?
107+ } ;
108+ #[ cfg( not( unix) ) ]
88109 let mut output_file = fs:: File :: create ( file_path) ?;
89- io:: copy ( & mut file, & mut output_file) ?;
110+ {
111+ let mut limited = LimitedReader :: new ( & mut file, max_decompressed_bytes ( ) ) ;
112+ io:: copy ( & mut limited, & mut output_file) ?;
113+ }
90114 set_last_modified_time ( & file, file_path) ?;
91115 #[ cfg( unix) ]
92116 unix_set_permissions ( file_path, & file) ?;
@@ -182,6 +206,7 @@ where
182206
183207 for explicit_path in input_filenames {
184208 let previous_location = cd_into_same_dir_as ( explicit_path) ?;
209+ let _cwd_guard = crate :: utils:: CwdGuard :: new ( previous_location) ;
185210
186211 // Unwrap safety:
187212 // paths should be canonicalized by now, and the root directory rejected.
@@ -255,8 +280,6 @@ where
255280 }
256281 }
257282 }
258-
259- env:: set_current_dir ( previous_location) ?;
260283 }
261284
262285 let bytes = writer. finish ( ) ?;
@@ -276,7 +299,11 @@ fn display_zip_comment_if_exists<R: Read>(file: &ZipFile<'_, R>) {
276299 // the future, maybe asking the user if he wants to display the comment
277300 // (informing him of its size) would be sensible for both normal and
278301 // accessibility mode..
279- info_accessible ! ( "Found comment in {}: {}" , file. name( ) , comment) ;
302+ info_accessible ! (
303+ "Found comment in {}: {}" ,
304+ SanitizedStr ( file. name( ) ) ,
305+ SanitizedStr ( comment)
306+ ) ;
280307 }
281308}
282309
@@ -315,7 +342,7 @@ fn unix_set_permissions<R: Read>(file_path: &Path, file: &ZipFile<'_, R>) -> Res
315342 use std:: fs:: Permissions ;
316343
317344 if let Some ( mode) = file. unix_mode ( ) {
318- fs:: set_permissions ( file_path, Permissions :: from_mode ( mode) ) ?;
345+ fs:: set_permissions ( file_path, Permissions :: from_mode ( sanitize_archive_mode ( mode) ) ) ?;
319346 }
320347
321348 Ok ( ( ) )
0 commit comments