@@ -5,6 +5,7 @@ use hex::ToHex;
55use sha2:: { Digest , Sha256 } ;
66use std:: collections:: HashMap ;
77use std:: io:: Read ;
8+ use std:: path:: Path ;
89use std:: sync:: Arc ;
910use swirl:: Job ;
1011
@@ -18,7 +19,7 @@ use crate::worker;
1819
1920use crate :: schema:: * ;
2021use crate :: util:: errors:: { cargo_err, AppResult } ;
21- use crate :: util:: { read_fill, read_le_u32, LimitErrorReader , Maximums } ;
22+ use crate :: util:: { read_fill, read_le_u32, CargoVcsInfo , LimitErrorReader , Maximums } ;
2223use crate :: views:: {
2324 EncodableCrate , EncodableCrateDependency , EncodableCrateUpload , GoodCrate , PublishWarnings ,
2425} ;
@@ -195,7 +196,8 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
195196 LimitErrorReader :: new ( req. body ( ) , maximums. max_upload_size ) . read_to_end ( & mut tarball) ?;
196197 let hex_cksum: String = Sha256 :: digest ( & tarball) . encode_hex ( ) ;
197198 let pkg_name = format ! ( "{}-{}" , krate. name, vers) ;
198- verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
199+ let cargo_vcs_info = verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
200+ let pkg_path_in_vcs = cargo_vcs_info. map ( |info| info. path_in_vcs ) ;
199201
200202 if let Some ( readme) = new_crate. readme {
201203 worker:: render_and_upload_readme (
@@ -205,6 +207,7 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
205207 . readme_file
206208 . unwrap_or_else ( || String :: from ( "README.md" ) ) ,
207209 repo,
210+ pkg_path_in_vcs,
208211 )
209212 . enqueue ( & conn) ?;
210213 }
@@ -379,7 +382,11 @@ pub fn add_dependencies(
379382 Ok ( git_deps)
380383}
381384
382- fn verify_tarball ( pkg_name : & str , tarball : & [ u8 ] , max_unpack : u64 ) -> AppResult < ( ) > {
385+ fn verify_tarball (
386+ pkg_name : & str ,
387+ tarball : & [ u8 ] ,
388+ max_unpack : u64 ,
389+ ) -> AppResult < Option < CargoVcsInfo > > {
383390 // All our data is currently encoded with gzip
384391 let decoder = GzDecoder :: new ( tarball) ;
385392
@@ -389,8 +396,12 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
389396
390397 // Use this I/O object now to take a peek inside
391398 let mut archive = tar:: Archive :: new ( decoder) ;
399+
400+ let vcs_info_path = Path :: new ( & pkg_name) . join ( ".cargo_vcs_info.json" ) ;
401+ let mut vcs_info = None ;
402+
392403 for entry in archive. entries ( ) ? {
393- let entry = entry. map_err ( |err| {
404+ let mut entry = entry. map_err ( |err| {
394405 err. chain ( cargo_err (
395406 "uploaded tarball is malformed or too large when decompressed" ,
396407 ) )
@@ -401,9 +412,15 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
401412 // upload a tarball that contains both `foo-0.1.0/` source code as well
402413 // as `bar-0.1.0/` source code, and this could overwrite other crates in
403414 // the registry!
404- if !entry. path ( ) ?. starts_with ( & pkg_name) {
415+ let entry_path = entry. path ( ) ?;
416+ if !entry_path. starts_with ( & pkg_name) {
405417 return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
406418 }
419+ if entry_path == vcs_info_path {
420+ let mut contents = String :: new ( ) ;
421+ entry. read_to_string ( & mut contents) ?;
422+ vcs_info = CargoVcsInfo :: from_contents ( & contents) . ok ( ) ;
423+ }
407424
408425 // Historical versions of the `tar` crate which Cargo uses internally
409426 // don't properly prevent hard links and symlinks from overwriting
@@ -415,7 +432,7 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
415432 return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
416433 }
417434 }
418- Ok ( ( ) )
435+ Ok ( vcs_info )
419436}
420437
421438#[ cfg( test) ]
@@ -435,14 +452,57 @@ mod tests {
435452 #[ test]
436453 fn verify_tarball_test ( ) {
437454 let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
438- add_file ( & mut pkg, "foo-0.0.1/.cargo_vcs_info.json " , br#"{}"# ) ;
455+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml " , b"" ) ;
439456 let mut serialized_archive = vec ! [ ] ;
440457 GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
441458 . read_to_end ( & mut serialized_archive)
442459 . unwrap ( ) ;
443460
444461 let limit = 512 * 1024 * 1024 ;
445- assert_ok ! ( verify_tarball( "foo-0.0.1" , & serialized_archive, limit) ) ;
462+ assert_eq ! (
463+ verify_tarball( "foo-0.0.1" , & serialized_archive, limit) . unwrap( ) ,
464+ None
465+ ) ;
446466 assert_err ! ( verify_tarball( "bar-0.0.1" , & serialized_archive, limit) ) ;
447467 }
468+
469+ #[ test]
470+ fn verify_tarball_test_incomplete_vcs_info ( ) {
471+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
472+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
473+ add_file (
474+ & mut pkg,
475+ "foo-0.0.1/.cargo_vcs_info.json" ,
476+ br#"{"unknown": "field"}"# ,
477+ ) ;
478+ let mut serialized_archive = vec ! [ ] ;
479+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
480+ . read_to_end ( & mut serialized_archive)
481+ . unwrap ( ) ;
482+ let limit = 512 * 1024 * 1024 ;
483+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, limit)
484+ . unwrap ( )
485+ . unwrap ( ) ;
486+ assert_eq ! ( vcs_info. path_in_vcs, "" ) ;
487+ }
488+
489+ #[ test]
490+ fn verify_tarball_test_vcs_info ( ) {
491+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
492+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
493+ add_file (
494+ & mut pkg,
495+ "foo-0.0.1/.cargo_vcs_info.json" ,
496+ br#"{"path_in_vcs": "path/in/vcs"}"# ,
497+ ) ;
498+ let mut serialized_archive = vec ! [ ] ;
499+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
500+ . read_to_end ( & mut serialized_archive)
501+ . unwrap ( ) ;
502+ let limit = 512 * 1024 * 1024 ;
503+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, limit)
504+ . unwrap ( )
505+ . unwrap ( ) ;
506+ assert_eq ! ( vcs_info. path_in_vcs, "path/in/vcs" ) ;
507+ }
448508}
0 commit comments