@@ -361,4 +361,65 @@ func AddStepsTo(sc *godog.ScenarioContext) {
361361 sc .Step (`^stub rekord running$` , stubRekordRunning )
362362 sc .Step (`^a valid Rekor entry for attestation of "([^"]*)"$` , RekorEntryForAttestation )
363363 sc .Step (`^a valid Rekor entry for image signature of "([^"]*)"$` , RekorEntryForImageSignature )
364+ sc .Step (`^VSA upload to Rekor should be expected$` , expectVSAUploadToRekor )
365+ sc .Step (`^VSA should be uploaded to Rekor successfully$` , vsaShouldBeUploadedToRekor )
366+ }
367+
368+ // expectVSAUploadToRekor creates WireMock stubs to expect VSA upload requests to Rekor
369+ func expectVSAUploadToRekor (ctx context.Context ) error {
370+ // Create a stub that accepts any VSA upload request to /api/v1/log/entries
371+ // and returns a successful Rekor entry response using the same format as existing stubs
372+ entryUUID := "24296fb24b8ad77a12345678901234567890abcd"
373+
374+ // Create a minimal valid base64 envelope for the response
375+ // This represents a basic in-toto attestation structure
376+ envelope := map [string ]interface {}{
377+ "payload" : "eyJzdWJqZWN0IjpudWxsLCJwcmVkaWNhdGUiOnsidmVyaWZpZWRMZXZlbHMiOltdLCJkZXBlbmRlbmN5TGV2ZWxzIjp7fSwidGltZVZlcmlmaWVkIjoiMjAyNC0wMS0wMVQwMDowMDowMFoifX0=" ,
378+ "payloadType" : "application/vnd.in-toto+json" ,
379+ "signatures" : []map [string ]string {
380+ {"sig" : "MEUCIQDexample123456789" , "keyid" : "" },
381+ },
382+ }
383+
384+ envelopeBytes , _ := json .Marshal (envelope )
385+ envelopeB64 := base64 .StdEncoding .EncodeToString (envelopeBytes )
386+
387+ response := map [string ]interface {}{
388+ entryUUID : map [string ]interface {}{
389+ "body" : envelopeB64 ,
390+ "integratedTime" : 1674049693 ,
391+ "logID" : "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d" ,
392+ "logIndex" : 9876543 ,
393+ "verification" : map [string ]interface {}{
394+ "signedEntryTimestamp" : "MEUCIQDexampleTimestamp123456789abcdefghijklmnopqrstuvwxyz==" ,
395+ },
396+ },
397+ }
398+
399+ responseBody , err := json .Marshal (response )
400+ if err != nil {
401+ return fmt .Errorf ("failed to marshal Rekor response: %w" , err )
402+ }
403+
404+ return wiremock .StubFor (ctx , wiremock .Post (wiremock .URLPathEqualTo ("/api/v1/log/entries" )).
405+ WillReturnResponse (wiremock .NewResponse ().
406+ WithStatus (201 ).
407+ WithHeaders (map [string ]string {
408+ "Content-Type" : "application/json" ,
409+ "Location" : fmt .Sprintf ("/api/v1/log/entries/%s" , entryUUID ),
410+ }).
411+ WithBody (string (responseBody ))))
412+ }
413+
414+ // vsaShouldBeUploadedToRekor verifies that VSA uploads to Rekor occurred successfully.
415+ // This relies on WireMock's automatic verification - if VSA uploads didn't happen or
416+ // didn't match our stub, WireMock will report unmatched requests/stubs in its After hook.
417+ func vsaShouldBeUploadedToRekor (ctx context.Context ) error {
418+ if ! wiremock .IsRunning (ctx ) {
419+ return fmt .Errorf ("WireMock is not running - cannot verify VSA uploads" )
420+ }
421+
422+ // WireMock automatically verifies that our expectVSAUploadToRekor stub was matched
423+ // by actual VSA upload requests. No explicit verification needed here.
424+ return nil
364425}
0 commit comments