@@ -4,6 +4,7 @@ use flate2::read::GzDecoder;
4
4
use hex:: ToHex ;
5
5
use sha2:: { Digest , Sha256 } ;
6
6
use std:: io:: Read ;
7
+ use std:: path:: Path ;
7
8
use std:: sync:: Arc ;
8
9
use swirl:: Job ;
9
10
@@ -17,7 +18,7 @@ use crate::worker;
17
18
18
19
use crate :: schema:: * ;
19
20
use 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 } ;
21
22
use crate :: views:: {
22
23
EncodableCrate , EncodableCrateDependency , EncodableCrateUpload , GoodCrate , PublishWarnings ,
23
24
} ;
@@ -194,9 +195,8 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
194
195
LimitErrorReader :: new ( req. body ( ) , maximums. max_upload_size ) . read_to_end ( & mut tarball) ?;
195
196
let hex_cksum: String = Sha256 :: digest ( & tarball) . encode_hex ( ) ;
196
197
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 ) ;
200
200
201
201
if let Some ( readme) = new_crate. readme {
202
202
worker:: render_and_upload_readme (
@@ -367,7 +367,11 @@ pub fn add_dependencies(
367
367
Ok ( git_deps)
368
368
}
369
369
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 > > {
371
375
// All our data is currently encoded with gzip
372
376
let decoder = GzDecoder :: new ( tarball) ;
373
377
@@ -377,8 +381,12 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
377
381
378
382
// Use this I/O object now to take a peek inside
379
383
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
+
380
388
for entry in archive. entries ( ) ? {
381
- let entry = entry. map_err ( |err| {
389
+ let mut entry = entry. map_err ( |err| {
382
390
err. chain ( cargo_err (
383
391
"uploaded tarball is malformed or too large when decompressed" ,
384
392
) )
@@ -389,9 +397,15 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
389
397
// upload a tarball that contains both `foo-0.1.0/` source code as well
390
398
// as `bar-0.1.0/` source code, and this could overwrite other crates in
391
399
// the registry!
392
- if !entry. path ( ) ?. starts_with ( & pkg_name) {
400
+ let entry_path = entry. path ( ) ?;
401
+ if !entry_path. starts_with ( & pkg_name) {
393
402
return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
394
403
}
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
+ }
395
409
396
410
// Historical versions of the `tar` crate which Cargo uses internally
397
411
// 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<
403
417
return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
404
418
}
405
419
}
406
- Ok ( ( ) )
420
+ Ok ( vcs_info )
407
421
}
408
422
409
423
#[ cfg( test) ]
@@ -423,14 +437,57 @@ mod tests {
423
437
#[ test]
424
438
fn verify_tarball_test ( ) {
425
439
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"" ) ;
427
441
let mut serialized_archive = vec ! [ ] ;
428
442
GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
429
443
. read_to_end ( & mut serialized_archive)
430
444
. unwrap ( ) ;
431
445
432
446
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
+ ) ;
434
451
assert_err ! ( verify_tarball( "bar-0.0.1" , & serialized_archive, limit) ) ;
435
452
}
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
+ }
436
493
}
0 commit comments