From f71b625008a9a6c2a5207f8261c0e6a61e6044e6 Mon Sep 17 00:00:00 2001 From: Julien Fraichot Date: Thu, 12 Dec 2024 18:33:34 +0100 Subject: [PATCH] feat(VerifiableCredential): validate credential subject --- .../useCases/validateVerifiableCredential.ts | 18 +++++++ .../validateVerifiableCredential.test.ts | 51 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/domain/verifier/useCases/validateVerifiableCredential.ts b/src/domain/verifier/useCases/validateVerifiableCredential.ts index 59113966..81030ca9 100644 --- a/src/domain/verifier/useCases/validateVerifiableCredential.ts +++ b/src/domain/verifier/useCases/validateVerifiableCredential.ts @@ -87,6 +87,22 @@ function validateDateRFC3339StringFormat (date: string, propertyName: string): v } } +function validateCredentialSubject (credentialSubject: any): void { + if (typeof credentialSubject !== 'object') { + throw new Error('`credentialSubject` must be an object'); + } + + if (Array.isArray(credentialSubject) && credentialSubject.length === 0) { + throw new Error('`credentialSubject` cannot be an empty array'); + } + + if (Array.isArray(credentialSubject)) { + credentialSubject.forEach(subject => { + validateCredentialSubject(subject); + }); + } +} + function validateCredentialStatus (certificateCredentialStatus: VCCredentialStatus | VCCredentialStatus[]): void { const statuses = Array.isArray(certificateCredentialStatus) ? certificateCredentialStatus : [certificateCredentialStatus]; statuses.forEach(status => { @@ -133,6 +149,8 @@ export default function validateVerifiableCredential (credential: BlockcertsV3 | throw new Error('`credentialSubject` must be defined'); } + validateCredentialSubject(credential.credentialSubject); + validateType(credential.type); validateContext(credential['@context'], credential.type); diff --git a/test/application/domain/verifier/useCases/validateVerifiableCredential.test.ts b/test/application/domain/verifier/useCases/validateVerifiableCredential.test.ts index e3a9a1e2..2a1326e1 100644 --- a/test/application/domain/verifier/useCases/validateVerifiableCredential.test.ts +++ b/test/application/domain/verifier/useCases/validateVerifiableCredential.test.ts @@ -154,6 +154,57 @@ describe('domain verifier validateVerifiableCredential test suite', function () }); }); + describe('validateCredentialSubject method', function () { + describe('given the credentialSubject is an invalid type', function () { + const invalidCredentialSubjectTypes = [ + 'string', 10, true + ]; + invalidCredentialSubjectTypes.forEach(function (type) { + it(`should throw an error when the credentialSubject is a ${typeof type}`, function () { + const fixture = { ...validFixture, credentialSubject: type }; + expect(function () { + validateVerifiableCredential(fixture); + }).toThrow('`credentialSubject` must be an object'); + }); + }); + }); + + describe('given the credentialSubject is a falsy value', function () { + const falsyValues = [null, undefined]; + falsyValues.forEach(function (value) { + it(`should throw an error when ${value}`, function () { + const fixture = { ...validFixture, credentialSubject: value }; + expect(function () { + validateVerifiableCredential(fixture); + }).toThrow('`credentialSubject` must be defined'); + }); + }); + }); + + describe('given the credentialSubject is an empty array', function () { + it('should throw an error', function () { + const fixture = { ...validFixture, credentialSubject: [] }; + expect(function () { + validateVerifiableCredential(fixture); + }).toThrow('`credentialSubject` cannot be an empty array'); + }); + }); + + describe('given the credentialSubject is an array of invalid types', function () { + const invalidCredentialSubjectTypes = [ + 'string', 10, true + ]; + invalidCredentialSubjectTypes.forEach(function (type) { + it(`should throw an error when the credentialSubject item is a ${typeof type}`, function () { + const fixture = { ...validFixture, credentialSubject: [type] }; + expect(function () { + validateVerifiableCredential(fixture); + }).toThrow('`credentialSubject` must be an object'); + }); + }); + }); + }); + describe('validateCredentialStatus method', function () { describe('when the credentialStatus property is defined', function () { describe('when the property is an object but the id is not defined', function () {