@@ -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::models::{
17
18
use crate :: render;
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
render:: 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,12 +437,51 @@ 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
- verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap ( ) ;
445
+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap ( ) ;
446
+ assert ! ( vcs_info. is_none( ) ) ;
432
447
verify_tarball ( "bar-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap_err ( ) ;
433
448
}
449
+
450
+ #[ test]
451
+ fn verify_tarball_test_incomplete_vcs_info ( ) {
452
+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
453
+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
454
+ add_file (
455
+ & mut pkg,
456
+ "foo-0.0.1/.cargo_vcs_info.json" ,
457
+ br#"{"unknown": "field"}"# ,
458
+ ) ;
459
+ let mut serialized_archive = vec ! [ ] ;
460
+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
461
+ . read_to_end ( & mut serialized_archive)
462
+ . unwrap ( ) ;
463
+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 )
464
+ . unwrap ( )
465
+ . unwrap ( ) ;
466
+ assert_eq ! ( vcs_info. path_in_vcs, "" ) ;
467
+ }
468
+
469
+ #[ test]
470
+ fn verify_tarball_test_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#"{"path_in_vcs": "path/in/vcs"}"# ,
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 vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 )
483
+ . unwrap ( )
484
+ . unwrap ( ) ;
485
+ assert_eq ! ( vcs_info. path_in_vcs, "path/in/vcs" ) ;
486
+ }
434
487
}
0 commit comments