@@ -4,6 +4,7 @@ use flate2::read::GzDecoder;
44use hex:: ToHex ;
55use sha2:: { Digest , Sha256 } ;
66use std:: io:: Read ;
7+ use std:: path:: Path ;
78use std:: sync:: Arc ;
89use swirl:: Job ;
910
@@ -17,7 +18,7 @@ use crate::models::{
1718use crate :: render;
1819use crate :: schema:: * ;
1920use crate :: util:: errors:: { cargo_err, AppResult } ;
20- use crate :: util:: { read_fill, read_le_u32, LimitErrorReader , Maximums } ;
21+ use crate :: util:: { read_fill, read_le_u32, CargoVcsInfo , LimitErrorReader , Maximums } ;
2122use crate :: views:: {
2223 EncodableCrate , EncodableCrateDependency , EncodableCrateUpload , GoodCrate , PublishWarnings ,
2324} ;
@@ -193,9 +194,8 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
193194 LimitErrorReader :: new ( req. body ( ) , maximums. max_upload_size ) . read_to_end ( & mut tarball) ?;
194195 let hex_cksum: String = Sha256 :: digest ( & tarball) . encode_hex ( ) ;
195196 let pkg_name = format ! ( "{}-{}" , krate. name, vers) ;
196- verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
197-
198- let pkg_path_in_vcs = None ;
197+ let cargo_vcs_info = verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
198+ let pkg_path_in_vcs = cargo_vcs_info. map ( |info| info. path_in_vcs ) ;
199199
200200 if let Some ( readme) = new_crate. readme {
201201 render:: render_and_upload_readme (
@@ -366,7 +366,11 @@ pub fn add_dependencies(
366366 Ok ( git_deps)
367367}
368368
369- fn verify_tarball ( pkg_name : & str , tarball : & [ u8 ] , max_unpack : u64 ) -> AppResult < ( ) > {
369+ fn verify_tarball (
370+ pkg_name : & str ,
371+ tarball : & [ u8 ] ,
372+ max_unpack : u64 ,
373+ ) -> AppResult < Option < CargoVcsInfo > > {
370374 // All our data is currently encoded with gzip
371375 let decoder = GzDecoder :: new ( tarball) ;
372376
@@ -376,8 +380,12 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
376380
377381 // Use this I/O object now to take a peek inside
378382 let mut archive = tar:: Archive :: new ( decoder) ;
383+
384+ let vcs_info_path = Path :: new ( & pkg_name) . join ( ".cargo_vcs_info.json" ) ;
385+ let mut vcs_info = None ;
386+
379387 for entry in archive. entries ( ) ? {
380- let entry = entry. map_err ( |err| {
388+ let mut entry = entry. map_err ( |err| {
381389 err. chain ( cargo_err (
382390 "uploaded tarball is malformed or too large when decompressed" ,
383391 ) )
@@ -388,9 +396,15 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
388396 // upload a tarball that contains both `foo-0.1.0/` source code as well
389397 // as `bar-0.1.0/` source code, and this could overwrite other crates in
390398 // the registry!
391- if !entry. path ( ) ?. starts_with ( & pkg_name) {
399+ let entry_path = entry. path ( ) ?;
400+ if !entry_path. starts_with ( & pkg_name) {
392401 return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
393402 }
403+ if entry_path == vcs_info_path {
404+ let mut contents = String :: new ( ) ;
405+ entry. read_to_string ( & mut contents) ?;
406+ vcs_info = CargoVcsInfo :: from_contents ( & contents) . ok ( ) ;
407+ }
394408
395409 // Historical versions of the `tar` crate which Cargo uses internally
396410 // don't properly prevent hard links and symlinks from overwriting
@@ -402,7 +416,7 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
402416 return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
403417 }
404418 }
405- Ok ( ( ) )
419+ Ok ( vcs_info )
406420}
407421
408422#[ cfg( test) ]
@@ -422,12 +436,51 @@ mod tests {
422436 #[ test]
423437 fn verify_tarball_test ( ) {
424438 let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
425- add_file ( & mut pkg, "foo-0.0.1/.cargo_vcs_info.json " , br#"{}"# ) ;
439+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml " , b"" ) ;
426440 let mut serialized_archive = vec ! [ ] ;
427441 GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
428442 . read_to_end ( & mut serialized_archive)
429443 . unwrap ( ) ;
430- verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap ( ) ;
444+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap ( ) ;
445+ assert ! ( vcs_info. is_none( ) ) ;
431446 verify_tarball ( "bar-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap_err ( ) ;
432447 }
448+
449+ #[ test]
450+ fn verify_tarball_test_incomplete_vcs_info ( ) {
451+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
452+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
453+ add_file (
454+ & mut pkg,
455+ "foo-0.0.1/.cargo_vcs_info.json" ,
456+ br#"{"unknown": "field"}"# ,
457+ ) ;
458+ let mut serialized_archive = vec ! [ ] ;
459+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
460+ . read_to_end ( & mut serialized_archive)
461+ . unwrap ( ) ;
462+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 )
463+ . unwrap ( )
464+ . unwrap ( ) ;
465+ assert_eq ! ( vcs_info. path_in_vcs, "" ) ;
466+ }
467+
468+ #[ test]
469+ fn verify_tarball_test_vcs_info ( ) {
470+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
471+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
472+ add_file (
473+ & mut pkg,
474+ "foo-0.0.1/.cargo_vcs_info.json" ,
475+ br#"{"path_in_vcs": "path/in/vcs"}"# ,
476+ ) ;
477+ let mut serialized_archive = vec ! [ ] ;
478+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
479+ . read_to_end ( & mut serialized_archive)
480+ . unwrap ( ) ;
481+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 )
482+ . unwrap ( )
483+ . unwrap ( ) ;
484+ assert_eq ! ( vcs_info. path_in_vcs, "path/in/vcs" ) ;
485+ }
433486}
0 commit comments