@@ -5,6 +5,7 @@ use hex::ToHex;
5
5
use sha2:: { Digest , Sha256 } ;
6
6
use std:: collections:: HashMap ;
7
7
use std:: io:: Read ;
8
+ use std:: path:: Path ;
8
9
use std:: sync:: Arc ;
9
10
use swirl:: Job ;
10
11
@@ -18,7 +19,7 @@ use crate::worker;
18
19
19
20
use crate :: schema:: * ;
20
21
use 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 } ;
22
23
use crate :: views:: {
23
24
EncodableCrate , EncodableCrateDependency , EncodableCrateUpload , GoodCrate , PublishWarnings ,
24
25
} ;
@@ -195,9 +196,8 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
195
196
LimitErrorReader :: new ( req. body ( ) , maximums. max_upload_size ) . read_to_end ( & mut tarball) ?;
196
197
let hex_cksum: String = Sha256 :: digest ( & tarball) . encode_hex ( ) ;
197
198
let pkg_name = format ! ( "{}-{}" , krate. name, vers) ;
198
- verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
199
-
200
- let pkg_path_in_vcs = None ;
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 ) ;
201
201
202
202
if let Some ( readme) = new_crate. readme {
203
203
worker:: render_and_upload_readme (
@@ -382,7 +382,11 @@ pub fn add_dependencies(
382
382
Ok ( git_deps)
383
383
}
384
384
385
- 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 > > {
386
390
// All our data is currently encoded with gzip
387
391
let decoder = GzDecoder :: new ( tarball) ;
388
392
@@ -392,8 +396,12 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
392
396
393
397
// Use this I/O object now to take a peek inside
394
398
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
+
395
403
for entry in archive. entries ( ) ? {
396
- let entry = entry. map_err ( |err| {
404
+ let mut entry = entry. map_err ( |err| {
397
405
err. chain ( cargo_err (
398
406
"uploaded tarball is malformed or too large when decompressed" ,
399
407
) )
@@ -404,9 +412,15 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
404
412
// upload a tarball that contains both `foo-0.1.0/` source code as well
405
413
// as `bar-0.1.0/` source code, and this could overwrite other crates in
406
414
// the registry!
407
- if !entry. path ( ) ?. starts_with ( & pkg_name) {
415
+ let entry_path = entry. path ( ) ?;
416
+ if !entry_path. starts_with ( & pkg_name) {
408
417
return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
409
418
}
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
+ }
410
424
411
425
// Historical versions of the `tar` crate which Cargo uses internally
412
426
// don't properly prevent hard links and symlinks from overwriting
@@ -418,7 +432,7 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
418
432
return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
419
433
}
420
434
}
421
- Ok ( ( ) )
435
+ Ok ( vcs_info )
422
436
}
423
437
424
438
#[ cfg( test) ]
@@ -438,14 +452,57 @@ mod tests {
438
452
#[ test]
439
453
fn verify_tarball_test ( ) {
440
454
let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
441
- 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"" ) ;
442
456
let mut serialized_archive = vec ! [ ] ;
443
457
GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
444
458
. read_to_end ( & mut serialized_archive)
445
459
. unwrap ( ) ;
446
460
447
461
let limit = 512 * 1024 * 1024 ;
448
- 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
+ ) ;
449
466
assert_err ! ( verify_tarball( "bar-0.0.1" , & serialized_archive, limit) ) ;
450
467
}
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
+ }
451
508
}
0 commit comments