Skip to content

vc: verifyParsedCredential trusts caller-supplied fields on parsed-credential input #108

@EfeDurmaz16

Description

@EfeDurmaz16

Summary

verifyParsedCredential() verifies the JWT proof (proof.jwt) but does not reconcile the outer credential object against the decoded payload. On the parsed-credential input path, all subsequent checks therefore run against caller-supplied fields rather than the signed content.

Where

  • packages/vc/src/verification/verify-proof.tsverifyJwtProof() calls verifyCredential(proof.jwt, resolver) and discards the decoded result; it only confirms the proof JWT is validly signed.
  • packages/vc/src/verification/verify-parsed-credential.ts — expiry, revocation, the trustedIssuers check (reads credential.issuer.id), and every ClaimVerifier then operate on the outer object passed by the caller.

Impact

When a caller passes a pre-parsed Verifiable<W3CCredential> (rather than a JWT string), they can attach any validly-signed proof.jwt and freely set the outer credentialSubject, issuer, expiry, etc. Concretely:

  • Claim/subject fields are spoofable (this is the parsed-input gap raised in fix(ack-pay): validate receipt payment option #88 for paymentOptionId / paymentRequestToken).
  • The trustedIssuers check compares the caller-supplied credential.issuer.id, so it can be set to a trusted DID while proof.jwt is signed by an attacker's own (resolvable) DID.

The JWT-string input path is unaffected, since parseJwtCredential() reconstructs the credential from the signed payload.

Proposed fix

After verifyProof() validates a JWT proof, derive the credential fields from the decoded proof.jwt payload (or reject parsed credentials whose outer object does not match the signed payload), so downstream checks operate on signed content for every credential type. This is cross-cutting, so it warrants its own change + tests rather than being handled per-consumer.

Interim mitigation

#88 hardens the receipt path specifically by re-deriving receipt fields from proof.jwt when given a parsed credential. This issue tracks the general fix in @agentcommercekit/vc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions