@@ -3446,7 +3446,12 @@ def test_upload_with_valid_attestation_succeeds(
3446
3446
)
3447
3447
monkeypatch .setattr (HasEvents , "record_event" , record_event )
3448
3448
3449
- verify = pretend .call_recorder (lambda _self , _verifier , _policy , _dist : None )
3449
+ verify = pretend .call_recorder (
3450
+ lambda _self , _verifier , _policy , _dist : (
3451
+ "https://docs.pypi.org/attestations/publish/v1" ,
3452
+ None ,
3453
+ )
3454
+ )
3450
3455
monkeypatch .setattr (Attestation , "verify" , verify )
3451
3456
monkeypatch .setattr (Verifier , "production" , lambda : pretend .stub ())
3452
3457
@@ -3456,6 +3461,177 @@ def test_upload_with_valid_attestation_succeeds(
3456
3461
3457
3462
assert len (verify .calls ) == 1
3458
3463
3464
+ def test_upload_with_invalid_attestation_predicate_type_fails (
3465
+ self ,
3466
+ monkeypatch ,
3467
+ pyramid_config ,
3468
+ db_request ,
3469
+ metrics ,
3470
+ ):
3471
+ from warehouse .events .models import HasEvents
3472
+
3473
+ project = ProjectFactory .create ()
3474
+ version = "1.0"
3475
+ publisher = GitHubPublisherFactory .create (projects = [project ])
3476
+ claims = {
3477
+ "sha" : "somesha" ,
3478
+ "repository" : f"{ publisher .repository_owner } /{ publisher .repository_name } " ,
3479
+ "workflow" : "workflow_name" ,
3480
+ }
3481
+ identity = PublisherTokenContext (publisher , SignedClaims (claims ))
3482
+ db_request .oidc_publisher = identity .publisher
3483
+ db_request .oidc_claims = identity .claims
3484
+
3485
+ db_request .db .add (Classifier (classifier = "Environment :: Other Environment" ))
3486
+ db_request .db .add (Classifier (classifier = "Programming Language :: Python" ))
3487
+
3488
+ filename = "{}-{}.tar.gz" .format (project .name , "1.0" )
3489
+ attestation = Attestation (
3490
+ version = 1 ,
3491
+ verification_material = VerificationMaterial (
3492
+ certificate = "somebase64string" , transparency_entries = [dict ()]
3493
+ ),
3494
+ envelope = Envelope (
3495
+ statement = "somebase64string" ,
3496
+ signature = "somebase64string" ,
3497
+ ),
3498
+ )
3499
+
3500
+ pyramid_config .testing_securitypolicy (identity = identity )
3501
+ db_request .user = None
3502
+ db_request .user_agent = "warehouse-tests/6.6.6"
3503
+ db_request .POST = MultiDict (
3504
+ {
3505
+ "metadata_version" : "1.2" ,
3506
+ "name" : project .name ,
3507
+ "attestations" : f"[{ attestation .model_dump_json ()} ]" ,
3508
+ "version" : version ,
3509
+ "summary" : "This is my summary!" ,
3510
+ "filetype" : "sdist" ,
3511
+ "md5_digest" : _TAR_GZ_PKG_MD5 ,
3512
+ "content" : pretend .stub (
3513
+ filename = filename ,
3514
+ file = io .BytesIO (_TAR_GZ_PKG_TESTDATA ),
3515
+ type = "application/tar" ,
3516
+ ),
3517
+ }
3518
+ )
3519
+
3520
+ storage_service = pretend .stub (store = lambda path , filepath , meta : None )
3521
+ db_request .find_service = lambda svc , name = None , context = None : {
3522
+ IFileStorage : storage_service ,
3523
+ IMetricsService : metrics ,
3524
+ }.get (svc )
3525
+
3526
+ record_event = pretend .call_recorder (
3527
+ lambda self , * , tag , request = None , additional : None
3528
+ )
3529
+ monkeypatch .setattr (HasEvents , "record_event" , record_event )
3530
+
3531
+ invalid_predicate_type = "Unsupported predicate type"
3532
+ verify = pretend .call_recorder (
3533
+ lambda _self , _verifier , _policy , _dist : (invalid_predicate_type , None )
3534
+ )
3535
+ monkeypatch .setattr (Attestation , "verify" , verify )
3536
+ monkeypatch .setattr (Verifier , "production" , lambda : pretend .stub ())
3537
+
3538
+ with pytest .raises (HTTPBadRequest ) as excinfo :
3539
+ legacy .file_upload (db_request )
3540
+
3541
+ resp = excinfo .value
3542
+
3543
+ assert resp .status_code == 400
3544
+ assert resp .status .startswith (
3545
+ f"400 Attestation with unsupported predicate type: { invalid_predicate_type } "
3546
+ )
3547
+
3548
+ def test_upload_with_multiple_attestations_fails (
3549
+ self ,
3550
+ monkeypatch ,
3551
+ pyramid_config ,
3552
+ db_request ,
3553
+ metrics ,
3554
+ ):
3555
+ from warehouse .events .models import HasEvents
3556
+
3557
+ project = ProjectFactory .create ()
3558
+ version = "1.0"
3559
+ publisher = GitHubPublisherFactory .create (projects = [project ])
3560
+ claims = {
3561
+ "sha" : "somesha" ,
3562
+ "repository" : f"{ publisher .repository_owner } /{ publisher .repository_name } " ,
3563
+ "workflow" : "workflow_name" ,
3564
+ }
3565
+ identity = PublisherTokenContext (publisher , SignedClaims (claims ))
3566
+ db_request .oidc_publisher = identity .publisher
3567
+ db_request .oidc_claims = identity .claims
3568
+
3569
+ db_request .db .add (Classifier (classifier = "Environment :: Other Environment" ))
3570
+ db_request .db .add (Classifier (classifier = "Programming Language :: Python" ))
3571
+
3572
+ filename = "{}-{}.tar.gz" .format (project .name , "1.0" )
3573
+ attestation = Attestation (
3574
+ version = 1 ,
3575
+ verification_material = VerificationMaterial (
3576
+ certificate = "somebase64string" , transparency_entries = [dict ()]
3577
+ ),
3578
+ envelope = Envelope (
3579
+ statement = "somebase64string" ,
3580
+ signature = "somebase64string" ,
3581
+ ),
3582
+ )
3583
+
3584
+ pyramid_config .testing_securitypolicy (identity = identity )
3585
+ db_request .user = None
3586
+ db_request .user_agent = "warehouse-tests/6.6.6"
3587
+ db_request .POST = MultiDict (
3588
+ {
3589
+ "metadata_version" : "1.2" ,
3590
+ "name" : project .name ,
3591
+ "attestations" : f"[{ attestation .model_dump_json ()} ,"
3592
+ f" { attestation .model_dump_json ()} ]" ,
3593
+ "version" : version ,
3594
+ "summary" : "This is my summary!" ,
3595
+ "filetype" : "sdist" ,
3596
+ "md5_digest" : _TAR_GZ_PKG_MD5 ,
3597
+ "content" : pretend .stub (
3598
+ filename = filename ,
3599
+ file = io .BytesIO (_TAR_GZ_PKG_TESTDATA ),
3600
+ type = "application/tar" ,
3601
+ ),
3602
+ }
3603
+ )
3604
+
3605
+ storage_service = pretend .stub (store = lambda path , filepath , meta : None )
3606
+ db_request .find_service = lambda svc , name = None , context = None : {
3607
+ IFileStorage : storage_service ,
3608
+ IMetricsService : metrics ,
3609
+ }.get (svc )
3610
+
3611
+ record_event = pretend .call_recorder (
3612
+ lambda self , * , tag , request = None , additional : None
3613
+ )
3614
+ monkeypatch .setattr (HasEvents , "record_event" , record_event )
3615
+
3616
+ verify = pretend .call_recorder (
3617
+ lambda _self , _verifier , _policy , _dist : (
3618
+ "https://docs.pypi.org/attestations/publish/v1" ,
3619
+ None ,
3620
+ )
3621
+ )
3622
+ monkeypatch .setattr (Attestation , "verify" , verify )
3623
+ monkeypatch .setattr (Verifier , "production" , lambda : pretend .stub ())
3624
+
3625
+ with pytest .raises (HTTPBadRequest ) as excinfo :
3626
+ legacy .file_upload (db_request )
3627
+
3628
+ resp = excinfo .value
3629
+
3630
+ assert resp .status_code == 400
3631
+ assert resp .status .startswith (
3632
+ "400 Only a single attestation per-file is supported at the moment."
3633
+ )
3634
+
3459
3635
def test_upload_with_malformed_attestation_fails (
3460
3636
self ,
3461
3637
monkeypatch ,
0 commit comments