@@ -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} ;
@@ -194,9 +195,8 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
194195 LimitErrorReader :: new ( req. body ( ) , maximums. max_upload_size ) . read_to_end ( & mut tarball) ?;
195196 let hex_cksum: String = Sha256 :: digest ( & tarball) . encode_hex ( ) ;
196197 let pkg_name = format ! ( "{}-{}" , krate. name, vers) ;
197- verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
198-
199- let pkg_path_in_vcs = None ;
198+ let cargo_vcs_info = verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
199+ let pkg_path_in_vcs = cargo_vcs_info. map ( |info| info. path_in_vcs ) ;
200200
201201 if let Some ( readme) = new_crate. readme {
202202 render:: render_and_upload_readme (
@@ -367,7 +367,11 @@ pub fn add_dependencies(
367367 Ok ( git_deps)
368368}
369369
370- fn verify_tarball ( pkg_name : & str , tarball : & [ u8 ] , max_unpack : u64 ) -> AppResult < ( ) > {
370+ fn verify_tarball (
371+ pkg_name : & str ,
372+ tarball : & [ u8 ] ,
373+ max_unpack : u64 ,
374+ ) -> AppResult < Option < CargoVcsInfo > > {
371375 // All our data is currently encoded with gzip
372376 let decoder = GzDecoder :: new ( tarball) ;
373377
@@ -377,8 +381,12 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
377381
378382 // Use this I/O object now to take a peek inside
379383 let mut archive = tar:: Archive :: new ( decoder) ;
384+
385+ let vcs_info_path = Path :: new ( & pkg_name) . join ( ".cargo_vcs_info.json" ) ;
386+ let mut vcs_info = None ;
387+
380388 for entry in archive. entries ( ) ? {
381- let entry = entry. map_err ( |err| {
389+ let mut entry = entry. map_err ( |err| {
382390 err. chain ( cargo_err (
383391 "uploaded tarball is malformed or too large when decompressed" ,
384392 ) )
@@ -389,9 +397,15 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
389397 // upload a tarball that contains both `foo-0.1.0/` source code as well
390398 // as `bar-0.1.0/` source code, and this could overwrite other crates in
391399 // the registry!
392- if !entry. path ( ) ?. starts_with ( & pkg_name) {
400+ let entry_path = entry. path ( ) ?;
401+ if !entry_path. starts_with ( & pkg_name) {
393402 return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
394403 }
404+ if entry_path == vcs_info_path {
405+ let mut contents = String :: new ( ) ;
406+ entry. read_to_string ( & mut contents) ?;
407+ vcs_info = CargoVcsInfo :: from_contents ( & contents) . ok ( ) ;
408+ }
395409
396410 // Historical versions of the `tar` crate which Cargo uses internally
397411 // don't properly prevent hard links and symlinks from overwriting
@@ -403,7 +417,7 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
403417 return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
404418 }
405419 }
406- Ok ( ( ) )
420+ Ok ( vcs_info )
407421}
408422
409423#[ cfg( test) ]
@@ -423,14 +437,57 @@ mod tests {
423437 #[ test]
424438 fn verify_tarball_test ( ) {
425439 let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
426- add_file ( & mut pkg, "foo-0.0.1/.cargo_vcs_info.json " , br#"{}"# ) ;
440+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml " , b"" ) ;
427441 let mut serialized_archive = vec ! [ ] ;
428442 GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
429443 . read_to_end ( & mut serialized_archive)
430444 . unwrap ( ) ;
431445
432446 let limit = 512 * 1024 * 1024 ;
433- assert_ok ! ( verify_tarball( "foo-0.0.1" , & serialized_archive, limit) ) ;
447+ assert_eq ! (
448+ verify_tarball( "foo-0.0.1" , & serialized_archive, limit) . unwrap( ) ,
449+ None
450+ ) ;
434451 assert_err ! ( verify_tarball( "bar-0.0.1" , & serialized_archive, limit) ) ;
435452 }
453+
454+ #[ test]
455+ fn verify_tarball_test_incomplete_vcs_info ( ) {
456+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
457+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
458+ add_file (
459+ & mut pkg,
460+ "foo-0.0.1/.cargo_vcs_info.json" ,
461+ br#"{"unknown": "field"}"# ,
462+ ) ;
463+ let mut serialized_archive = vec ! [ ] ;
464+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
465+ . read_to_end ( & mut serialized_archive)
466+ . unwrap ( ) ;
467+ let limit = 512 * 1024 * 1024 ;
468+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, limit)
469+ . unwrap ( )
470+ . unwrap ( ) ;
471+ assert_eq ! ( vcs_info. path_in_vcs, "" ) ;
472+ }
473+
474+ #[ test]
475+ fn verify_tarball_test_vcs_info ( ) {
476+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
477+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
478+ add_file (
479+ & mut pkg,
480+ "foo-0.0.1/.cargo_vcs_info.json" ,
481+ br#"{"path_in_vcs": "path/in/vcs"}"# ,
482+ ) ;
483+ let mut serialized_archive = vec ! [ ] ;
484+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
485+ . read_to_end ( & mut serialized_archive)
486+ . unwrap ( ) ;
487+ let limit = 512 * 1024 * 1024 ;
488+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, limit)
489+ . unwrap ( )
490+ . unwrap ( ) ;
491+ assert_eq ! ( vcs_info. path_in_vcs, "path/in/vcs" ) ;
492+ }
436493}
0 commit comments