@@ -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
} ;
@@ -193,9 +194,8 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
193
194
LimitErrorReader :: new ( req. body ( ) , maximums. max_upload_size ) . read_to_end ( & mut tarball) ?;
194
195
let hex_cksum: String = Sha256 :: digest ( & tarball) . encode_hex ( ) ;
195
196
let pkg_name = format ! ( "{}-{}" , krate. name, vers) ;
196
- verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
197
-
198
- let pkg_path_in_vcs = None ;
197
+ let cargo_vcs_info = verify_tarball ( & pkg_name, & tarball, maximums. max_unpack_size ) ?;
198
+ let pkg_path_in_vcs = cargo_vcs_info. map ( |info| info. path_in_vcs ) ;
199
199
200
200
if let Some ( readme) = new_crate. readme {
201
201
render:: render_and_upload_readme (
@@ -366,7 +366,11 @@ pub fn add_dependencies(
366
366
Ok ( git_deps)
367
367
}
368
368
369
- fn verify_tarball ( pkg_name : & str , tarball : & [ u8 ] , max_unpack : u64 ) -> AppResult < ( ) > {
369
+ fn verify_tarball (
370
+ pkg_name : & str ,
371
+ tarball : & [ u8 ] ,
372
+ max_unpack : u64 ,
373
+ ) -> AppResult < Option < CargoVcsInfo > > {
370
374
// All our data is currently encoded with gzip
371
375
let decoder = GzDecoder :: new ( tarball) ;
372
376
@@ -376,8 +380,12 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
376
380
377
381
// Use this I/O object now to take a peek inside
378
382
let mut archive = tar:: Archive :: new ( decoder) ;
383
+
384
+ let vcs_info_path = Path :: new ( & pkg_name) . join ( ".cargo_vcs_info.json" ) ;
385
+ let mut vcs_info = None ;
386
+
379
387
for entry in archive. entries ( ) ? {
380
- let entry = entry. map_err ( |err| {
388
+ let mut entry = entry. map_err ( |err| {
381
389
err. chain ( cargo_err (
382
390
"uploaded tarball is malformed or too large when decompressed" ,
383
391
) )
@@ -388,9 +396,15 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
388
396
// upload a tarball that contains both `foo-0.1.0/` source code as well
389
397
// as `bar-0.1.0/` source code, and this could overwrite other crates in
390
398
// the registry!
391
- if !entry. path ( ) ?. starts_with ( & pkg_name) {
399
+ let entry_path = entry. path ( ) ?;
400
+ if !entry_path. starts_with ( & pkg_name) {
392
401
return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
393
402
}
403
+ if entry_path == vcs_info_path {
404
+ let mut contents = String :: new ( ) ;
405
+ entry. read_to_string ( & mut contents) ?;
406
+ vcs_info = CargoVcsInfo :: from_contents ( & contents) . ok ( ) ;
407
+ }
394
408
395
409
// Historical versions of the `tar` crate which Cargo uses internally
396
410
// don't properly prevent hard links and symlinks from overwriting
@@ -402,7 +416,7 @@ fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<
402
416
return Err ( cargo_err ( "invalid tarball uploaded" ) ) ;
403
417
}
404
418
}
405
- Ok ( ( ) )
419
+ Ok ( vcs_info )
406
420
}
407
421
408
422
#[ cfg( test) ]
@@ -422,12 +436,51 @@ mod tests {
422
436
#[ test]
423
437
fn verify_tarball_test ( ) {
424
438
let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
425
- add_file ( & mut pkg, "foo-0.0.1/.cargo_vcs_info.json " , br#"{}"# ) ;
439
+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml " , b"" ) ;
426
440
let mut serialized_archive = vec ! [ ] ;
427
441
GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
428
442
. read_to_end ( & mut serialized_archive)
429
443
. unwrap ( ) ;
430
- verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap ( ) ;
444
+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap ( ) ;
445
+ assert ! ( vcs_info. is_none( ) ) ;
431
446
verify_tarball ( "bar-0.0.1" , & serialized_archive, 512 * 1024 * 1024 ) . unwrap_err ( ) ;
432
447
}
448
+
449
+ #[ test]
450
+ fn verify_tarball_test_incomplete_vcs_info ( ) {
451
+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
452
+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
453
+ add_file (
454
+ & mut pkg,
455
+ "foo-0.0.1/.cargo_vcs_info.json" ,
456
+ br#"{"unknown": "field"}"# ,
457
+ ) ;
458
+ let mut serialized_archive = vec ! [ ] ;
459
+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
460
+ . read_to_end ( & mut serialized_archive)
461
+ . unwrap ( ) ;
462
+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 )
463
+ . unwrap ( )
464
+ . unwrap ( ) ;
465
+ assert_eq ! ( vcs_info. path_in_vcs, "" ) ;
466
+ }
467
+
468
+ #[ test]
469
+ fn verify_tarball_test_vcs_info ( ) {
470
+ let mut pkg = tar:: Builder :: new ( vec ! [ ] ) ;
471
+ add_file ( & mut pkg, "foo-0.0.1/Cargo.toml" , b"" ) ;
472
+ add_file (
473
+ & mut pkg,
474
+ "foo-0.0.1/.cargo_vcs_info.json" ,
475
+ br#"{"path_in_vcs": "path/in/vcs"}"# ,
476
+ ) ;
477
+ let mut serialized_archive = vec ! [ ] ;
478
+ GzEncoder :: new ( pkg. into_inner ( ) . unwrap ( ) . as_slice ( ) , Default :: default ( ) )
479
+ . read_to_end ( & mut serialized_archive)
480
+ . unwrap ( ) ;
481
+ let vcs_info = verify_tarball ( "foo-0.0.1" , & serialized_archive, 512 * 1024 * 1024 )
482
+ . unwrap ( )
483
+ . unwrap ( ) ;
484
+ assert_eq ! ( vcs_info. path_in_vcs, "path/in/vcs" ) ;
485
+ }
433
486
}
0 commit comments