From a06091a8cedc900ae78832622622058b7b653792 Mon Sep 17 00:00:00 2001 From: Rishabh Singh Date: Tue, 17 Jun 2025 08:25:27 +0530 Subject: [PATCH 1/4] fix: add w3c credential status check --- package-lock.json | 8 +-- package.json | 2 +- src/utils/fragment/index.ts | 55 ++++++++++++++++++- .../document-status/w3cCredentialStatus.ts | 37 ++++++++++++- 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index b680742..3f80ffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@tradetrust-tt/token-registry-v4": "npm:@tradetrust-tt/token-registry@^4.16.0", "@tradetrust-tt/token-registry-v5": "npm:@tradetrust-tt/token-registry@^5.3.0", "@tradetrust-tt/tradetrust": "^6.10.1", - "@tradetrust-tt/tradetrust-utils": "^2.3.1", + "@tradetrust-tt/tradetrust-utils": "^2.3.2", "@tradetrust-tt/tt-verify": "^9.4.0", "@trustvc/w3c-context": "^1.2.13", "@trustvc/w3c-credential-status": "^1.2.12", @@ -5188,9 +5188,9 @@ } }, "node_modules/@tradetrust-tt/tradetrust-utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@tradetrust-tt/tradetrust-utils/-/tradetrust-utils-2.3.1.tgz", - "integrity": "sha512-ts0nyqEN62qpUXXSZ59nYBqajcKzMYKOi5cBpwlTYbOS4cRi6PcVZmM273xLmf2XLwNb8BA6agLpAx3ll5IWZw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tradetrust-tt/tradetrust-utils/-/tradetrust-utils-2.3.2.tgz", + "integrity": "sha512-T3rikZ3v0qqB3xzmq+KM30OEziyesHgN0CRQrhEvLlQw5IULtmcuwLZGHLE8KyaI8Zd7mqbuubJlfyEMNXJZuA==", "dependencies": { "@tradetrust-tt/tt-verify": "^9.4.0", "dotenv": "^16.4.5", diff --git a/package.json b/package.json index 29b336a..fba04a7 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "@tradetrust-tt/token-registry-v4": "npm:@tradetrust-tt/token-registry@^4.16.0", "@tradetrust-tt/token-registry-v5": "npm:@tradetrust-tt/token-registry@^5.3.0", "@tradetrust-tt/tradetrust": "^6.10.1", - "@tradetrust-tt/tradetrust-utils": "^2.3.1", + "@tradetrust-tt/tradetrust-utils": "^2.3.2", "@tradetrust-tt/tt-verify": "^9.4.0", "@trustvc/w3c-context": "^1.2.13", "@trustvc/w3c-credential-status": "^1.2.12", diff --git a/src/utils/fragment/index.ts b/src/utils/fragment/index.ts index ed30ae7..0d972bb 100644 --- a/src/utils/fragment/index.ts +++ b/src/utils/fragment/index.ts @@ -1 +1,54 @@ -export { errorMessageHandling, interpretFragments } from '@tradetrust-tt/tradetrust-utils'; +import { + errorMessageHandling as OAErrorMessageHandling, + CONSTANTS, + interpretFragments, +} from '@tradetrust-tt/tradetrust-utils'; +import { InvalidVerificationFragment, utils, VerificationFragment } from '@tradetrust-tt/tt-verify'; +import { W3CCredentialStatusCode } from '../../verify/fragments/document-status/w3cCredentialStatus'; +import { CredentialStatusResult } from '@trustvc/w3c-vc'; + +const getW3CCredentialStatusFragment = + utils.getFragmentByName>( + 'W3CCredentialStatus', + ); + +const w3cCredentialStatusRevoked = (fragments: VerificationFragment[]): boolean => { + const issuedFragment = getW3CCredentialStatusFragment(fragments); + return ( + issuedFragment?.reason?.code === W3CCredentialStatusCode.DOCUMENT_REVOKED || + issuedFragment?.reason?.code === W3CCredentialStatusCode.DOCUMENT_REVOKED_AND_SUSPENDED + ); +}; + +const w3cCredentialStatusSuspended = (fragments: VerificationFragment[]): boolean => { + const issuedFragment = getW3CCredentialStatusFragment(fragments); + //checking for both revoked and suspended + return ( + issuedFragment?.reason?.code === W3CCredentialStatusCode.DOCUMENT_SUSPENDED || + issuedFragment?.reason?.code === W3CCredentialStatusCode.DOCUMENT_REVOKED_AND_SUSPENDED + ); +}; + +const errorMessageHandling = (fragments: VerificationFragment[]): string[] => { + const errors = []; + const isW3cFragments = fragments.some( + (f) => f.name.startsWith('W3C') || f.name === 'TransferableRecords', + ); + if (isW3cFragments) { + if (w3cCredentialStatusRevoked(fragments)) { + errors.push(CONSTANTS.TYPES.REVOKED); + } + if (w3cCredentialStatusSuspended(fragments)) { + errors.push(CONSTANTS.TYPES.SUSPENDED); + } + + return errors; + } else return OAErrorMessageHandling(fragments); +}; + +export { + interpretFragments, + errorMessageHandling, + w3cCredentialStatusRevoked, + w3cCredentialStatusSuspended, +}; diff --git a/src/verify/fragments/document-status/w3cCredentialStatus.ts b/src/verify/fragments/document-status/w3cCredentialStatus.ts index 7fc72eb..828df75 100644 --- a/src/verify/fragments/document-status/w3cCredentialStatus.ts +++ b/src/verify/fragments/document-status/w3cCredentialStatus.ts @@ -5,7 +5,23 @@ import { } from '@trustvc/w3c-credential-status'; import { CredentialStatus, isSignedDocument, verifyCredentialStatus } from '@trustvc/w3c-vc'; import { SignedVerifiableCredential } from '../../..'; - +//w3cCredentialStatus enums +export enum W3CCredentialStatusCode { + UNEXPECTED_ERROR = 0, + DOCUMENT_NOT_ISSUED = 1, + CONTRACT_ADDRESS_INVALID = 2, + ETHERS_UNHANDLED_ERROR = 3, + SKIPPED = 4, + DOCUMENT_REVOKED = 5, + DOCUMENT_SUSPENDED = 10, + DOCUMENT_REVOKED_AND_SUSPENDED = 11, + INVALID_ARGUMENT = 6, + CONTRACT_NOT_FOUND = 404, + INVALID_ISSUERS = 7, + INVALID_VALIDATION_METHOD = 8, + UNRECOGNIZED_DOCUMENT = 9, + SERVER_ERROR = 500, +} export const w3cCredentialStatus: Verifier = { skip: async () => { return { @@ -47,12 +63,18 @@ export const w3cCredentialStatus: Verifier = { verifyCredentialStatus(cs, cs?.type as CredentialStatusType, verifierOptions), ), ); + const purposes = verificationResult.map((item) => item.purpose); + const hasRevocation = purposes.includes('revocation'); + const hasSuspension = purposes.includes('suspension'); + const hasRevocationAndSuspension = hasRevocation && hasSuspension; if (verificationResult.some((r) => r.error)) { return { type: 'DOCUMENT_STATUS', name: 'W3CCredentialStatus', reason: { + code: W3CCredentialStatusCode.ETHERS_UNHANDLED_ERROR, + codeString: 'ERROR', message: verificationResult.map((r) => r.error).join(', '), }, data: verificationResult, @@ -71,6 +93,19 @@ export const w3cCredentialStatus: Verifier = { name: 'W3CCredentialStatus', data: verificationResult, status: 'INVALID', + reason: { + code: hasRevocationAndSuspension + ? W3CCredentialStatusCode.DOCUMENT_REVOKED_AND_SUSPENDED + : hasRevocation + ? W3CCredentialStatusCode.DOCUMENT_REVOKED + : W3CCredentialStatusCode.DOCUMENT_SUSPENDED, + codeString: hasRevocationAndSuspension + ? 'REVOKED_AND_SUSPENDED' + : hasRevocation + ? 'REVOKED' + : 'SUSPENDED', + message: 'Document has been revoked or suspended.', + }, }; } }, From 73be2b41417845573caa31f76fd696adb2ae216a Mon Sep 17 00:00:00 2001 From: Rishabh Singh Date: Tue, 17 Jun 2025 08:49:19 +0530 Subject: [PATCH 2/4] fix: update test --- src/__tests__/core/verify.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/__tests__/core/verify.test.ts b/src/__tests__/core/verify.test.ts index 88cda4c..8a1dfcb 100644 --- a/src/__tests__/core/verify.test.ts +++ b/src/__tests__/core/verify.test.ts @@ -237,12 +237,14 @@ describe.concurrent('W3C verify', () => { statusListIndex: '131072', }, }; - + console.log(await verifyDocument(tampered)); expect(await verifyDocument(tampered)).toEqual( expect.arrayContaining([ expect.objectContaining({ name: 'W3CCredentialStatus', reason: { + code: 3, + codeString: 'ERROR', message: 'Invalid statusListIndex: Index out of range: min=0, max=131071', }, status: 'ERROR', From 8a1c41489a0c376ae35cf7658e51da20c7114974 Mon Sep 17 00:00:00 2001 From: Rishabh Singh Date: Tue, 17 Jun 2025 09:23:40 +0530 Subject: [PATCH 3/4] fix: update enum status codes --- src/__tests__/core/verify.test.ts | 15 +++++----- .../document-status/w3cCredentialStatus.ts | 28 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/__tests__/core/verify.test.ts b/src/__tests__/core/verify.test.ts index 8a1dfcb..159af92 100644 --- a/src/__tests__/core/verify.test.ts +++ b/src/__tests__/core/verify.test.ts @@ -8,6 +8,7 @@ import { WRAPPED_DOCUMENT_DID_TOKEN_REGISTRY_V3, WRAPPED_DOCUMENT_DNS_TXT_V2, } from '../fixtures/fixtures'; +import { W3CCredentialStatusCode } from 'src/verify/fragments/document-status/w3cCredentialStatus'; const providerUrl = 'https://rpc-amoy.polygon.technology'; @@ -98,7 +99,7 @@ describe.concurrent('W3C verify', () => { expect.objectContaining({ name: 'W3CSignatureIntegrity', reason: { - code: 0, + code: W3CCredentialStatusCode.SKIPPED, codeString: 'SKIPPED', message: "Document either has no proof or proof.type is not 'BbsBlsSignature2020'.", }, @@ -120,7 +121,7 @@ describe.concurrent('W3C verify', () => { expect.objectContaining({ name: 'W3CIssuerIdentity', reason: { - code: 0, + code: W3CCredentialStatusCode.SKIPPED, codeString: 'SKIPPED', message: 'Document has no issuer field.', }, @@ -188,7 +189,7 @@ describe.concurrent('W3C verify', () => { expect.objectContaining({ name: 'W3CCredentialStatus', reason: { - code: 0, + code: W3CCredentialStatusCode.SKIPPED, codeString: 'SKIPPED', message: 'Document does not have a valid credentialStatus or type.', }, @@ -243,7 +244,7 @@ describe.concurrent('W3C verify', () => { expect.objectContaining({ name: 'W3CCredentialStatus', reason: { - code: 3, + code: W3CCredentialStatusCode.UNEXPECTED_ERROR, codeString: 'ERROR', message: 'Invalid statusListIndex: Index out of range: min=0, max=131071', }, @@ -330,7 +331,7 @@ describe.concurrent('W3C verify', () => { expect.objectContaining({ name: 'TransferableRecords', reason: { - code: 9, + code: W3CCredentialStatusCode.UNRECOGNIZED_DOCUMENT, codeString: 'UNRECOGNIZED_DOCUMENT', message: "Document's credentialStatus does not have tokenRegistry", }, @@ -360,7 +361,7 @@ describe.concurrent('W3C verify', () => { expect.objectContaining({ name: 'TransferableRecords', reason: { - code: 9, + code: W3CCredentialStatusCode.UNRECOGNIZED_DOCUMENT, codeString: 'UNRECOGNIZED_DOCUMENT', message: "Document's credentialStatus does not have tokenNetwork.chainId", }, @@ -386,7 +387,7 @@ describe.concurrent('W3C verify', () => { expect.objectContaining({ name: 'TransferableRecords', reason: { - code: 1, + code: W3CCredentialStatusCode.DOCUMENT_NOT_ISSUED, codeString: 'DOCUMENT_NOT_MINTED', message: 'Document has not been issued under token registry', }, diff --git a/src/verify/fragments/document-status/w3cCredentialStatus.ts b/src/verify/fragments/document-status/w3cCredentialStatus.ts index 828df75..69eace2 100644 --- a/src/verify/fragments/document-status/w3cCredentialStatus.ts +++ b/src/verify/fragments/document-status/w3cCredentialStatus.ts @@ -7,20 +7,18 @@ import { CredentialStatus, isSignedDocument, verifyCredentialStatus } from '@tru import { SignedVerifiableCredential } from '../../..'; //w3cCredentialStatus enums export enum W3CCredentialStatusCode { - UNEXPECTED_ERROR = 0, + SKIPPED = 0, DOCUMENT_NOT_ISSUED = 1, - CONTRACT_ADDRESS_INVALID = 2, - ETHERS_UNHANDLED_ERROR = 3, - SKIPPED = 4, - DOCUMENT_REVOKED = 5, - DOCUMENT_SUSPENDED = 10, - DOCUMENT_REVOKED_AND_SUSPENDED = 11, - INVALID_ARGUMENT = 6, - CONTRACT_NOT_FOUND = 404, - INVALID_ISSUERS = 7, + DOCUMENT_REVOKED = 11, + DOCUMENT_SUSPENDED = 12, + DOCUMENT_REVOKED_AND_SUSPENDED = 101, + STATUS_LIST_NOT_FOUND = 404, INVALID_VALIDATION_METHOD = 8, UNRECOGNIZED_DOCUMENT = 9, SERVER_ERROR = 500, + UNEXPECTED_ERROR = 4, + INVALID_ARGUMENT = 6, + INVALID_ISSUERS = 7, } export const w3cCredentialStatus: Verifier = { skip: async () => { @@ -28,7 +26,7 @@ export const w3cCredentialStatus: Verifier = { type: 'DOCUMENT_STATUS', name: 'W3CCredentialStatus', reason: { - code: 0, + code: W3CCredentialStatusCode.SKIPPED, codeString: 'SKIPPED', message: `Document does not have a valid credentialStatus or type.`, }, @@ -73,7 +71,7 @@ export const w3cCredentialStatus: Verifier = { type: 'DOCUMENT_STATUS', name: 'W3CCredentialStatus', reason: { - code: W3CCredentialStatusCode.ETHERS_UNHANDLED_ERROR, + code: W3CCredentialStatusCode.UNEXPECTED_ERROR, codeString: 'ERROR', message: verificationResult.map((r) => r.error).join(', '), }, @@ -104,7 +102,11 @@ export const w3cCredentialStatus: Verifier = { : hasRevocation ? 'REVOKED' : 'SUSPENDED', - message: 'Document has been revoked or suspended.', + message: hasRevocationAndSuspension + ? 'Document has been revoked and suspended.' + : hasRevocation + ? 'Document has been revoked.' + : 'Document has been suspended.', }, }; } From 016431d7a88f6c8e3e313823704fb035f6e3077f Mon Sep 17 00:00:00 2001 From: Rishabh Singh Date: Tue, 17 Jun 2025 09:45:31 +0530 Subject: [PATCH 4/4] fix: remove console --- src/__tests__/core/verify.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/__tests__/core/verify.test.ts b/src/__tests__/core/verify.test.ts index 159af92..6fb349b 100644 --- a/src/__tests__/core/verify.test.ts +++ b/src/__tests__/core/verify.test.ts @@ -238,7 +238,6 @@ describe.concurrent('W3C verify', () => { statusListIndex: '131072', }, }; - console.log(await verifyDocument(tampered)); expect(await verifyDocument(tampered)).toEqual( expect.arrayContaining([ expect.objectContaining({