From ae1faa5b9c0b42a5929ab79505b53bf74dfe7d2b Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Tue, 2 Jun 2026 12:49:48 +0200 Subject: [PATCH] fix(vcr/verifier): handle unparseable issuer DID instead of panicking The signature-check path discarded the error from did.ParseDID(issuer) and dereferenced the resulting nil pointer, crashing the node when a credential carried an unparseable issuer DID (e.g. a did:x509 with trailing unsupported fields, as emitted by the AET ZORG-ID SDK). Capture the parse error and return a clean validation error instead. Fixes #4235 Assisted by AI --- vcr/verifier/verifier.go | 5 ++++- vcr/verifier/verifier_test.go | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/vcr/verifier/verifier.go b/vcr/verifier/verifier.go index 59f833327e..2ad697e621 100644 --- a/vcr/verifier/verifier.go +++ b/vcr/verifier/verifier.go @@ -153,7 +153,10 @@ func (v verifier) Verify(credentialToVerify vc.VerifiableCredential, allowUntrus // Check signature if checkSignature { - issuerDID, _ := did.ParseDID(credentialToVerify.Issuer.String()) + issuerDID, err := did.ParseDID(credentialToVerify.Issuer.String()) + if err != nil { + return fmt.Errorf("could not parse issuer DID: %w", err) + } metadata := resolver.ResolveMetadata{ResolveTime: validAt, AllowDeactivated: false} rawJwt := credentialToVerify.Raw() if rawJwt != "" { diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index 3466d519e4..4d3bc4f7d9 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -122,6 +122,25 @@ func TestVerifier_Verify(t *testing.T) { }) }) + t.Run("fails (instead of panicking) when issuer DID is unparseable", func(t *testing.T) { + // Regression test for #4235: an unparseable issuer DID (e.g. a did:x509 with trailing + // unsupported fields) must result in a validation error, not a nil-pointer panic. + ctx := newMockContext(t) + credID := ssi.MustParseURI("did:web:example.com#1") + cred := vc.VerifiableCredential{ + Context: []ssi.URI{vc.VCContextV1URI()}, + Type: []ssi.URI{vc.VerifiableCredentialTypeV1URI(), ssi.MustParseURI("ExampleCredential")}, + ID: &credID, + Issuer: ssi.MustParseURI("did:x509:0:sha256:abc::san:otherName:1.2.3.4#extra"), + IssuanceDate: time.Now().Add(-time.Hour), + } + ctx.store.EXPECT().GetRevocations(credID).Return(nil, ErrNotFound) + + validationErr := ctx.verifier.Verify(cred, true, true, nil) + + assert.ErrorContains(t, validationErr, "could not parse issuer DID") + }) + t.Run("invalid when revoked", func(t *testing.T) { vc := testCredential(t) ctx := newMockContext(t)