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,17 @@ use same_file::Handle;
1514use time:: OffsetDateTime ;
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 , PathFmt , SanitizedStr , canonicalize , cd_into_same_dir_as ,
26+ create_symlink , ensure_parent_dir_exists, get_invalid_utf8_paths, is_same_file_as_output,
27+ pretty_format_list_of_paths , read_file_type, strip_cur_dir, validate_symlink_target ,
2728 } ,
2829 warning,
2930} ;
@@ -42,12 +43,15 @@ where
4243 Some ( password) => archive. by_index_decrypt ( idx, password) ?,
4344 None => archive. by_index ( idx) ?,
4445 } ;
45- let file_path = match file. enclosed_name ( ) {
46+ let relpath = match file. enclosed_name ( ) {
4647 Some ( path) => path. to_owned ( ) ,
47- None => continue ,
48+ None => {
49+ warning ! ( "skipping entry {} with unsafe name: {}" , idx, SanitizedStr ( file. name( ) ) ) ;
50+ continue ;
51+ }
4852 } ;
4953
50- let file_path = output_folder. join ( file_path ) ;
54+ let file_path = output_folder. join ( & relpath ) ;
5155
5256 display_zip_comment_if_exists ( & file) ;
5357
6266 let mut target = String :: new ( ) ;
6367 file. read_to_string ( & mut target) ?;
6468
69+ validate_symlink_target ( & relpath, std:: path:: Path :: new ( & target) ) ?;
6570 #[ cfg( unix) ]
6671 std:: os:: unix:: fs:: symlink ( & target, & file_path) ?;
6772 #[ cfg( windows) ]
@@ -81,10 +86,23 @@ where
8186 let mut target = String :: new ( ) ;
8287 file. read_to_string ( & mut target) ?;
8388
84- info ! ( "linking {} -> \" {}\" " , PathFmt ( file_path) , target) ;
89+ validate_symlink_target ( & relpath, std:: path:: Path :: new ( & target) ) ?;
90+ info ! ( "linking {} -> \" {}\" " , PathFmt ( file_path) , SanitizedStr ( & target) ) ;
8591
8692 create_symlink ( Path :: new ( & target) , file_path) ?;
8793 } else {
94+ #[ cfg( unix) ]
95+ let mut output_file = {
96+ use fs_err:: os:: unix:: fs:: OpenOptionsExt ;
97+ let mode = file. unix_mode ( ) . map ( sanitize_archive_mode) . unwrap_or ( 0o644 ) ;
98+ fs:: OpenOptions :: new ( )
99+ . write ( true )
100+ . create ( true )
101+ . truncate ( true )
102+ . mode ( mode)
103+ . open ( file_path) ?
104+ } ;
105+ #[ cfg( not( unix) ) ]
88106 let mut output_file = fs:: File :: create ( file_path) ?;
89107 io:: copy ( & mut file, & mut output_file) ?;
90108 set_last_modified_time ( & file, file_path) ?;
@@ -182,6 +200,7 @@ where
182200
183201 for explicit_path in input_filenames {
184202 let previous_location = cd_into_same_dir_as ( explicit_path) ?;
203+ let _cwd_guard = crate :: utils:: CwdGuard :: new ( previous_location) ;
185204
186205 // Unwrap safety:
187206 // paths should be canonicalized by now, and the root directory rejected.
@@ -255,8 +274,6 @@ where
255274 }
256275 }
257276 }
258-
259- env:: set_current_dir ( previous_location) ?;
260277 }
261278
262279 let bytes = writer. finish ( ) ?;
@@ -276,7 +293,11 @@ fn display_zip_comment_if_exists<R: Read>(file: &ZipFile<'_, R>) {
276293 // the future, maybe asking the user if he wants to display the comment
277294 // (informing him of its size) would be sensible for both normal and
278295 // accessibility mode..
279- info_accessible ! ( "Found comment in {}: {}" , file. name( ) , comment) ;
296+ info_accessible ! (
297+ "Found comment in {}: {}" ,
298+ SanitizedStr ( file. name( ) ) ,
299+ SanitizedStr ( comment)
300+ ) ;
280301 }
281302}
282303
@@ -311,7 +332,7 @@ fn unix_set_permissions<R: Read>(file_path: &Path, file: &ZipFile<'_, R>) -> Res
311332 use std:: fs:: Permissions ;
312333
313334 if let Some ( mode) = file. unix_mode ( ) {
314- fs:: set_permissions ( file_path, Permissions :: from_mode ( mode) ) ?;
335+ fs:: set_permissions ( file_path, Permissions :: from_mode ( sanitize_archive_mode ( mode) ) ) ?;
315336 }
316337
317338 Ok ( ( ) )
0 commit comments